From bf61359bc787f0e3e880c11ab8ce8514491c89e4 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 23 Mar 2024 11:16:45 -0700 Subject: [PATCH 01/12] feat(wallet): Added adapter library for consuming minimal-slp-wallet --- src/adapters/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/adapters/index.js b/src/adapters/index.js index df75773..5d434c6 100644 --- a/src/adapters/index.js +++ b/src/adapters/index.js @@ -9,15 +9,12 @@ import BCHJS from '@psf/bch-js' // Load individual adapter libraries. import IPFSAdapter from './ipfs/index.js' - import LocalDB from './localdb/index.js' import LogsAPI from './logapi.js' import Passport from './passport.js' import Nodemailer from './nodemailer.js' - // const { wlogger } = require('./wlogger') import JSONFiles from './json-files.js' - import FullStackJWT from './fullstack-jwt.js' import config from '../../config/index.js' From 79e3aba26e198f7dda776dee098ba0b7150d65c8 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 23 Mar 2024 16:18:10 -0700 Subject: [PATCH 02/12] Adding default startup wallet --- src/adapters/index.js | 12 +- src/adapters/wallet.adapter.js | 129 ++++++++++++++ test/unit/adapters/test-wallet.json | 11 ++ test/unit/adapters/wallet.adapter.unit.js | 198 ++++++++++++++++++++++ test/unit/mocks/adapters/wallet.js | 149 ++++++++++++++++ 5 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 src/adapters/wallet.adapter.js create mode 100644 test/unit/adapters/test-wallet.json create mode 100644 test/unit/adapters/wallet.adapter.unit.js create mode 100644 test/unit/mocks/adapters/wallet.js diff --git a/src/adapters/index.js b/src/adapters/index.js index 5d434c6..7a6e0ce 100644 --- a/src/adapters/index.js +++ b/src/adapters/index.js @@ -17,6 +17,7 @@ import Nodemailer from './nodemailer.js' import JSONFiles from './json-files.js' import FullStackJWT from './fullstack-jwt.js' import config from '../../config/index.js' +import Wallet from './wallet.adapter.js' class Adapters { constructor (localConfig = {}) { @@ -29,6 +30,7 @@ class Adapters { this.jsonFiles = new JSONFiles() this.bchjs = new BCHJS() this.config = config + this.wallet = new Wallet(localConfig) // Get a valid JWT API key and instance bch-js. this.fullStackJwt = new FullStackJWT(config) @@ -36,14 +38,22 @@ class Adapters { async start () { try { + let apiToken if (this.config.getJwtAtStartup) { // Get a JWT token and instantiate bch-js with it. Then pass that instance // to all the rest of the apps controllers and adapters. - await this.fullStackJwt.getJWT() + apiToken = await this.fullStackJwt.getJWT() // Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js. this.bchjs = await this.fullStackJwt.instanceBchjs() } + // Create a default instance of minimal-slp-wallet without initializing it + // (without retrieving the wallets UTXOs). This instance will be overwritten + // if the operator has configured BCH payments. + console.log('\nCreating default startup wallet. This wallet may be overwritten.') + await this.wallet.instanceWalletWithoutInitialization({}, { apiToken }) + this.bchjs = this.wallet.bchWallet.bchjs + // Start the IPFS node. // Do not start these adapters if this is an e2e test. if (this.config.env !== 'test') { diff --git a/src/adapters/wallet.adapter.js b/src/adapters/wallet.adapter.js new file mode 100644 index 0000000..57d3fb2 --- /dev/null +++ b/src/adapters/wallet.adapter.js @@ -0,0 +1,129 @@ +/* + Wallet adapter. This library instantiates minimal-slp-wallet based on different + configuration settings. That single instance of minimal-slp-wallet is then + easily passed around the rest of this application. +*/ + +// Global npm libraries +import BchWallet from 'minimal-slp-wallet' + +// Local libraries +import config from '../../config/index.js' +import JsonFiles from './json-files.js' + +class Wallet { + constructor (localConfig = {}) { + // Encapsulate dependencies + this.config = config + this.BchWallet = BchWallet + this.jsonFiles = new JsonFiles() + + // Bind 'this' object to all subfunctions + this.instanceWalletWithoutInitialization = this.instanceWalletWithoutInitialization.bind(this) + } + + // This is used for initializing the wallet, without waiting to update the wallet + // UTXOs from the blockchain. + // This is useful when the wallet is simply needed to make calls to the blockchain, + // and there is no need to hydrate it with UTXO data. + async instanceWalletWithoutInitialization (walletData = {}, advancedConfig = {}) { + try { + // Use the apiToken from the config settings, if one is not passed-in at + // run-time. + if (!advancedConfig.apiToken) { + advancedConfig.apiToken = this.config.apiToken + } + + // If an authentication password is passed in the config, add it. + if (this.config.authPass) { + advancedConfig.authPass = this.config.authPass + } + + // Detect and configure different blockchain infrastructure settings. + if (this.config.useFullStackCash) { + advancedConfig.interface = 'rest-api' + advancedConfig.restURL = this.config.apiServer + } else { + // By default use the web3 Cash Stack (https://cashstack.info) + advancedConfig.interface = 'consumer-api' + advancedConfig.restURL = this.config.consumerUrl + } + + console.log('advancedConfig setting when creating wallet: ', advancedConfig) + + // Instantiate minimal-slp-wallet. + if (walletData.mnemonic) { + // Instance the wallet using the mnemonic passed in to this function. + console.log('Mnemonic provided to wallet library from calling function.') + this.bchWallet = await this._instanceWallet(walletData.mnemonic, advancedConfig) + } else if (this.config.mnemonic) { + // Otherwise use the mnemonic in the config setting or passed as an environment variable. + console.log('Mnemonic retrieved from config file or environment variable.') + this.bchWallet = await this._instanceWallet(this.config.mnemonic, advancedConfig) + } else { + // If no mnemonic is provided, then generate a new mnemonic to create the wallet. + console.log('New mnemonic generated.') + this.bchWallet = await this._instanceWallet(undefined, advancedConfig) + } + + // Wait for wallet to initialize. + await this.bchWallet.walletInfoPromise + console.log('BCH wallet initialized.') + console.log(`Wallet address: ${this.bchWallet.walletInfo.cashAddress}`) + console.log(`Wallet mnemonic: ${this.bchWallet.walletInfo.mnemonic}`) + + return this.bchWallet + } catch (err) { + console.error('Error in instanceWalletWithoutInitialization()') + throw err + } + } + + // This function is used for easier mocking of unit tests. + async _instanceWallet (mnemonic, config) { + const wallet = new this.BchWallet(mnemonic, config) + await wallet.walletInfoPromise + + console.log('_instanceWallet() wallet.bchjs.restURL: ', wallet.bchjs.restURL) + + return wallet + } + + // Open the wallet file, or create one if the file doesn't exist. + // Does not instance the wallet. The output of this function is expected to + // be passed to instanceWallet(). + async openWallet (advancedConfig = {}) { + try { + let walletData + + // Try to open the wallet.json file. + try { + // console.log('this.WALLET_FILE: ', this.WALLET_FILE) + walletData = await this.jsonFiles.readJSON(this.WALLET_FILE) + } catch (err) { + // Create a new wallet file if one does not already exist. + console.log('Wallet file not found. Creating new wallet.json file.') + + // Create a new wallet. + // No-Update flag creates wallet without making any network calls. + const walletInstance = await this.instanceWalletWithoutInitialization({}, advancedConfig) + + walletData = walletInstance.walletInfo + + // Add the nextAddress property + walletData.nextAddress = 1 + + // Write the wallet data to the JSON file. + await this.jsonFiles.writeJSON(walletData, this.WALLET_FILE) + } + + // console.log('walletData: ', walletData) + return walletData + } catch (err) { + console.error('Error in openWallet()') + throw err + } + } +} + +export default Wallet diff --git a/test/unit/adapters/test-wallet.json b/test/unit/adapters/test-wallet.json new file mode 100644 index 0000000..7ff3a31 --- /dev/null +++ b/test/unit/adapters/test-wallet.json @@ -0,0 +1,11 @@ +{ + "privateKey": "KwftZ1XUKMP9cLwTjXM3Axk5dnjy28nZ7b54PMushFEYs71tXQZu", + "publicKey": "038ac0965dc75b9b98a8e1fee01d8e4bcb23770ad00d8b41f76ef3c07519e76ded", + "mnemonic": "blue unfair eye isolate capital devote horse lumber thrive second text flame", + "cashAddress": "bitcoincash:qprt28ydgfgx8da26y35us57ur5pexwjns0uvyn3qj", + "address": "bitcoincash:qprt28ydgfgx8da26y35us57ur5pexwjns0uvyn3qj", + "legacyAddress": "17SsGH7mb4nQEVh7hwTPYbYbgmWTnyWf3y", + "hdPath": "m/44'/245'/0'/0/0", + "slpAddress": "simpleledger:qprt28ydgfgx8da26y35us57ur5pexwjnsr88lx37v", + "nextAddress": 1 +} \ No newline at end of file diff --git a/test/unit/adapters/wallet.adapter.unit.js b/test/unit/adapters/wallet.adapter.unit.js new file mode 100644 index 0000000..7f8551e --- /dev/null +++ b/test/unit/adapters/wallet.adapter.unit.js @@ -0,0 +1,198 @@ +/* + Unit tests for the Wallet Adapter library. +*/ + +// Global npm libraries +import { assert } from 'chai' +import sinon from 'sinon' +import BchWallet from 'minimal-slp-wallet' +import fs from 'fs' + +// Local libraries +import WalletAdapter from '../../../src/adapters/wallet.adapter.js' +import { MockBchWallet } from '../mocks/adapters/wallet.js' + +// Hack to get __dirname back. +// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/ +import * as url from 'url' +const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) + +// Global constants +const testWalletFile = `${__dirname.toString()}/test-wallet.json` + +describe('#wallet', () => { + let uut + let sandbox + + before(() => { + // Delete the test file if it exists. + try { + deleteFile(testWalletFile) + } catch (err) { } + }) + + beforeEach(() => { + uut = new WalletAdapter() + sandbox = sinon.createSandbox() + }) + + afterEach(() => sandbox.restore()) + + after(() => { + // Delete the test file if it exists. + try { + deleteFile(testWalletFile) + } catch (err) { } + }) + + describe('#_instanceWallet', () => { + it('should create a wallet given a mnemonic', async () => { + const mnemonic = 'wagon tray learn flat erase laugh lonely rug check captain jacket morning' + const result = await uut._instanceWallet(mnemonic) + // console.log('result: ', result) + assert.equal(result.walletInfo.mnemonic, mnemonic) + }) + }) + + describe('#openWallet', () => { + it('should create a new wallet when wallet file does not exist', async () => { + // Mock dependencies + uut.BchWallet = MockBchWallet + // Ensure we open the test file, not the production wallet file. + uut.WALLET_FILE = testWalletFile + const result = await uut.openWallet() + // console.log('result: ', result) + assert.property(result, 'mnemonic') + assert.property(result, 'privateKey') + assert.property(result, 'publicKey') + assert.property(result, 'cashAddress') + assert.property(result, 'address') + assert.property(result, 'slpAddress') + assert.property(result, 'legacyAddress') + assert.property(result, 'hdPath') + }) + + it('should open existing wallet file', async () => { + // This test case uses the file created in the previous test case. + // Ensure we open the test file, not the production wallet file. + uut.WALLET_FILE = testWalletFile + const result = await uut.openWallet() + // console.log('result: ', result) + assert.property(result, 'mnemonic') + assert.property(result, 'privateKey') + assert.property(result, 'publicKey') + assert.property(result, 'cashAddress') + assert.property(result, 'address') + assert.property(result, 'slpAddress') + assert.property(result, 'legacyAddress') + assert.property(result, 'hdPath') + }) + + it('should catch and throw an error', async () => { + try { + // Force an error + uut.WALLET_FILE = '' + uut.BchWallet = () => { + } + await uut.openWallet() + // console.log('result: ', result) + assert.fail('Unexpected code path') + } catch (err) { + // console.log('err: ', err) + assert.include(err.message, 'this.BchWallet is not a constructor') + } + }) + }) + + describe('#instanceWalletWithoutInitialization', () => { + it('should create an instance of BchWallet', async () => { + // Create a mock wallet. + const mockWallet = new BchWallet() + await mockWallet.walletInfoPromise + sandbox.stub(mockWallet, 'initialize').resolves() + + // Mock dependencies + sandbox.stub(uut, '_instanceWallet').resolves(mockWallet) + uut.config.authPass = 'fake-auth-pass' + + // Ensure we open the test file, not the production wallet file. + uut.WALLET_FILE = testWalletFile + const walletData = await uut.openWallet() + + // console.log('walletData: ', walletData) + const result = await uut.instanceWalletWithoutInitialization(walletData) + // console.log('result: ', result) + + assert.property(result, 'walletInfoPromise') + assert.property(result, 'walletInfo') + }) + + it('should catch and throw an error', async () => { + try { + // Force an error + sandbox.stub(uut, '_instanceWallet').rejects(new Error('test error')) + + await uut.instanceWalletWithoutInitialization() + + assert.fail('Unexpected code path') + } catch (err) { + // console.log('err: ', err) + assert.include(err.message, 'test error') + } + }) + + it('should create an instance of BchWallet using web2 infra', async () => { + // Create a mock wallet. + const mockWallet = new BchWallet() + await mockWallet.walletInfoPromise + sandbox.stub(mockWallet, 'initialize').resolves() + // Mock dependencies + sandbox.stub(uut, '_instanceWallet').resolves(mockWallet) + // Ensure we open the test file, not the production wallet file. + uut.WALLET_FILE = testWalletFile + const walletData = await uut.openWallet() + // console.log('walletData: ', walletData) + // Force desired code path + uut.config.useFullStackCash = true + const result = await uut.instanceWalletWithoutInitialization(walletData) + // console.log('result: ', result) + assert.property(result, 'walletInfoPromise') + assert.property(result, 'walletInfo') + }) + + it('should generate wallet from mnemonic in config', async () => { + // Create a mock wallet. + const mockWallet = new BchWallet() + await mockWallet.walletInfoPromise + sandbox.stub(mockWallet, 'initialize').resolves() + + // Mock dependencies + sandbox.stub(uut, '_instanceWallet').resolves(mockWallet) + + // Ensure we open the test file, not the production wallet file. + uut.WALLET_FILE = testWalletFile + const walletData = await uut.openWallet() + // console.log('walletData: ', walletData) + + const originalConfig = uut.config.mnemonic + uut.config.mnemonic = walletData.mnemonic + + const result = await uut.instanceWalletWithoutInitialization({}) + // console.log('result: ', result) + + uut.config.mnemonic = originalConfig + + assert.property(result, 'walletInfoPromise') + assert.property(result, 'walletInfo') + }) + }) +}) + +const deleteFile = (filepath) => { + try { + // Delete state if exist + fs.unlinkSync(filepath) + } catch (err) { + // console.error('Error trying to delete file: ', err) + } +} diff --git a/test/unit/mocks/adapters/wallet.js b/test/unit/mocks/adapters/wallet.js new file mode 100644 index 0000000..09e8378 --- /dev/null +++ b/test/unit/mocks/adapters/wallet.js @@ -0,0 +1,149 @@ +import BCHJS from "@psf/bch-js"; + +const mockWallet = { + mnemonic: 'course abstract aerobic deer try switch turtle diet fence affair butter top', + privateKey: 'L5D2UAam8tvo3uii5kpgaGyjvVMimdrXu8nWGQSQjuuAix6ji1YQ', + publicKey: '0379433ffc401483ade310469953c1cba77c71af904f07c15bde330d7198b4d6dc', + cashAddress: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00', + address: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00', + slpAddress: 'simpleledger:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acq9zm7d33', + legacyAddress: '1JQj1KcQL7GPKzc1D2PvdUSgw3MbDtrHzi', + hdPath: "m/44'/245'/0'/0/0", + nextAddress: 1 +}; + +class MockBchWallet { + constructor() { + this.walletInfoPromise = true; + this.walletInfo = mockWallet; + this.initialize = async () => {} + this.bchjs = new BCHJS(); + this.burnTokens = async () => { + return { success: true, txid: 'txid' }; + }; + this.sendTokens = async () => { + return 'fakeTxid'; + }; + this.getUtxos = async () => { }; + this.getBalance = async () => { }; + this.listTokens = async () => { }; + this.getTxData = async () => { + return [{ + tokenTicker: 'TROUT' + }]; + }; + this.optimize = async () => { }; + // Environment variable is used by wallet-balance.unit.js to force an error. + if (process.env.NO_UTXO) { + this.utxos = {}; + } + else { + this.utxos = { + utxoStore: { + address: 'bitcoincash:qqetvdnlt0p8g27dr44cx7h057kpzly9xse9huc97z', + bchUtxos: [ + { + height: 700685, + tx_hash: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654', + tx_pos: 0, + value: 1000, + txid: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654', + vout: 0, + isValid: false + }, + { + height: 700685, + tx_hash: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654', + tx_pos: 2, + value: 19406, + txid: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654', + vout: 2, + isValid: false + } + ], + nullUtxos: [], + slpUtxos: { + type1: { + mintBatons: [], + tokens: [ + { + 'height': 717331, + 'tx_hash': '74889580bb1a5f8c026aa2f55118ac9917df3332f7abae72a70343daa1c29621', + 'tx_pos': 1, + 'value': 546, + 'txid': '74889580bb1a5f8c026aa2f55118ac9917df3332f7abae72a70343daa1c29621', + 'vout': 1, + 'isSlp': true, + 'type': 'token', + 'qty': '10', + 'tokenId': '600ee24d0f208aebc2bdd2c4ee1b9acb6d57343561442e8676b5bbea311d5a0f', + 'address': 'bitcoincash:qqraj35x6l2qyqhjm5l7qlt7z2245ez8l5z3dwkeq5', + 'ticker': 'FLIPS', + 'name': 'FLIPS', + 'documentUri': '', + 'documentHash': '', + 'decimals': 1, + 'qtyStr': '1' + }, + { + 'height': 730597, + 'tx_hash': '52520faddfafc46b8f8c9548b097f3a3b82a5bf363b5095047b9c5f83247fe36', + 'tx_pos': 1, + 'value': 546, + 'txid': '52520faddfafc46b8f8c9548b097f3a3b82a5bf363b5095047b9c5f83247fe36', + 'vout': 1, + 'isSlp': true, + 'type': 'token', + 'qty': '34999991', + 'tokenId': '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0', + 'address': 'bitcoincash:qqraj35x6l2qyqhjm5l7qlt7z2245ez8l5z3dwkeq5', + 'ticker': 'PSF', + 'name': 'Permissionless Software Foundation', + 'documentUri': 'psfoundation.cash', + 'documentHash': '', + 'decimals': 8, + 'qtyStr': '0.34999991' + }, + { + 'height': 730597, + 'tx_hash': '5dc7e7c91382aed1666a51212dfb74050261e12c3c4f62b6b1e57f42d6c51ee1', + 'tx_pos': 2, + 'value': 546, + 'txid': '5dc7e7c91382aed1666a51212dfb74050261e12c3c4f62b6b1e57f42d6c51ee1', + 'vout': 2, + 'isSlp': true, + 'type': 'token', + 'qty': '18898', + 'tokenId': 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', + 'address': 'bitcoincash:qqraj35x6l2qyqhjm5l7qlt7z2245ez8l5z3dwkeq5', + 'ticker': 'TROUT', + 'name': "Trout's test token", + 'documentUri': 'troutsblog.com', + 'documentHash': '', + 'decimals': 2, + 'qtyStr': '188.98' + } + ] + }, + nft: { + tokens: [] + }, + group: { + tokens: [], + mintBatons: [] + } + } + } + }; + } + } +} + +export { MockBchWallet }; + +export { mockWallet }; + +export default { + MockBchWallet, + mockWallet +}; From 6a4aadde8a624bce9904b901c0cf19e2ea1a1133 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 23 Mar 2024 17:14:26 -0700 Subject: [PATCH 03/12] Refactoring config file --- config/env/common.js | 55 ++++++++++++++++++----- src/adapters/wallet.adapter.js | 8 ++-- test/unit/adapters/test-wallet.json | 11 ----- test/unit/adapters/wallet.adapter.unit.js | 18 +++++--- wallet.json | 11 +++++ 5 files changed, 71 insertions(+), 32 deletions(-) delete mode 100644 test/unit/adapters/test-wallet.json create mode 100644 wallet.json diff --git a/config/env/common.js b/config/env/common.js index cb42e65..464357d 100644 --- a/config/env/common.js +++ b/config/env/common.js @@ -43,14 +43,43 @@ export default { ? process.env.EMAILPASS : 'emailpassword', + // BEGIN WALLET CONFIGURATION + + // BCH Mnemonic for generating encryption keys and payment address + mnemonic: process.env.MNEMONIC ? process.env.MNEMONIC : '', + + // (Optional) use a wallet file generated by psf-bch-wallet. + walletFile: process.env.WALLET_FILE + ? process.env.WALLET_FILE + : `${__dirname.toString()}/../../wallet.json`, + + // Wallet Configuration + // Default value: web3 - connect to bch-api through web 3 JSON RPC over IPFS. + // Alternate value: web2 - connect to bch-api through a web 2 REST API over HTTP. + walletInterface: process.env.WALLET_INTERFACE ? process.env.WALLET_INTERFACE : 'web3', + + // Wallet API Server + // Default web2 server: https://api.fullstack.cash/v5/ + // Default web3 server: https://free-bch.fullstack.cash/ + apiServer: process.env.WALLET_INTERFACE === 'web2' + ? ( + process.env.APISERVER + ? process.env.APISERVER + : 'https://api.fullstack.cash/v5/' + ) + : 'https://free-bch.fullstack.cash/', + + // Basic Authentication Password for connecting to bch-api through a web 2 + // REST API over HTTP. + authPass: process.env.WALLET_AUTH_PASS + ? process.env.WALLET_AUTH_PASS + : '', + // FullStack.cash account information, used for automatic JWT handling. getJwtAtStartup: process.env.GET_JWT_AT_STARTUP ? true : false, authServer: process.env.AUTHSERVER ? process.env.AUTHSERVER : 'https://auth.fullstack.cash', - apiServer: process.env.APISERVER - ? process.env.APISERVER - : 'https://api.fullstack.cash/v5/', fullstackLogin: process.env.FULLSTACKLOGIN ? process.env.FULLSTACKLOGIN : 'demo@demo.com', @@ -58,8 +87,17 @@ export default { ? process.env.FULLSTACKPASS : 'demo', - // IPFS settings. - useIpfs: process.env.DISABLE_IPFS ? false : true, // Disable IPFS flag + // END WALLET CONFIGURATION + + // BEGIN IPFS CONFIGURATION + + // Debug verbosity level of helia-coord. + // 0 = no debug logs. 3 = maximum logs. + debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 2, + + // Enable/Disable the IPFS node at startup. Enabled by default. + useIpfs: process.env.DISABLE_IPFS ? false : true, + isCircuitRelay: process.env.ENABLE_CIRCUIT_RELAY ? true : false, // SSL domain used for websocket connection via browsers. crDomain: process.env.CR_DOMAIN ? process.env.CR_DOMAIN : '', @@ -88,11 +126,6 @@ export default { ipfsTcpPort: process.env.IPFS_TCP_PORT ? process.env.IPFS_TCP_PORT : 4001, ipfsWsPort: process.env.IPFS_WS_PORT ? process.env.IPFS_WS_PORT : 4003, - // BCH Mnemonic for generating encryption keys and payment address - mnemonic: process.env.MNEMONIC ? process.env.MNEMONIC : '', - - debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 2, - // Settings for production, using external go-ipfs node. isProduction: process.env.SVC_ENV === 'prod' ? true : false, ipfsHost: process.env.IPFS_HOST ? process.env.IPFS_HOST : 'localhost', @@ -110,4 +143,6 @@ export default { // v2 Circuit Relay server (FullStack.cash) // '/ip4/78.46.129.7/tcp/4001/p2p/12D3KooWFQ11GQ5NubsJGhYZ4X3wrAGimLevxfm6HPExCrMYhpSL' ] + + // END IPFS CONFIGURATION } diff --git a/src/adapters/wallet.adapter.js b/src/adapters/wallet.adapter.js index 57d3fb2..5925f55 100644 --- a/src/adapters/wallet.adapter.js +++ b/src/adapters/wallet.adapter.js @@ -40,7 +40,7 @@ class Wallet { } // Detect and configure different blockchain infrastructure settings. - if (this.config.useFullStackCash) { + if (this.config.walletInterface === 'web2') { advancedConfig.interface = 'rest-api' advancedConfig.restURL = this.config.apiServer } else { @@ -98,8 +98,8 @@ class Wallet { // Try to open the wallet.json file. try { - // console.log('this.WALLET_FILE: ', this.WALLET_FILE) - walletData = await this.jsonFiles.readJSON(this.WALLET_FILE) + // console.log('this.config.walletFile: ', this.config.walletFile) + walletData = await this.jsonFiles.readJSON(this.config.walletFile) } catch (err) { // Create a new wallet file if one does not already exist. console.log('Wallet file not found. Creating new wallet.json file.') @@ -114,7 +114,7 @@ class Wallet { walletData.nextAddress = 1 // Write the wallet data to the JSON file. - await this.jsonFiles.writeJSON(walletData, this.WALLET_FILE) + await this.jsonFiles.writeJSON(walletData, this.config.walletFile) } // console.log('walletData: ', walletData) diff --git a/test/unit/adapters/test-wallet.json b/test/unit/adapters/test-wallet.json deleted file mode 100644 index 7ff3a31..0000000 --- a/test/unit/adapters/test-wallet.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "privateKey": "KwftZ1XUKMP9cLwTjXM3Axk5dnjy28nZ7b54PMushFEYs71tXQZu", - "publicKey": "038ac0965dc75b9b98a8e1fee01d8e4bcb23770ad00d8b41f76ef3c07519e76ded", - "mnemonic": "blue unfair eye isolate capital devote horse lumber thrive second text flame", - "cashAddress": "bitcoincash:qprt28ydgfgx8da26y35us57ur5pexwjns0uvyn3qj", - "address": "bitcoincash:qprt28ydgfgx8da26y35us57ur5pexwjns0uvyn3qj", - "legacyAddress": "17SsGH7mb4nQEVh7hwTPYbYbgmWTnyWf3y", - "hdPath": "m/44'/245'/0'/0/0", - "slpAddress": "simpleledger:qprt28ydgfgx8da26y35us57ur5pexwjnsr88lx37v", - "nextAddress": 1 -} \ No newline at end of file diff --git a/test/unit/adapters/wallet.adapter.unit.js b/test/unit/adapters/wallet.adapter.unit.js index 7f8551e..2f919d6 100644 --- a/test/unit/adapters/wallet.adapter.unit.js +++ b/test/unit/adapters/wallet.adapter.unit.js @@ -59,7 +59,7 @@ describe('#wallet', () => { // Mock dependencies uut.BchWallet = MockBchWallet // Ensure we open the test file, not the production wallet file. - uut.WALLET_FILE = testWalletFile + uut.walletFile = testWalletFile const result = await uut.openWallet() // console.log('result: ', result) assert.property(result, 'mnemonic') @@ -75,7 +75,7 @@ describe('#wallet', () => { it('should open existing wallet file', async () => { // This test case uses the file created in the previous test case. // Ensure we open the test file, not the production wallet file. - uut.WALLET_FILE = testWalletFile + uut.walletFile = testWalletFile const result = await uut.openWallet() // console.log('result: ', result) assert.property(result, 'mnemonic') @@ -91,7 +91,7 @@ describe('#wallet', () => { it('should catch and throw an error', async () => { try { // Force an error - uut.WALLET_FILE = '' + uut.config.walletFile = '' uut.BchWallet = () => { } await uut.openWallet() @@ -116,7 +116,7 @@ describe('#wallet', () => { uut.config.authPass = 'fake-auth-pass' // Ensure we open the test file, not the production wallet file. - uut.WALLET_FILE = testWalletFile + uut.config.walletFile = testWalletFile const walletData = await uut.openWallet() // console.log('walletData: ', walletData) @@ -146,15 +146,19 @@ describe('#wallet', () => { const mockWallet = new BchWallet() await mockWallet.walletInfoPromise sandbox.stub(mockWallet, 'initialize').resolves() + // Mock dependencies sandbox.stub(uut, '_instanceWallet').resolves(mockWallet) + // Ensure we open the test file, not the production wallet file. - uut.WALLET_FILE = testWalletFile + uut.config.walletFile = testWalletFile const walletData = await uut.openWallet() // console.log('walletData: ', walletData) + // Force desired code path - uut.config.useFullStackCash = true + uut.config.walletInterface = 'web2' const result = await uut.instanceWalletWithoutInitialization(walletData) + // console.log('result: ', result) assert.property(result, 'walletInfoPromise') assert.property(result, 'walletInfo') @@ -170,7 +174,7 @@ describe('#wallet', () => { sandbox.stub(uut, '_instanceWallet').resolves(mockWallet) // Ensure we open the test file, not the production wallet file. - uut.WALLET_FILE = testWalletFile + uut.config.walletFile = testWalletFile const walletData = await uut.openWallet() // console.log('walletData: ', walletData) diff --git a/wallet.json b/wallet.json new file mode 100644 index 0000000..3c81c77 --- /dev/null +++ b/wallet.json @@ -0,0 +1,11 @@ +{ + "mnemonic": "course abstract aerobic deer try switch turtle diet fence affair butter top", + "privateKey": "L5D2UAam8tvo3uii5kpgaGyjvVMimdrXu8nWGQSQjuuAix6ji1YQ", + "publicKey": "0379433ffc401483ade310469953c1cba77c71af904f07c15bde330d7198b4d6dc", + "cashAddress": "bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00", + "address": "bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00", + "slpAddress": "simpleledger:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acq9zm7d33", + "legacyAddress": "1JQj1KcQL7GPKzc1D2PvdUSgw3MbDtrHzi", + "hdPath": "m/44'/245'/0'/0/0", + "nextAddress": 1 +} \ No newline at end of file From 48484ed7af1ce7930a1d52b8dd0f0127598f66b9 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 23 Mar 2024 17:23:07 -0700 Subject: [PATCH 04/12] Fixing typo with web3 wallet --- src/adapters/wallet.adapter.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/adapters/wallet.adapter.js b/src/adapters/wallet.adapter.js index 5925f55..52ef636 100644 --- a/src/adapters/wallet.adapter.js +++ b/src/adapters/wallet.adapter.js @@ -46,7 +46,7 @@ class Wallet { } else { // By default use the web3 Cash Stack (https://cashstack.info) advancedConfig.interface = 'consumer-api' - advancedConfig.restURL = this.config.consumerUrl + advancedConfig.restURL = this.config.apiServer } console.log('advancedConfig setting when creating wallet: ', advancedConfig) @@ -84,8 +84,6 @@ class Wallet { const wallet = new this.BchWallet(mnemonic, config) await wallet.walletInfoPromise - console.log('_instanceWallet() wallet.bchjs.restURL: ', wallet.bchjs.restURL) - return wallet } From b0797d92f5c88046757bbc1a4b8d8b09b0d310c1 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 23 Mar 2024 17:42:59 -0700 Subject: [PATCH 05/12] Increased test coverage to 100% --- src/adapters/wallet.adapter.js | 107 ++++++++++++++++ test/unit/adapters/wallet.adapter.unit.js | 141 ++++++++++++++++++++++ test/unit/misc/config.js | 6 +- 3 files changed, 253 insertions(+), 1 deletion(-) diff --git a/src/adapters/wallet.adapter.js b/src/adapters/wallet.adapter.js index 52ef636..9dd2052 100644 --- a/src/adapters/wallet.adapter.js +++ b/src/adapters/wallet.adapter.js @@ -122,6 +122,113 @@ class Wallet { throw err } } + + // Create an instance of minimal-slp-wallet. Same as + // instanceWalletWithoutInitialization(), but waits for the wallet to initialize + // its UTXOs (wallet balance and tokens). + async instanceWallet (walletData = {}, advancedConfig = {}) { + try { + // Instance the wallet without initialization. + await this.instanceWalletWithoutInitialization(walletData, advancedConfig) + + // Initialize the wallet + await this.bchWallet.initialize() + + return this.bchWallet + } catch (err) { + console.error('Error in wallet.js/instanceWallet()') + throw err + } + } + + // Increments the 'nextAddress' property in the wallet file. This property + // indicates the HD index that should be used to generate a key pair for + // storing funds for Offers. + // This function opens the wallet file, increments the nextAddress property, + // then saves the change to the wallet file. + async incrementNextAddress () { + try { + const walletData = await this.openWallet() + // console.log('original walletdata: ', walletData) + walletData.nextAddress++ + // console.log('walletData finish: ', walletData) + await this.jsonFiles.writeJSON(walletData, this.WALLET_FILE) + // Update the working instance of the wallet. + this.bchWallet.walletInfo.nextAddress++ + // console.log('this.bchWallet.walletInfo: ', this.bchWallet.walletInfo) + return walletData.nextAddress + } catch (err) { + console.error('Error in incrementNextAddress()') + throw err + } + } + + // This method returns an object that contains a private key WIF, public address, + // and the index of the HD wallet that the key pair was generated from. + // TODO: Allow input integer. If input is used, use that as the index. If no + // input is provided, then call incrementNextAddress(). + async getKeyPair (hdIndex = 0) { + try { + if (!hdIndex) { + // Increment the HD index and generate a new key pair. + hdIndex = await this.incrementNextAddress() + } + const mnemonic = this.bchWallet.walletInfo.mnemonic + // root seed buffer + const rootSeed = await this.bchWallet.bchjs.Mnemonic.toSeed(mnemonic) + const masterHDNode = this.bchWallet.bchjs.HDNode.fromSeed(rootSeed) + // HDNode of BIP44 account + // const account = this.bchWallet.bchjs.HDNode.derivePath(masterHDNode, "m/44'/245'/0'") + const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${hdIndex}`) + const cashAddress = this.bchWallet.bchjs.HDNode.toCashAddress(childNode) + console.log('Generating a new key pair for cashAddress: ', cashAddress) + const wif = this.bchWallet.bchjs.HDNode.toWIF(childNode) + const outObj = { + cashAddress, + wif, + hdIndex + } + return outObj + } catch (err) { + console.error('Error in getKeyPair()') + throw err + } + } + + // Optimize the wallet by consolidating the UTXOs. + async optimize () { + const UTXO_THREASHOLD = 7 + // Do a dry-run first to see if there are enough UTXOs worth consolidating. + const dryRunOut = await this.bchWallet.optimize(true) + if (dryRunOut.bchUtxoCnt > UTXO_THREASHOLD) { + // Consolidate BCH UTXOs if the count is above the threashold. + const txids = await this.bchWallet.optimize() + console.log(`Wallet optimized with these return values: ${JSON.stringify(txids, null, 2)}`) + } + return true + } + + // Get the balance of the wallet in sats and PSF tokens. + // This function is called by the GET /entry/balance controller. + async getBalance () { + const balance = await this.bchWallet.getBalance() + // console.log('balance: ', balance) + const tokens = await this.bchWallet.listTokens() + // console.log('tokens: ', tokens) + // Find the array entry for the PSF token + const psfTokens = tokens.find(x => x.tokenId === '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0') + // console.log('psfTokens: ', psfTokens) + let psfBalance = 0 + if (psfTokens) { + psfBalance = psfTokens.qty + } + const outObj = { + satBalance: balance, + psfBalance, + success: true + } + return outObj + } } export default Wallet diff --git a/test/unit/adapters/wallet.adapter.unit.js b/test/unit/adapters/wallet.adapter.unit.js index 2f919d6..7ac98ac 100644 --- a/test/unit/adapters/wallet.adapter.unit.js +++ b/test/unit/adapters/wallet.adapter.unit.js @@ -190,6 +190,147 @@ describe('#wallet', () => { assert.property(result, 'walletInfo') }) }) + + describe('#instanceWallet', () => { + it('should create an instance of BchWallet', async () => { + // Create a mock wallet. + const mockWallet = new BchWallet() + await mockWallet.walletInfoPromise + sandbox.stub(mockWallet, 'initialize').resolves() + + // Mock dependencies + sandbox.stub(uut, '_instanceWallet').resolves(mockWallet) + + // Ensure we open the test file, not the production wallet file. + uut.WALLET_FILE = testWalletFile + const walletData = await uut.openWallet() + // console.log('walletData: ', walletData) + const result = await uut.instanceWallet(walletData) + // console.log('result: ', result) + + assert.property(result, 'walletInfoPromise') + assert.property(result, 'walletInfo') + }) + + it('should catch and throw an error', async () => { + try { + // Force an error + sandbox.stub(uut, 'instanceWalletWithoutInitialization').rejects(new Error('test error')) + + await uut.instanceWallet() + assert.fail('Unexpected code path') + } catch (err) { + // console.log('err: ', err) + assert.include(err.message, 'test error') + } + }) + + it('should create an instance of BchWallet using web2 infra', async () => { + // Create a mock wallet. + const mockWallet = new BchWallet() + await mockWallet.walletInfoPromise + sandbox.stub(mockWallet, 'initialize').resolves() + + // Mock dependencies + sandbox.stub(uut, '_instanceWallet').resolves(mockWallet) + + // Ensure we open the test file, not the production wallet file. + uut.WALLET_FILE = testWalletFile + const walletData = await uut.openWallet() + // console.log('walletData: ', walletData) + + // Force desired code path + uut.config.useFullStackCash = true + const result = await uut.instanceWallet(walletData) + // console.log('result: ', result) + + assert.property(result, 'walletInfoPromise') + assert.property(result, 'walletInfo') + }) + }) + + describe('#incrementNextAddress', () => { + it('should increment the nextAddress property', async () => { + // Ensure we open the test file, not the production wallet file. + uut.WALLET_FILE = testWalletFile + // mock instance of minimal-slp-wallet + uut.bchWallet = new MockBchWallet() + const result = await uut.incrementNextAddress() + assert.equal(result, 2) + }) + + it('should catch and throw an error', async () => { + try { + // Force an error + sandbox.stub(uut, 'openWallet').rejects(new Error('test error')) + await uut.incrementNextAddress() + assert.fail('Unexpected code path') + } catch (err) { + assert.include(err.message, 'test error') + } + }) + }) + + describe('#getKeyPair', () => { + it('should return an object with a key pair', async () => { + // Ensure we open the test file, not the production wallet file. + uut.WALLET_FILE = testWalletFile + // mock instance of minimal-slp-wallet + uut.bchWallet = new MockBchWallet() + const result = await uut.getKeyPair() + // console.log('result: ', result) + assert.property(result, 'cashAddress') + assert.property(result, 'wif') + assert.property(result, 'hdIndex') + }) + + it('should catch and throw an error', async () => { + try { + // Force an error + sandbox + .stub(uut, 'incrementNextAddress') + .rejects(new Error('test error')) + await uut.getKeyPair() + assert.fail('Unexpected code path') + } catch (err) { + assert.include(err.message, 'test error') + } + }) + }) + + describe('#optimize', () => { + it('should call the wallet optimize function', async () => { + // mock instance of minimal-slp-wallet + uut.bchWallet = new MockBchWallet() + sandbox.stub(uut.bchWallet, 'optimize').resolves({ bchUtxoCnt: 10 }) + const result = await uut.optimize() + assert.equal(result, true) + }) + }) + + describe('#getBalance', () => { + it('should get the balance for the wallet', async () => { + // mock instance of minimal-slp-wallet + uut.bchWallet = new MockBchWallet() + // Mock dependencies and force desired code path + sandbox.stub(uut.bchWallet, 'getBalance').resolves(41012) + sandbox.stub(uut.bchWallet, 'listTokens').resolves([{ + tokenId: '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0', + ticker: 'PSF', + name: 'Permissionless Software Foundation', + decimals: 8, + tokenType: 1, + url: 'psfoundation.cash', + qty: 2 + }]) + const result = await uut.getBalance() + // console.log('result: ', result) + // Assert the expected properties exist and have the expected values. + assert.equal(result.satBalance, 41012) + assert.equal(result.psfBalance, 2) + assert.equal(result.success, true) + }) + }) }) const deleteFile = (filepath) => { diff --git a/test/unit/misc/config.js b/test/unit/misc/config.js index 6a55b7e..54bce08 100644 --- a/test/unit/misc/config.js +++ b/test/unit/misc/config.js @@ -38,9 +38,13 @@ describe('#config', () => { assert.equal(config.env, 'test') }) - it('Should return test environment config', async () => { + it('Should return prod environment config', async () => { process.env.SVC_ENV = 'prod' + process.env.WALLET_INTERFACE = 'web2' + process.env.APISERVER = 'https://api.fullstack.cash/v5/' + + await import('../../../config/env/common.js?foo=bar2') const importedConfig3 = await import('../../../config/index.js?foo=bar2') const config = importedConfig3.default // console.log('config: ', config) From 00d53659b15f066ba69874ebc844bd24f1402261 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 23 Mar 2024 17:44:41 -0700 Subject: [PATCH 06/12] Updating README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 91bfafb..351bec3 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,10 @@ npm install npm start ``` +### Configuration + +This app is intended to be started via a bash shell script. See the environment variables used to configure this app in the [config/env/common.js file](./config/env/common.js). + ## File Structure The file layout of this repository follows the file layout of [Clean Architecture](https://christroutner.github.io/trouts-blog/blog/clean-architecture). Understaning the principles laid out this article will help developers navigate the code base. From 3fb03791752fe1d290500546eaa704a4952a8abb Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 24 Mar 2024 07:17:42 -0700 Subject: [PATCH 07/12] Adding config section to README --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index aab48de..af1d74d 100644 --- a/README.md +++ b/README.md @@ -61,16 +61,13 @@ docker-compose up -d - You can bring the containers down with `docker-compose down` - You can bring the containers back up with `docker-compose up -d`. -### Development Environment +### Configuration -A development environment will allow you modify the code on-the-fly and contribute to the code base of this repository. Ubuntu v20 is the recommended OS for creating a dev environment. Other operating systems may cause issues. +This program is intended to started with a Bash shell script. In that script, you can set the following environment variables: ```bash -git clone https://github.com/Permissionless-Software-Foundation/ipfs-service-provider -cd ipfs-service-provider -./install-mongo-sh -npm install -npm start +# The instance of bch-api to connect to. +export APISERVER=http://172.17.0.1:3000/v5/ ``` ## File Structure From 53d84300ec6316d7f29cee6c5235be1384e61264 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 24 Mar 2024 07:25:48 -0700 Subject: [PATCH 08/12] fix(ports): Setting ports 5032, 5668, 5669, 5557 --- config/env/common.js | 2 +- config/env/production.js | 2 +- production/docker/Dockerfile | 2 -- production/docker/docker-compose.yml | 12 ++++++------ production/docker/start-production.sh | 9 +++------ 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/config/env/common.js b/config/env/common.js index fbca196..e620c52 100644 --- a/config/env/common.js +++ b/config/env/common.js @@ -29,7 +29,7 @@ console.log('GET_JWT_AT_STARTUP: ', process.env.GET_JWT_AT_STARTUP) export default { // Configure TCP port. - port: process.env.PORT || 5020, + port: process.env.PORT || 5032, // Password for HTML UI that displays logs. logPass: 'test', diff --git a/config/env/production.js b/config/env/production.js index 24805ba..f12fb7c 100644 --- a/config/env/production.js +++ b/config/env/production.js @@ -12,6 +12,6 @@ export default { token: 'secret-jwt-token', database: process.env.DBURL ? process.env.DBURL - : 'mongodb://172.17.0.1:5555/bch-service-prod', + : 'mongodb://172.17.0.1:5557/bch-service-prod', env: 'prod' } diff --git a/production/docker/Dockerfile b/production/docker/Dockerfile index 7287c6d..3936b9a 100644 --- a/production/docker/Dockerfile +++ b/production/docker/Dockerfile @@ -73,8 +73,6 @@ RUN mkdir /home/safeuser/.ipfsdata #VOLUME /home/safeuser/ipfsdata #VOLUME /home/safeuser/orbitdb -EXPOSE 5010 - # Start the application. #COPY start-production.sh start-production.sh CMD ["./start-production.sh"] diff --git a/production/docker/docker-compose.yml b/production/docker/docker-compose.yml index a86bdba..40d3c5e 100644 --- a/production/docker/docker-compose.yml +++ b/production/docker/docker-compose.yml @@ -3,11 +3,11 @@ version: '3.9' services: - mongo-bch-service: + mongo-bch-wallet: image: mongo:4.2.0 container_name: mongo-bch-service ports: - - '5555:27017' # : + - '5557:27017' # : volumes: - ../data/mongodb:/data/db command: mongod --logpath=/dev/null # -- quiet @@ -24,11 +24,11 @@ services: max-file: '10' mem_limit: 1000mb links: - - mongo-bch-service + - mongo-bch-wallet ports: - - '5010:5010' # : - - '4001:4001' - - '4003:4003' + - '5032:5032' # : + - '5668:5668' + - '5669:5669' volumes: - ../data/ipfsdata:/home/safeuser/ipfs-bch-wallet-service/.ipfsdata - ./start-production.sh:/home/safeuser/ipfs-bch-wallet-service/start-production.sh diff --git a/production/docker/start-production.sh b/production/docker/start-production.sh index 586504f..7e0ec66 100755 --- a/production/docker/start-production.sh +++ b/production/docker/start-production.sh @@ -16,9 +16,6 @@ export COORD_NAME=bch-wallet-service-generic # and you must forward that subdomain to the IPFS_WS_PORT. #export CR_DOMAIN=subdomain.yourdomain.com -# Debug level. 0 = minimal info. 2 = max info. -export DEBUG_LEVEL=2 - # BCH Wallet Service Settings # Change APISERVER to point to your own installation of bch-api export APISERVER=https://api.fullstack.cash/v5/ @@ -32,7 +29,7 @@ export FULLSTACKPASS=demo # Production database connection string. -export DBURL=mongodb://172.17.0.1:5555/ipfs-service-prod +export DBURL=mongodb://172.17.0.1:5557/ipfs-service-prod # Configure REST API port export PORT=5010 @@ -41,8 +38,8 @@ export PORT=5010 export SVC_ENV=prod export IPFS_HOST=172.17.0.1 export IPFS_API_PORT=5001 -export IPFS_TCP_PORT=4001 -export IPFS_WS_PORT=4003 +export IPFS_TCP_PORT=5668 +export IPFS_WS_PORT=5669 # Set the debug level for helia-coord. 0-3. # 0 = no debug logs. 3 = maximum debug logs. From 0a406cb9a4b4f95ab8166a0141a4503e5bf72ab5 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 24 Mar 2024 07:43:34 -0700 Subject: [PATCH 09/12] fix(helia-coord): Updating to v1.5.6, auto-retry of bootstrap list --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index da7340a..6d4328f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "datastore-fs": "9.1.6", "glob": "7.1.6", "helia": "2.1.0", - "helia-coord": "1.5.5", + "helia-coord": "1.5.6", "jsonrpc-lite": "2.2.0", "jsonwebtoken": "8.5.1", "jwt-bch-lib": "1.3.0", @@ -11533,9 +11533,9 @@ } }, "node_modules/helia-coord": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/helia-coord/-/helia-coord-1.5.5.tgz", - "integrity": "sha512-IUG/xobS7rcoIarYKTst/YicvXZSIrWSzqcFRu2Xujv820BblQrDMxHbMmUUoWGFnlgt/xw8cqBvH4AzrHLueg==", + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/helia-coord/-/helia-coord-1.5.6.tgz", + "integrity": "sha512-sh7rEiw883aPsIMZ4vxbzA6OKQjuhz4vlIRoaNWJruju1mJY4hrn9BFPrtJ96KN054zZ0EwziVdTxD9vPV4bSQ==", "dependencies": { "@chris.troutner/retry-queue": "1.0.8", "bch-encrypt-lib": "2.1.1", @@ -33949,9 +33949,9 @@ } }, "helia-coord": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/helia-coord/-/helia-coord-1.5.5.tgz", - "integrity": "sha512-IUG/xobS7rcoIarYKTst/YicvXZSIrWSzqcFRu2Xujv820BblQrDMxHbMmUUoWGFnlgt/xw8cqBvH4AzrHLueg==", + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/helia-coord/-/helia-coord-1.5.6.tgz", + "integrity": "sha512-sh7rEiw883aPsIMZ4vxbzA6OKQjuhz4vlIRoaNWJruju1mJY4hrn9BFPrtJ96KN054zZ0EwziVdTxD9vPV4bSQ==", "requires": { "@chris.troutner/retry-queue": "1.0.8", "bch-encrypt-lib": "2.1.1", diff --git a/package.json b/package.json index 0c8c895..55c1215 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "datastore-fs": "9.1.6", "glob": "7.1.6", "helia": "2.1.0", - "helia-coord": "1.5.5", + "helia-coord": "1.5.6", "jsonrpc-lite": "2.2.0", "jsonwebtoken": "8.5.1", "jwt-bch-lib": "1.3.0", From 316c5d63999c1ff6e3c1b8627c547d0d15897936 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 24 Mar 2024 08:52:57 -0700 Subject: [PATCH 10/12] fix(Docker): Using node.js v20 --- production/docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/production/docker/Dockerfile b/production/docker/Dockerfile index 3936b9a..41b4a63 100644 --- a/production/docker/Dockerfile +++ b/production/docker/Dockerfile @@ -13,7 +13,7 @@ RUN apt-get install -y sudo git curl nano gnupg wget RUN sudo apt-get install -y ca-certificates curl gnupg RUN sudo mkdir -p /etc/apt/keyrings RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg -RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list +RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list RUN sudo apt-get update RUN sudo apt-get install -y nodejs build-essential @@ -35,7 +35,7 @@ RUN runuser -l safeuser -c "npm config set prefix '~/.npm-global'" # Update to the latest version of npm. # Working with npm@7.21.1 -RUN npm install -g npm +#RUN npm install -g npm # Update to the latest version of npm. #RUN npm install -g npm@7.23.0 From 62ac11c861d03adbe2cf6623c2b51680241e1769 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 24 Mar 2024 08:59:47 -0700 Subject: [PATCH 11/12] Fixing docker names --- production/docker/docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/production/docker/docker-compose.yml b/production/docker/docker-compose.yml index 40d3c5e..6bfc309 100644 --- a/production/docker/docker-compose.yml +++ b/production/docker/docker-compose.yml @@ -3,7 +3,7 @@ version: '3.9' services: - mongo-bch-wallet: + mongo-bch-service: image: mongo:4.2.0 container_name: mongo-bch-service ports: @@ -13,10 +13,10 @@ services: command: mongod --logpath=/dev/null # -- quiet restart: always - ipfs-bch-wallet: + ipfs-bch-wallet-service: build: . #image: christroutner/ipfs-service-provider:v5.0.1 - container_name: ipfs-service + container_name: ipfs-bch-wallet-service logging: driver: 'json-file' options: @@ -24,7 +24,7 @@ services: max-file: '10' mem_limit: 1000mb links: - - mongo-bch-wallet + - mongo-bch-service ports: - '5032:5032' # : - '5668:5668' From 95096346517b6a13250985868f0b7b053152ddf9 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 25 Mar 2024 16:10:47 -0700 Subject: [PATCH 12/12] fix(config): Setting announceJsonLd --- config/env/common.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/env/common.js b/config/env/common.js index 7147fcd..82c5c47 100644 --- a/config/env/common.js +++ b/config/env/common.js @@ -18,7 +18,6 @@ import * as url from 'url' import { readFileSync } from 'fs' const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) const pkgInfo = JSON.parse(readFileSync(`${__dirname.toString()}/../../package.json`)) - const version = pkgInfo.version const ipfsCoordName = process.env.COORD_NAME @@ -115,7 +114,7 @@ export default { '@type': 'WebAPI', name: ipfsCoordName, version, - protocol: 'bch-wallet', + protocol: 'ipfs-bch-wallet-service', description: 'IPFS service providing BCH blockchain access needed by a wallet.', documentation: 'https://ipfs-bch-wallet-service.fullstack.cash/',