From e60f4ff1445059859e96d128136ccef3e40cb278 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 19 May 2021 12:01:42 -0700 Subject: [PATCH] fix(v5 electrumx): Added first unit and integration tests for new electrumx route --- package.json | 4 +- src/routes/v5/electrumx.js | 460 +++++++++++--------------------- test/v5/a01-electrumx.js | 277 +++++++++---------- test/v5/mocks/electrumx-mock.js | 28 +- 4 files changed, 310 insertions(+), 459 deletions(-) diff --git a/package.json b/package.json index f4629c9..34c8506 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ "coverage": "nyc report --reporter=text-lcov | coveralls", "coverage:report": "export NETWORK=mainnet && nyc --reporter=html mocha --timeout 25000 test/v4/", "docs": "./node_modules/.bin/apidoc -i src/routes/v4 -o docs", - "test:temp1": "export NETWORK=mainnet && export TEST=integration && export SLPDB_URL=https://slpdb.fountainhead.cash/ && mocha --exit --timeout 25000 -g '#nft' test/v4/integration/", - "test:temp2": "mocha -g '#getNftGroup' --exit --timeout 30000 test/v4/" + "test:temp1": "export NETWORK=mainnet && export TEST=integration && mocha -g '#getBalance' --exit --timeout 30000 test/v5/", + "test:temp2": "export NETWORK=mainnet && mocha -g '#getBalance' --exit --timeout 30000 test/v5/" }, "engines": { "node": ">=10.15.1" diff --git a/src/routes/v5/electrumx.js b/src/routes/v5/electrumx.js index af2133b..2ee0d66 100644 --- a/src/routes/v5/electrumx.js +++ b/src/routes/v5/electrumx.js @@ -34,6 +34,8 @@ class Electrum { _this.bchjs = bchjs // _this.bitcore = bitcore + _this.fulcrumApi = 'http://fulcrum-api.fullstackbch.nl/v1/' + // _this.electrumx = new ElectrumCash( // 'bch-api', // '1.4.1', @@ -48,6 +50,8 @@ class Electrum { _this.router = router _this.router.get('/', _this.root) + _this.router.get('/balance/:address', _this.getBalance) + // _this.router.post('/balance', _this.balanceBulk) // _this.router.get('/utxos/:address', _this.getUtxos) // _this.router.post('/utxos', _this.utxosBulk) // _this.router.get('/tx/data/:txid', _this.getTransactionDetails) @@ -55,105 +59,160 @@ class Electrum { // _this.router.post('/tx/broadcast', _this.broadcastTransaction) // _this.router.get('/block/headers/:height', _this.getBlockHeaders) // _this.router.post('/block/headers', _this.blockHeadersBulk) - // _this.router.get('/balance/:address', _this.getBalance) - // _this.router.post('/balance', _this.balanceBulk) // _this.router.get('/transactions/:address', _this.getTransactions) // _this.router.post('/transactions', _this.transactionsBulk) // _this.router.get('/unconfirmed/:address', _this.getMempool) // _this.router.post('/unconfirmed', _this.mempoolBulk) } - // Initializes a connection to electrum servers. - // async connect () { + /** + * @api {get} /electrumx/balance/{addr} Get balance for a single address. + * @apiName Balance for a single address + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Returns an object with confirmed and unconfirmed balance associated with an address. + * + * + * @apiExample Example usage: + * curl -X GET "https://api.fullstack.cash/v4/electrumx/balance/bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur3" -H "accept: application/json" + * + */ + // GET handler for single balance + async getBalance (req, res, next) { + try { + const address = req.params.address + + // Reject if address is an array. + if (Array.isArray(address)) { + res.status(400) + return res.json({ + success: false, + error: 'address can not be an array. Use POST for bulk upload.' + }) + } + + // Ensure the address is in cash address format. + const cashAddr = _this.bchjs.Address.toCashAddress(address) + + // Prevent a common user error. Ensure they are using the correct network address. + const networkIsValid = _this.routeUtils.validateNetwork(cashAddr) + if (!networkIsValid) { + res.status(400) + return res.json({ + success: false, + error: + 'Invalid network. Trying to use a testnet address on mainnet, or vice versa.' + }) + } + + wlogger.debug( + 'Executing electrumx/getBalance with this address: ', + cashAddr + ) + + const response = await this.axios.get( + `${this.fulcrumApi}electrumx/balance/${address}` + ) + + res.status(200) + return res.json(response.data) + } catch (err) { + // Write out error to error log. + wlogger.error('Error in elecrumx.js/getBalance().', err) + + return _this.errorHandler(err, res) + } + } + + /** + * @api {post} /electrumx/balance Get balances for an array of addresses. + * @apiName Balances for an array of addresses + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Returns an array of balanes associated with an array of address. + * Limited to 20 items per request. + * + * @apiExample Example usage: + * curl -X POST "https://api.fullstack.cash/v4/electrumx/balance" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf","bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"]}' + * + * + */ + // POST handler for bulk queries on address balance + // async balanceBulk (req, res, next) { // try { - // console.log('Attempting to connect to ElectrumX server...') + // let addresses = req.body.addresses // - // // console.log('_this.electrumx: ', _this.electrumx) + // // Reject if addresses is not an array. + // if (!Array.isArray(addresses)) { + // res.status(400) + // return res.json({ + // error: 'addresses needs to be an array. Use GET for single address.' + // }) + // } // - // // Return immediately if a connection has already been established. - // if (_this.isReady) return true + // // Enforce array size rate limits + // if (!_this.routeUtils.validateArraySize(req, addresses)) { + // res.status(400) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 + // return res.json({ + // error: 'Array too large.' + // }) + // } // - // // Connect to the server. - // await _this.electrumx.connect() + // wlogger.debug( + // 'Executing electrumx.js/balanceBulk with these addresses: ', + // addresses + // ) // - // // Set the connection flag. - // _this.isReady = true + // // Validate each element in the address array. + // for (let i = 0; i < addresses.length; i++) { + // const thisAddress = addresses[i] // - // // Periodically check the connection. If it's not connected, attempt to - // // reconnect. - // _this.reconnectIntervalHandle = setInterval(async function () { - // const status = _this.electrumx.connection.status - // // console.log(`Electrumx status: ${status}`) - // - // // 1 = connected. If we're not connected, attemp to reconnect. - // if (status !== 1) { - // wlogger.info(`Electrumx not connectes. Status: ${status}`) - // wlogger.info('Attempting to reconnect...') - // await _this.electrumx.connect() - // wlogger.info('...reconnected.') + // // Ensure the input is a valid BCH address. + // try { + // _this.bchjs.Address.toLegacyAddress(thisAddress) + // } catch (err) { + // res.status(400) + // return res.json({ + // error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` + // }) // } - // }, 30000) // - // console.log('...Successfully connected to ElectrumX server.') + // // Prevent a common user error. Ensure they are using the correct network address. + // const networkIsValid = _this.routeUtils.validateNetwork(thisAddress) + // if (!networkIsValid) { + // res.status(400) + // return res.json({ + // error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` + // }) + // } + // } // - // // console.log(`_this.isReady: ${_this.isReady}`) - // return _this.isReady + // // Loops through each address and creates an array of Promises, querying + // // ElectrumX API in parallel. + // addresses = addresses.map(async (address, index) => { + // // console.log(`address: ${address}`) + // const balance = await _this._balanceFromElectrumx(address) + // + // return { + // balance, + // address + // } + // }) + // + // // Wait for all parallel Insight requests to return. + // const result = await Promise.all(addresses) + // + // // Return the array of retrieved address information. + // res.status(200) + // return res.json({ + // success: true, + // balances: result + // }) // } catch (err) { - // console.log('err: ', err) - // wlogger.error('Error in electrumx.js/connect(): ', err) - // // throw err + // wlogger.error('Error in electrumx.js/balanceBulk().', err) + // + // return _this.errorHandler(err, res) // } // } - // Disconnect from the ElectrumX server. - // async disconnect () { - // try { - // // Return immediately if the isReady flag is false. - // if (!_this.isReady) return true - // - // // Disable the reconnect timer. - // clearInterval(_this.reconnectIntervalHandle) - // - // // Disconnect from the server. - // await _this.electrumx.disconnect() - // - // // Clear the isReady flag. - // _this.isReady = false - // - // // Return true to signal that the disconnection happened successfully. - // return true - // } catch (err) { - // // console.log(`err: `, err) - // wlogger.error('Error in electrumx.js/disconnect()') - // throw err - // } - // } - - // DRY error handler. - errorHandler (err, res) { - // Attempt to decode the error message. - const { msg, status } = _this.routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ success: false, error: msg }) - } - - // Handle error patterns specific to this route. - if (err.message) { - res.status(400) - return res.json({ success: false, error: err.message }) - } - - // If error can be handled, return the stack trace - res.status(500) - return res.json({ error: util.inspect(err) }) - } - - // Root API endpoint. Simply acknowledges that it exists. - root (req, res, next) { - return res.json({ status: 'electrumx' }) - } - // Returns a promise that resolves to UTXO data for an address. Expects input // to be a cash address, and input validation to have already been done by // parent, calling function. @@ -765,199 +824,6 @@ class Electrum { // } // } - // Returns a promise that resolves to a balance for an address. Expects input - // to be a cash address, and input validation to have already been done by - // parent, calling function. - // async _balanceFromElectrumx (address) { - // try { - // // Convert the address to a scripthash. - // const scripthash = _this.addressToScripthash(address) - // - // if (!_this.isReady) { - // throw new Error( - // 'ElectrumX server connection is not ready. Call await connectToServer() first.' - // ) - // } - // - // // Query the address balance from the ElectrumX server. - // const electrumResponse = await _this.electrumx.request( - // 'blockchain.scripthash.get_balance', - // scripthash - // ) - // // console.log( - // // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` - // // ) - // - // return electrumResponse - // } catch (err) { - // // console.log('err1: ', err) - // - // // Write out error to error log. - // wlogger.error('Error in elecrumx.js/_utxosFromElectrumx(): ', err) - // throw err - // } - // } - - /** - * @api {get} /electrumx/balance/{addr} Get balance for a single address. - * @apiName Balance for a single address - * @apiGroup ElectrumX / Fulcrum - * @apiDescription Returns an object with confirmed and unconfirmed balance associated with an address. - * - * - * @apiExample Example usage: - * curl -X GET "https://api.fullstack.cash/v4/electrumx/balance/bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur3" -H "accept: application/json" - * - */ - // GET handler for single balance - // async getBalance (req, res, next) { - // try { - // const address = req.params.address - // - // // Reject if address is an array. - // if (Array.isArray(address)) { - // res.status(400) - // return res.json({ - // success: false, - // error: 'address can not be an array. Use POST for bulk upload.' - // }) - // } - // - // // Ensure the address is in cash address format. - // const cashAddr = _this.bchjs.Address.toCashAddress(address) - // - // // Prevent a common user error. Ensure they are using the correct network address. - // const networkIsValid = _this.routeUtils.validateNetwork(cashAddr) - // if (!networkIsValid) { - // res.status(400) - // return res.json({ - // success: false, - // error: - // 'Invalid network. Trying to use a testnet address on mainnet, or vice versa.' - // }) - // } - // - // wlogger.debug( - // 'Executing electrumx/getBalance with this address: ', - // cashAddr - // ) - // - // // Get data from ElectrumX server. - // const electrumResponse = await _this._balanceFromElectrumx(cashAddr) - // // console.log(`_utxosFromElectrumx(): ${JSON.stringify(electrumResponse, null, 2)}`) - // - // // Pass the error message if ElectrumX reports an error. - // if (Object.prototype.hasOwnProperty.call(electrumResponse, 'code')) { - // res.status(400) - // return res.json({ - // success: false, - // message: electrumResponse.message - // }) - // } - // - // res.status(200) - // return res.json({ - // success: true, - // balance: electrumResponse - // }) - // } catch (err) { - // // Write out error to error log. - // wlogger.error('Error in elecrumx.js/getBalance().', err) - // - // return _this.errorHandler(err, res) - // } - // } - - /** - * @api {post} /electrumx/balance Get balances for an array of addresses. - * @apiName Balances for an array of addresses - * @apiGroup ElectrumX / Fulcrum - * @apiDescription Returns an array of balanes associated with an array of address. - * Limited to 20 items per request. - * - * @apiExample Example usage: - * curl -X POST "https://api.fullstack.cash/v4/electrumx/balance" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf","bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"]}' - * - * - */ - // POST handler for bulk queries on address balance - // async balanceBulk (req, res, next) { - // try { - // let addresses = req.body.addresses - // - // // Reject if addresses is not an array. - // if (!Array.isArray(addresses)) { - // res.status(400) - // return res.json({ - // error: 'addresses needs to be an array. Use GET for single address.' - // }) - // } - // - // // Enforce array size rate limits - // if (!_this.routeUtils.validateArraySize(req, addresses)) { - // res.status(400) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - // return res.json({ - // error: 'Array too large.' - // }) - // } - // - // wlogger.debug( - // 'Executing electrumx.js/balanceBulk with these addresses: ', - // addresses - // ) - // - // // Validate each element in the address array. - // for (let i = 0; i < addresses.length; i++) { - // const thisAddress = addresses[i] - // - // // Ensure the input is a valid BCH address. - // try { - // _this.bchjs.Address.toLegacyAddress(thisAddress) - // } catch (err) { - // res.status(400) - // return res.json({ - // error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - // }) - // } - // - // // Prevent a common user error. Ensure they are using the correct network address. - // const networkIsValid = _this.routeUtils.validateNetwork(thisAddress) - // if (!networkIsValid) { - // res.status(400) - // return res.json({ - // error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - // }) - // } - // } - // - // // Loops through each address and creates an array of Promises, querying - // // ElectrumX API in parallel. - // addresses = addresses.map(async (address, index) => { - // // console.log(`address: ${address}`) - // const balance = await _this._balanceFromElectrumx(address) - // - // return { - // balance, - // address - // } - // }) - // - // // Wait for all parallel Insight requests to return. - // const result = await Promise.all(addresses) - // - // // Return the array of retrieved address information. - // res.status(200) - // return res.json({ - // success: true, - // balances: result - // }) - // } catch (err) { - // wlogger.error('Error in electrumx.js/balanceBulk().', err) - // - // return _this.errorHandler(err, res) - // } - // } - // Returns a promise that resolves an array of transaction history for an // address. Expects input to be a cash address, and input validation to have // already been done by parent, calling function. @@ -1344,30 +1210,30 @@ class Electrum { // } // } - // Convert a 'bitcoincash:...' address to a script hash used by ElectrumX. - // addressToScripthash (addrStr) { - // try { - // // console.log(`addrStr: ${addrStr}`) - // - // const address = _this.bitcore.Address.fromString(addrStr) - // // console.log(`address: ${address}`) - // - // const script = address.isPayToPublicKeyHash() - // ? _this.bitcore.Script.buildPublicKeyHashOut(address) - // : _this.bitcore.Script.buildScriptHashOut(address) - // // console.log(`script: ${script}`) - // - // const scripthash = _this.bitcore.crypto.Hash.sha256(script.toBuffer()) - // .reverse() - // .toString('hex') - // // console.log(`scripthash: ${scripthash}`) - // - // return scripthash - // } catch (err) { - // wlogger.error('Error in electrumx.js/addressToScripthash()') - // throw err - // } - // } + // DRY error handler. + errorHandler (err, res) { + // Attempt to decode the error message. + const { msg, status } = _this.routeUtils.decodeError(err) + if (msg) { + res.status(status) + return res.json({ success: false, error: msg }) + } + + // Handle error patterns specific to this route. + if (err.message) { + res.status(400) + return res.json({ success: false, error: err.message }) + } + + // If error can be handled, return the stack trace + res.status(500) + return res.json({ error: util.inspect(err) }) + } + + // Root API endpoint. Simply acknowledges that it exists. + root (req, res, next) { + return res.json({ status: 'electrumx' }) + } } module.exports = Electrum diff --git a/test/v5/a01-electrumx.js b/test/v5/a01-electrumx.js index d2fb814..3fa8e19 100644 --- a/test/v5/a01-electrumx.js +++ b/test/v5/a01-electrumx.js @@ -32,15 +32,15 @@ util.inspect.defaultOptions = { depth: 1 } // A wrapper for asserting that the correct response is returned when an error // is expected. -function expectRouteError (res, result, expectedError, code = 400) { - assert.equal(res.statusCode, code, `HTTP status code ${code} expected.`) - - assert.property(result, 'error') - assert.include(result.error, expectedError) - - assert.property(result, 'success') - assert.equal(result.success, false) -} +// function expectRouteError (res, result, expectedError, code = 400) { +// assert.equal(res.statusCode, code, `HTTP status code ${code} expected.`) +// +// assert.property(result, 'error') +// assert.include(result.error, expectedError) +// +// assert.property(result, 'success') +// assert.equal(result.success, false) +// } describe('#Electrumx', () => { let req, res @@ -54,20 +54,19 @@ describe('#Electrumx', () => { if (!process.env.NETWORK) process.env.NETWORK = 'testnet' // Connect to electrumx servers if this is an integration test. - if (process.env.TEST === 'integration') { - await electrumxRoute.connect() - console.log('Connected to ElectrumX server') - } + // if (process.env.TEST === 'integration') { + // await electrumxRoute.connect() + // console.log('Connected to ElectrumX server') + // } }) after(async () => { // console.log(`electrumxRoute.electrumx: `, electrumxRoute.electrumx) - // Disconnect from the electrumx server if this is an integration test. - if (process.env.TEST === 'integration') { - await electrumxRoute.disconnect() - console.log('Disconnected from ElectrumX server') - } + // if (process.env.TEST === 'integration') { + // await electrumxRoute.disconnect() + // console.log('Disconnected from ElectrumX server') + // } }) // Setup the mocks before each test. @@ -95,15 +94,15 @@ describe('#Electrumx', () => { }) // A wrapper for stubbing with the Sinon sandbox. - function stubMethodForUnitTests (obj, method, value) { - if (process.env.TEST !== 'unit') return false - - electrumxRoute.isReady = true // Force flag. - - sandbox.stub(obj, method).resolves(value) - - return true - } + // function stubMethodForUnitTests (obj, method, value) { + // if (process.env.TEST !== 'unit') return false + // + // electrumxRoute.isReady = true // Force flag. + // + // sandbox.stub(obj, method).resolves(value) + // + // return true + // } describe('#root', () => { // root route handler. @@ -116,29 +115,106 @@ describe('#Electrumx', () => { }) }) - // describe('#addressToScripthash', () => { - // it('should accurately return a scripthash for a P2PKH address', () => { - // const addr = 'bitcoincash:qpr270a5sxphltdmggtj07v4nskn9gmg9yx4m5h7s4' - // - // const scripthash = electrumxRoute.addressToScripthash(addr) - // - // const expectedOutput = - // 'bce4d5f2803bd1ed7c1ba00dcb3edffcbba50524af7c879d6bb918d04f138965' - // - // assert.equal(scripthash, expectedOutput) - // }) - // - // it('should accurately return a scripthash for a P2SH address', () => { - // const addr = 'bitcoincash:pz0z7u9p96h2p6hfychxdrmwgdlzpk5luc5yks2wxq' - // - // const scripthash = electrumxRoute.addressToScripthash(addr) - // - // const expectedOutput = - // '8bc2235c8e7d5634d9ec429fc0171f2c58e728d4f1e2fb7e440e313133cfa4f0' - // - // assert.equal(scripthash, expectedOutput) - // }) - // }) + describe('#getBalance', () => { + it('should throw 400 if address is empty', async () => { + const result = await electrumxRoute.getBalance(req, res) + // console.log(`result: ${util.inspect(result)}`) + + assert.equal(res.statusCode, 422, 'Expect 422 status code') + + assert.property(result, 'error') + assert.include(result.error, 'Unsupported address format') + + assert.property(result, 'success') + assert.equal(result.success, false) + }) + + it('should throw 400 on array input', async () => { + req.params.address = ['qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] + + const result = await electrumxRoute.getBalance(req, res) + // console.log(`result: ${util.inspect(result)}`) + + assert.equal(res.statusCode, 400, 'Expect 400 status code') + + assert.property(result, 'error') + assert.include(result.error, 'address can not be an array') + + assert.property(result, 'success') + assert.equal(result.success, false) + }) + + it('should throw an error for an invalid address', async () => { + req.params.address = '02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' + + const result = await electrumxRoute.getBalance(req, res) + // console.log(`result: ${util.inspect(result)}`) + + assert.equal(res.statusCode, 422, 'Expect 422 status code') + + assert.property(result, 'error') + assert.include(result.error, 'Unsupported address format') + + assert.property(result, 'success') + assert.equal(result.success, false) + }) + + it('should detect a network mismatch', async () => { + req.params.address = 'bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4' + + const result = await electrumxRoute.getBalance(req, res) + // console.log(`result: ${util.inspect(result)}`) + + assert.equal(res.statusCode, 400, 'Expect 400 status code') + + assert.property(result, 'error') + assert.include(result.error, 'Invalid network', 'Proper error message') + + assert.property(result, 'success') + assert.equal(result.success, false) + }) + + it('should pass errors from electrum-cash to user', async () => { + // Address has invalid checksum. + req.params.address = + 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' + + // Call the details API. + const result = await electrumxRoute.getBalance(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, 'success') + assert.equal(result.success, false) + + assert.property(result, 'error') + assert.include(result.error, 'Unsupported address format') + }) + + it('should get balance for a single address', async () => { + req.params.address = + 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' + + // Mock unit tests to prevent live network calls. + if (process.env.TEST === 'unit') { + electrumxRoute.isReady = true // Force flag. + + sandbox + .stub(electrumxRoute.axios, 'get') + .resolves({ data: mockData.balance }) + } + + // Call the details API. + const result = await electrumxRoute.getBalance(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, 'success') + assert.equal(result.success, true) + + assert.property(result, 'balance') + assert.property(result.balance, 'confirmed') + assert.property(result.balance, 'unconfirmed') + }) + }) // describe('#_utxosFromElectrumx', () => { // it('should throw error for invalid address', async () => { @@ -1058,107 +1134,6 @@ describe('#Electrumx', () => { // }) // }) - // describe('#getBalance', () => { - // it('should throw 400 if address is empty', async () => { - // const result = await electrumxRoute.getBalance(req, res) - // // console.log(`result: ${util.inspect(result)}`) - // - // assert.equal(res.statusCode, 422, 'Expect 422 status code') - // - // assert.property(result, 'error') - // assert.include(result.error, 'Unsupported address format') - // - // assert.property(result, 'success') - // assert.equal(result.success, false) - // }) - // - // it('should throw 400 on array input', async () => { - // req.params.address = ['qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] - // - // const result = await electrumxRoute.getBalance(req, res) - // // console.log(`result: ${util.inspect(result)}`) - // - // assert.equal(res.statusCode, 400, 'Expect 400 status code') - // - // assert.property(result, 'error') - // assert.include(result.error, 'address can not be an array') - // - // assert.property(result, 'success') - // assert.equal(result.success, false) - // }) - // - // it('should throw an error for an invalid address', async () => { - // req.params.address = '02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' - // - // const result = await electrumxRoute.getBalance(req, res) - // // console.log(`result: ${util.inspect(result)}`) - // - // assert.equal(res.statusCode, 422, 'Expect 422 status code') - // - // assert.property(result, 'error') - // assert.include(result.error, 'Unsupported address format') - // - // assert.property(result, 'success') - // assert.equal(result.success, false) - // }) - // - // it('should detect a network mismatch', async () => { - // req.params.address = 'bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4' - // - // const result = await electrumxRoute.getBalance(req, res) - // // console.log(`result: ${util.inspect(result)}`) - // - // assert.equal(res.statusCode, 400, 'Expect 400 status code') - // - // assert.property(result, 'error') - // assert.include(result.error, 'Invalid network', 'Proper error message') - // - // assert.property(result, 'success') - // assert.equal(result.success, false) - // }) - // - // it('should pass errors from electrum-cash to user', async () => { - // // Address has invalid checksum. - // req.params.address = - // 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' - // - // // Call the details API. - // const result = await electrumxRoute.getBalance(req, res) - // // console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.property(result, 'success') - // assert.equal(result.success, false) - // - // assert.property(result, 'error') - // assert.include(result.error, 'Unsupported address format') - // }) - // - // it('should get balance for a single address', async () => { - // req.params.address = - // 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' - // - // // Mock unit tests to prevent live network calls. - // if (process.env.TEST === 'unit') { - // electrumxRoute.isReady = true // Force flag. - // - // sandbox - // .stub(electrumxRoute, '_balanceFromElectrumx') - // .resolves(mockData.balance) - // } - // - // // Call the details API. - // const result = await electrumxRoute.getBalance(req, res) - // // console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.property(result, 'success') - // assert.equal(result.success, true) - // - // assert.property(result, 'balance') - // assert.property(result.balance, 'confirmed') - // assert.property(result.balance, 'unconfirmed') - // }) - // }) - // describe('#balanceBulk', () => { // it('should throw an error for an empty body', async () => { // req.body = {} diff --git a/test/v5/mocks/electrumx-mock.js b/test/v5/mocks/electrumx-mock.js index 41c96a1..a4d37a5 100644 --- a/test/v5/mocks/electrumx-mock.js +++ b/test/v5/mocks/electrumx-mock.js @@ -20,8 +20,11 @@ const utxos = [ ] const balance = { - confirmed: 7000, - unconfirmed: 0 + success: true, + balance: { + confirmed: 7000, + unconfirmed: 0 + } } const txHistory = [ @@ -44,7 +47,8 @@ const txDetails = { blocktime: 1578327094, confirmations: 31861, hash: '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251', - hex: '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000', + hex: + '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000', locktime: 0, size: 392, time: 1578327094, @@ -53,8 +57,10 @@ const txDetails = { vin: [ { scriptSig: { - asm: 'dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e[ALL|FORKID] 020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309', - hex: '41dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309' + asm: + 'dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e[ALL|FORKID] 020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309', + hex: + '41dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309' }, sequence: 4294967295, txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165', @@ -62,8 +68,10 @@ const txDetails = { }, { scriptSig: { - asm: '347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f77[ALL|FORKID] 028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954', - hex: '41347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954' + asm: + '347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f77[ALL|FORKID] 028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954', + hex: + '41347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954' }, sequence: 4294967295, txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165', @@ -86,7 +94,8 @@ const txDetails = { n: 1, scriptPubKey: { addresses: ['bitcoincash: qq59hv6s3qdjrtyfwfxxldkuj9xsjmx48vrz882knz'], - asm: 'OP_DUP OP_HASH160 285bb350881b21ac89724c6fb6dc914d096cd53b OP_EQUALVERIFY OP_CHECKSIG', + asm: + 'OP_DUP OP_HASH160 285bb350881b21ac89724c6fb6dc914d096cd53b OP_EQUALVERIFY OP_CHECKSIG', hex: '76a914285bb350881b21ac89724c6fb6dc914d096cd53b88ac', reqSigs: 1, type: 'pubkeyhash' @@ -97,7 +106,8 @@ const txDetails = { n: 2, scriptPubKey: { addresses: ['bitcoincash: qpzlruwy4xu5rxjs3z37nsj29y7h59gwvsu4ddp0u4'], - asm: 'OP_DUP OP_HASH160 45f1f1c4a9b9419a5088a3e9c24a293d7a150e64 OP_EQUALVERIFY OP_CHECKSIG', + asm: + 'OP_DUP OP_HASH160 45f1f1c4a9b9419a5088a3e9c24a293d7a150e64 OP_EQUALVERIFY OP_CHECKSIG', hex: '76a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac', reqSigs: 1, type: 'pubkeyhash'