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 5115925..2ee0d66 100644 --- a/src/routes/v5/electrumx.js +++ b/src/routes/v5/electrumx.js @@ -8,9 +8,9 @@ const express = require('express') const router = express.Router() const axios = require('axios') const util = require('util') -const bitcore = require('bitcore-lib-cash') +// const bitcore = require('bitcore-lib-cash') -const ElectrumCash = require('electrum-cash').ElectrumClient +// const ElectrumCash = require('electrum-cash').ElectrumClient // const ElectrumCash = require('/home/trout/work/personal/electrum-cash/electrum.js').Client // eslint-disable-line const wlogger = require('../../util/winston-logging') @@ -32,770 +32,37 @@ class Electrum { _this.axios = axios _this.routeUtils = routeUtils _this.bchjs = bchjs - _this.bitcore = bitcore + // _this.bitcore = bitcore - _this.electrumx = new ElectrumCash( - 'bch-api', - '1.4.1', - process.env.FULCRUM_URL, - process.env.FULCRUM_PORT - // '192.168.0.6', - // '50002' - ) + _this.fulcrumApi = 'http://fulcrum-api.fullstackbch.nl/v1/' - _this.isReady = false + // _this.electrumx = new ElectrumCash( + // 'bch-api', + // '1.4.1', + // process.env.FULCRUM_URL, + // process.env.FULCRUM_PORT + // // '192.168.0.6', + // // '50002' + // ) + + // _this.isReady = false // _this.connectToServers() _this.router = router _this.router.get('/', _this.root) - _this.router.get('/utxos/:address', _this.getUtxos) - _this.router.post('/utxos', _this.utxosBulk) - _this.router.get('/tx/data/:txid', _this.getTransactionDetails) - _this.router.post('/tx/data', _this.transactionDetailsBulk) - _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 () { - try { - console.log('Attempting to connect to ElectrumX server...') - - // console.log('_this.electrumx: ', _this.electrumx) - - // Return immediately if a connection has already been established. - if (_this.isReady) return true - - // Connect to the server. - await _this.electrumx.connect() - - // Set the connection flag. - _this.isReady = true - - // 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.') - } - }, 30000) - - console.log('...Successfully connected to ElectrumX server.') - - // console.log(`_this.isReady: ${_this.isReady}`) - return _this.isReady - } catch (err) { - console.log('err: ', err) - wlogger.error('Error in electrumx.js/connect(): ', err) - // throw err - } - } - - // 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. - async _utxosFromElectrumx (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 utxos from the ElectrumX server. - const electrumResponse = await _this.electrumx.request( - 'blockchain.scripthash.listunspent', - scripthash - ) - // console.log( - // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` - // ) - - return electrumResponse - } catch (err) { - // console.log('err: ', err) - - // Write out error to error log. - wlogger.error('Error in elecrumx.js/_utxosFromElectrumx(): ', err) - throw err - } - } - - /** - * @api {get} /electrumx/utxos/{addr} Get utxos for a single address. - * @apiName UTXOs for a single address - * @apiGroup ElectrumX / Fulcrum - * @apiDescription Returns an object with UTXOs associated with an address. - * - * - * @apiExample Example usage: - * curl -X GET "https://api.fullstack.cash/v4/electrumx/utxos/bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur3" -H "accept: application/json" - * - */ - // GET handler for single balance - async getUtxos (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.' - }) - } - - 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/getUtxos with this address: ', - cashAddr - ) - - // Get data from ElectrumX server. - const electrumResponse = await _this._utxosFromElectrumx(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, - utxos: electrumResponse - }) - } catch (err) { - // Write out error to error log. - wlogger.error('Error in elecrumx.js/getUtxos().', err) - - return _this.errorHandler(err, res) - } - } - - /** - * @api {post} /electrumx/utxo Get utxos for an array of addresses. - * @apiName UTXOs for an array of addresses - * @apiGroup ElectrumX / Fulcrum - * @apiDescription Returns an array of objects with UTXOs associated with an address. - * Limited to 20 items per request. - * - * @apiExample Example usage: - * curl -X POST "https://api.fullstack.cash/v4/electrumx/utxos" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf","bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"]}' - * - * - */ - // POST handler for bulk queries on address details - async utxosBulk (req, res, next) { - try { - let addresses = req.body.addresses - // const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0 - - // 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/utxoBulk 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 - // Insight API in parallel. - addresses = addresses.map(async (address, index) => { - // console.log(`address: ${address}`) - const utxos = await _this._utxosFromElectrumx(address) - - return { - utxos, - 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, - utxos: result - }) - } catch (err) { - wlogger.error('Error in electrumx.js/utxoBulk().', err) - - return _this.errorHandler(err, res) - } - } - - // Returns a promise that resolves to transaction details data for a txid. - // Expects input to be a txid string, and input validation to have already - // been done by parent, calling function. - async _transactionDetailsFromElectrum (txid, verbose = true) { - try { - if (!_this.isReady) { - throw new Error( - 'ElectrumX server connection is not ready. Call await connectToServer() first.' - ) - } - - // Query the utxos from the ElectrumX server. - const electrumResponse = await _this.electrumx.request( - 'blockchain.transaction.get', - txid, - verbose - ) - // console.log( - // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` - // ) - - return electrumResponse - } catch (err) { - // console.log('err: ', err) - - // Write out error to error log. - wlogger.error( - 'Error in elecrumx.js/_transactionDetailsFromElectrum(): ', - err - ) - throw err - } - } - - /** - * @api {get} /electrumx/tx/data/{txid} Get transaction details for a TXID - * @apiName transaction details for a TXID - * @apiGroup ElectrumX / Fulcrum - * @apiDescription Returns an object with transaction details of the TXID - * - * - * @apiExample Example usage: - * curl -X GET "https://api.fullstack.cash/v4/electrumx/tx/data/a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d" -H "accept: application/json" - * - */ - // GET handler for single transaction - async getTransactionDetails (req, res, next) { - try { - const txid = req.params.txid - const verbose = req.query.verbose - - // Reject if txid is anything other than a string - if (typeof txid !== 'string') { - res.status(400) - return res.json({ - success: false, - error: 'txid must be a string' - }) - } - - wlogger.debug( - 'Executing electrumx/getTransactionDetails with this txid: ', - txid - ) - - // Get data from ElectrumX server. - const electrumResponse = await _this._transactionDetailsFromElectrum( - txid, - verbose - ) - // console.log(`_transactionDetailsFromElectrum(): ${JSON.stringify(electrumResponse, null, 2)}`) - - // Pass the error message if ElectrumX reports an error. - if (electrumResponse instanceof Error) { - res.status(400) - return res.json({ - success: false, - error: electrumResponse.message - }) - } - - res.status(200) - return res.json({ - success: true, - details: electrumResponse - }) - } catch (err) { - // Write out error to error log. - wlogger.error('Error in elecrumx.js/getTransactionDetails().', err) - - return _this.errorHandler(err, res) - } - } - - /** - * @api {post} /electrumx/tx/data Get transaction details for an array of TXIDs - * @apiName Transaction details for an array of TXIDs - * @apiGroup ElectrumX / Fulcrum - * @apiDescription Returns an array of objects with transaction details of an array of TXIDs. - * Limited to 20 items per request. - * - * @apiExample Example usage: - * curl -X POST "https://api.fullstack.cash/v4/electrumx/tx/data" -H "accept: application/json" -H "Content-Type: application/json" -d '{"txids":["a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d","a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d"], "verbose":false}' - * - * - */ - // POST handler for bulk queries on transaction details - async transactionDetailsBulk (req, res, next) { - try { - const txids = req.body.txids - const verbose = req.body.verbose || true - - // Reject if txids is not an array. - if (!Array.isArray(txids)) { - res.status(400) - return res.json({ - success: false, - error: 'txids needs to be an array. Use GET for single txid.' - }) - } - - // Enforce array size rate limits - if (!_this.routeUtils.validateArraySize(req, txids)) { - res.status(400) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - success: false, - error: 'Array too large.' - }) - } - - wlogger.debug( - 'Executing electrumx.js/transactionDetailsBulk with these txids: ', - txids - ) - - // Loops through each address and creates an array of Promises, querying - // the Electrum server in parallel. - const transactions = txids.map(async (txid, index) => { - // console.log(`address: ${address}`) - const details = await _this._transactionDetailsFromElectrum( - txid, - verbose - ) - - return { details, txid } - }) - - // Wait for all parallel Electrum requests to return. - const result = await Promise.all(transactions) - - // Return the array of retrieved transaction details. - res.status(200) - return res.json({ - success: true, - transactions: result - }) - } catch (err) { - wlogger.error('Error in electrumx.js/transactionDetailsBulk().', err) - - return _this.errorHandler(err, res) - } - } - - // Returns a promise that resolves to transaction ID of the broadcasted transaction or an error. - // Expects input to be a txHex string, and input validation to have already - // been done by parent, calling function. - async _broadcastTransactionWithElectrum (txHex) { - try { - if (!_this.isReady) { - throw new Error( - 'ElectrumX server connection is not ready. Call await connectToServer() first.' - ) - } - - // Broadcast the transaction hex to the ElectrumX server. - const electrumResponse = await _this.electrumx.request( - 'blockchain.transaction.broadcast', - txHex - ) - // console.log( - // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` - // ) - - return electrumResponse - } catch (err) { - // console.log('err: ', err) - - // Write out error to error log. - wlogger.error( - 'Error in elecrumx.js/_transactionDetailsFromElectrum(): ', - err - ) - throw err - } - } - - /** - * @api {post} /electrumx/tx/broadcast Broadcast a raw transaction - * @apiName Broadcast a raw transaction - * @apiGroup ElectrumX / Fulcrum - * @apiDescription Broadcast a raw transaction and return the transaction ID on success or error on failure. - * - * @apiExample Example usage: - * curl -X POST "https://api.fullstack.cash/v4/electrumx/tx/broadcast" -H "accept: application/json" -H "Content-Type: application/json" -d '{"txHex":"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"}' - * - */ - // POST handler for broadcasting a single transaction - async broadcastTransaction (req, res, next) { - try { - const txHex = req.body.txHex - - if (typeof txHex !== 'string') { - res.status(400) - return res.json({ - success: false, - error: 'request body must be a string.' - }) - } - - wlogger.debug( - 'Executing electrumx/broadcastTransaction with this tx hex: ', - txHex - ) - - // Get data from ElectrumX server. - const electrumResponse = await _this._broadcastTransactionWithElectrum(txHex) - // console.log(`_utxosFromElectrumx(): ${JSON.stringify(electrumResponse, null, 2)}`) - - // Pass the error message if ElectrumX reports an error. - if (electrumResponse instanceof Error) { - res.status(400) - return res.json({ - success: false, - error: electrumResponse.message - }) - } - - res.status(200) - return res.json({ - success: true, - txid: electrumResponse - }) - } catch (err) { - wlogger.error('Error in electrumx.js/broadcastTransaction().', err) - - return _this.errorHandler(err, res) - } - } - - // Returns a promise that resolves to block header data for a block height. - // Expects input to be a height number, and input validation to have already - // been done by parent, calling function. - async _blockHeadersFromElectrum (height, count = 1) { - try { - if (!_this.isReady) { - throw new Error( - 'ElectrumX server connection is not ready. Call await connectToServer() first.' - ) - } - - // Query the block header from the ElectrumX server. - const electrumResponse = await _this.electrumx.request('blockchain.block.headers', height, count) - // console.log( - // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` - // ) - - const HEADER_SIZE = 80 * 2 - - if (!(electrumResponse instanceof Error)) { - const headers = electrumResponse.hex.match(new RegExp(`.{1,${HEADER_SIZE}}`, 'g')) - return headers - } - - return electrumResponse - } catch (err) { - // console.log('err: ', err) - - // Write out error to error log. - wlogger.error( - 'Error in elecrumx.js/_blockHeaderFromElectrum(): ', - err - ) - throw err - } - } - - /** - * @api {get} /electrumx/block/headers/{height} Get `count` block headers starting at a height - * @apiName Block header data for a `count` blocks starting at a block height - * @apiGroup ElectrumX / Fulcrum - * @apiDescription Returns an array with block headers starting at the block height - * - * - * @apiExample Example usage: - * curl -X GET "https://api.fullstack.cash/v4/electrumx/block/header/42?count=2" -H "accept: application/json" - * - */ - // GET handler for single block headers - async getBlockHeaders (req, res, next) { - try { - const height = Number(req.params.height) - const count = req.query.count === undefined ? 1 : Number(req.query.count) - - // Reject if height is not a number - if (Number.isNaN(height) || height < 0) { - res.status(400) - return res.json({ - success: false, - error: 'height must be a positive number' - }) - } - - // Reject if height is not a number - if (Number.isNaN(count) || count < 0) { - res.status(400) - return res.json({ - success: false, - error: 'count must be a positive number' - }) - } - - wlogger.debug( - 'Executing electrumx/getBlockHeaders with this height: ', - height - ) - - // Get data from ElectrumX server. - const electrumResponse = await _this._blockHeadersFromElectrum(height, count) - // console.log(`_transactionDetailsFromElectrum(): ${JSON.stringify(electrumResponse, null, 2)}`) - - // Pass the error message if ElectrumX reports an error. - if (electrumResponse instanceof Error) { - res.status(400) - return res.json({ - success: false, - error: electrumResponse.message - }) - } - - res.status(200) - return res.json({ - success: true, - headers: electrumResponse - }) - } catch (err) { - // Write out error to error log. - wlogger.error('Error in elecrumx.js/getBlockHeader().', err) - - return _this.errorHandler(err, res) - } - } - - /** - * @api {post} /electrumx/block/headers Get block headers for an array of height + count pairs - * @apiName Block headers for an array of height + count pairs - * @apiGroup ElectrumX / Fulcrum - * @apiDescription Returns an array of objects with blockheaders of an array of TXIDs. - * Limited to 20 items per request. - * - * @apiExample Example usage: - * curl -X POST "https://api.fullstack.cash/v4/electrumx/block/headers" -H "accept: application/json" -H "Content-Type: application/json" -d '{"heights":[{ "height": 42, count: 2 }, { "height": 100, count: 5 }]}' - * - */ - // POST handler for bulk queries on block headers - async blockHeadersBulk (req, res, next) { - try { - const heights = req.body.heights - - // Reject if heights is not an array. - if (!Array.isArray(heights)) { - res.status(400) - return res.json({ - success: false, - error: 'heights needs to be an array. Use GET for single height.' - }) - } - - // Enforce array size rate limits - if (!_this.routeUtils.validateArraySize(req, heights)) { - res.status(400) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - success: false, - error: 'Array too large.' - }) - } - - wlogger.debug( - 'Executing electrumx.js/blockHeadersBulk with these txids: ', - heights - ) - - // Loops through each address and creates an array of Promises, querying - // the Electrum server in parallel. - const transactions = heights.map(async (obj) => { - const headers = await _this._blockHeadersFromElectrum( - obj.height, - obj.count - ) - - return { headers } - }) - - // Wait for all parallel Electrum requests to return. - const result = await Promise.all(transactions) - - // Return the array of retrieved transaction details. - res.status(200) - return res.json({ - success: true, - headers: result - }) - } catch (err) { - wlogger.error('Error in electrumx.js/blockHeadersBulk().', err) - - return _this.errorHandler(err, res) - } - } - - // 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 - } + // _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) + // _this.router.post('/tx/data', _this.transactionDetailsBulk) + // _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('/transactions/:address', _this.getTransactions) + // _this.router.post('/transactions', _this.transactionsBulk) + // _this.router.get('/unconfirmed/:address', _this.getMempool) + // _this.router.post('/unconfirmed', _this.mempoolBulk) } /** @@ -842,24 +109,12 @@ class Electrum { 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 - }) - } + const response = await this.axios.get( + `${this.fulcrumApi}electrumx/balance/${address}` + ) res.status(200) - return res.json({ - success: true, - balance: electrumResponse - }) + return res.json(response.data) } catch (err) { // Write out error to error log. wlogger.error('Error in elecrumx.js/getBalance().', err) @@ -881,115 +136,726 @@ class Electrum { * */ // POST handler for bulk queries on address balance - async balanceBulk (req, res, next) { - try { - let addresses = req.body.addresses + // 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) + // } + // } - // 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.' - }) - } + // 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. + // async _utxosFromElectrumx (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 utxos from the ElectrumX server. + // const electrumResponse = await _this.electrumx.request( + // 'blockchain.scripthash.listunspent', + // scripthash + // ) + // // console.log( + // // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` + // // ) + // + // return electrumResponse + // } catch (err) { + // // console.log('err: ', err) + // + // // Write out error to error log. + // wlogger.error('Error in elecrumx.js/_utxosFromElectrumx(): ', err) + // throw err + // } + // } - // 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.' - }) - } + /** + * @api {get} /electrumx/utxos/{addr} Get utxos for a single address. + * @apiName UTXOs for a single address + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Returns an object with UTXOs associated with an address. + * + * + * @apiExample Example usage: + * curl -X GET "https://api.fullstack.cash/v4/electrumx/utxos/bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur3" -H "accept: application/json" + * + */ + // GET handler for single balance + // async getUtxos (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.' + // }) + // } + // + // 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/getUtxos with this address: ', + // cashAddr + // ) + // + // // Get data from ElectrumX server. + // const electrumResponse = await _this._utxosFromElectrumx(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, + // utxos: electrumResponse + // }) + // } catch (err) { + // // Write out error to error log. + // wlogger.error('Error in elecrumx.js/getUtxos().', err) + // + // return _this.errorHandler(err, res) + // } + // } - wlogger.debug( - 'Executing electrumx.js/balanceBulk with these addresses: ', - addresses - ) + /** + * @api {post} /electrumx/utxo Get utxos for an array of addresses. + * @apiName UTXOs for an array of addresses + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Returns an array of objects with UTXOs associated with an address. + * Limited to 20 items per request. + * + * @apiExample Example usage: + * curl -X POST "https://api.fullstack.cash/v4/electrumx/utxos" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf","bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"]}' + * + * + */ + // POST handler for bulk queries on address details + // async utxosBulk (req, res, next) { + // try { + // let addresses = req.body.addresses + // // const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0 + // + // // 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/utxoBulk 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 + // // Insight API in parallel. + // addresses = addresses.map(async (address, index) => { + // // console.log(`address: ${address}`) + // const utxos = await _this._utxosFromElectrumx(address) + // + // return { + // utxos, + // 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, + // utxos: result + // }) + // } catch (err) { + // wlogger.error('Error in electrumx.js/utxoBulk().', err) + // + // return _this.errorHandler(err, res) + // } + // } - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] + // Returns a promise that resolves to transaction details data for a txid. + // Expects input to be a txid string, and input validation to have already + // been done by parent, calling function. + // async _transactionDetailsFromElectrum (txid, verbose = true) { + // try { + // if (!_this.isReady) { + // throw new Error( + // 'ElectrumX server connection is not ready. Call await connectToServer() first.' + // ) + // } + // + // // Query the utxos from the ElectrumX server. + // const electrumResponse = await _this.electrumx.request( + // 'blockchain.transaction.get', + // txid, + // verbose + // ) + // // console.log( + // // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` + // // ) + // + // return electrumResponse + // } catch (err) { + // // console.log('err: ', err) + // + // // Write out error to error log. + // wlogger.error( + // 'Error in elecrumx.js/_transactionDetailsFromElectrum(): ', + // err + // ) + // throw err + // } + // } - // 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}` - }) - } + /** + * @api {get} /electrumx/tx/data/{txid} Get transaction details for a TXID + * @apiName transaction details for a TXID + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Returns an object with transaction details of the TXID + * + * + * @apiExample Example usage: + * curl -X GET "https://api.fullstack.cash/v4/electrumx/tx/data/a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d" -H "accept: application/json" + * + */ + // GET handler for single transaction + // async getTransactionDetails (req, res, next) { + // try { + // const txid = req.params.txid + // const verbose = req.query.verbose + // + // // Reject if txid is anything other than a string + // if (typeof txid !== 'string') { + // res.status(400) + // return res.json({ + // success: false, + // error: 'txid must be a string' + // }) + // } + // + // wlogger.debug( + // 'Executing electrumx/getTransactionDetails with this txid: ', + // txid + // ) + // + // // Get data from ElectrumX server. + // const electrumResponse = await _this._transactionDetailsFromElectrum( + // txid, + // verbose + // ) + // // console.log(`_transactionDetailsFromElectrum(): ${JSON.stringify(electrumResponse, null, 2)}`) + // + // // Pass the error message if ElectrumX reports an error. + // if (electrumResponse instanceof Error) { + // res.status(400) + // return res.json({ + // success: false, + // error: electrumResponse.message + // }) + // } + // + // res.status(200) + // return res.json({ + // success: true, + // details: electrumResponse + // }) + // } catch (err) { + // // Write out error to error log. + // wlogger.error('Error in elecrumx.js/getTransactionDetails().', err) + // + // return _this.errorHandler(err, res) + // } + // } - // 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.` - }) - } - } + /** + * @api {post} /electrumx/tx/data Get transaction details for an array of TXIDs + * @apiName Transaction details for an array of TXIDs + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Returns an array of objects with transaction details of an array of TXIDs. + * Limited to 20 items per request. + * + * @apiExample Example usage: + * curl -X POST "https://api.fullstack.cash/v4/electrumx/tx/data" -H "accept: application/json" -H "Content-Type: application/json" -d '{"txids":["a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d","a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d"], "verbose":false}' + * + * + */ + // POST handler for bulk queries on transaction details + // async transactionDetailsBulk (req, res, next) { + // try { + // const txids = req.body.txids + // const verbose = req.body.verbose || true + // + // // Reject if txids is not an array. + // if (!Array.isArray(txids)) { + // res.status(400) + // return res.json({ + // success: false, + // error: 'txids needs to be an array. Use GET for single txid.' + // }) + // } + // + // // Enforce array size rate limits + // if (!_this.routeUtils.validateArraySize(req, txids)) { + // res.status(400) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 + // return res.json({ + // success: false, + // error: 'Array too large.' + // }) + // } + // + // wlogger.debug( + // 'Executing electrumx.js/transactionDetailsBulk with these txids: ', + // txids + // ) + // + // // Loops through each address and creates an array of Promises, querying + // // the Electrum server in parallel. + // const transactions = txids.map(async (txid, index) => { + // // console.log(`address: ${address}`) + // const details = await _this._transactionDetailsFromElectrum( + // txid, + // verbose + // ) + // + // return { details, txid } + // }) + // + // // Wait for all parallel Electrum requests to return. + // const result = await Promise.all(transactions) + // + // // Return the array of retrieved transaction details. + // res.status(200) + // return res.json({ + // success: true, + // transactions: result + // }) + // } catch (err) { + // wlogger.error('Error in electrumx.js/transactionDetailsBulk().', err) + // + // return _this.errorHandler(err, res) + // } + // } - // 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) + // Returns a promise that resolves to transaction ID of the broadcasted transaction or an error. + // Expects input to be a txHex string, and input validation to have already + // been done by parent, calling function. + // async _broadcastTransactionWithElectrum (txHex) { + // try { + // if (!_this.isReady) { + // throw new Error( + // 'ElectrumX server connection is not ready. Call await connectToServer() first.' + // ) + // } + // + // // Broadcast the transaction hex to the ElectrumX server. + // const electrumResponse = await _this.electrumx.request( + // 'blockchain.transaction.broadcast', + // txHex + // ) + // // console.log( + // // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` + // // ) + // + // return electrumResponse + // } catch (err) { + // // console.log('err: ', err) + // + // // Write out error to error log. + // wlogger.error( + // 'Error in elecrumx.js/_transactionDetailsFromElectrum(): ', + // err + // ) + // throw err + // } + // } - return { - balance, - address - } - }) + /** + * @api {post} /electrumx/tx/broadcast Broadcast a raw transaction + * @apiName Broadcast a raw transaction + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Broadcast a raw transaction and return the transaction ID on success or error on failure. + * + * @apiExample Example usage: + * curl -X POST "https://api.fullstack.cash/v4/electrumx/tx/broadcast" -H "accept: application/json" -H "Content-Type: application/json" -d '{"txHex":"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"}' + * + */ + // POST handler for broadcasting a single transaction + // async broadcastTransaction (req, res, next) { + // try { + // const txHex = req.body.txHex + // + // if (typeof txHex !== 'string') { + // res.status(400) + // return res.json({ + // success: false, + // error: 'request body must be a string.' + // }) + // } + // + // wlogger.debug( + // 'Executing electrumx/broadcastTransaction with this tx hex: ', + // txHex + // ) + // + // // Get data from ElectrumX server. + // const electrumResponse = await _this._broadcastTransactionWithElectrum(txHex) + // // console.log(`_utxosFromElectrumx(): ${JSON.stringify(electrumResponse, null, 2)}`) + // + // // Pass the error message if ElectrumX reports an error. + // if (electrumResponse instanceof Error) { + // res.status(400) + // return res.json({ + // success: false, + // error: electrumResponse.message + // }) + // } + // + // res.status(200) + // return res.json({ + // success: true, + // txid: electrumResponse + // }) + // } catch (err) { + // wlogger.error('Error in electrumx.js/broadcastTransaction().', err) + // + // return _this.errorHandler(err, res) + // } + // } - // Wait for all parallel Insight requests to return. - const result = await Promise.all(addresses) + // Returns a promise that resolves to block header data for a block height. + // Expects input to be a height number, and input validation to have already + // been done by parent, calling function. + // async _blockHeadersFromElectrum (height, count = 1) { + // try { + // if (!_this.isReady) { + // throw new Error( + // 'ElectrumX server connection is not ready. Call await connectToServer() first.' + // ) + // } + // + // // Query the block header from the ElectrumX server. + // const electrumResponse = await _this.electrumx.request('blockchain.block.headers', height, count) + // // console.log( + // // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` + // // ) + // + // const HEADER_SIZE = 80 * 2 + // + // if (!(electrumResponse instanceof Error)) { + // const headers = electrumResponse.hex.match(new RegExp(`.{1,${HEADER_SIZE}}`, 'g')) + // return headers + // } + // + // return electrumResponse + // } catch (err) { + // // console.log('err: ', err) + // + // // Write out error to error log. + // wlogger.error( + // 'Error in elecrumx.js/_blockHeaderFromElectrum(): ', + // err + // ) + // throw err + // } + // } - // 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) + /** + * @api {get} /electrumx/block/headers/{height} Get `count` block headers starting at a height + * @apiName Block header data for a `count` blocks starting at a block height + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Returns an array with block headers starting at the block height + * + * + * @apiExample Example usage: + * curl -X GET "https://api.fullstack.cash/v4/electrumx/block/header/42?count=2" -H "accept: application/json" + * + */ + // GET handler for single block headers + // async getBlockHeaders (req, res, next) { + // try { + // const height = Number(req.params.height) + // const count = req.query.count === undefined ? 1 : Number(req.query.count) + // + // // Reject if height is not a number + // if (Number.isNaN(height) || height < 0) { + // res.status(400) + // return res.json({ + // success: false, + // error: 'height must be a positive number' + // }) + // } + // + // // Reject if height is not a number + // if (Number.isNaN(count) || count < 0) { + // res.status(400) + // return res.json({ + // success: false, + // error: 'count must be a positive number' + // }) + // } + // + // wlogger.debug( + // 'Executing electrumx/getBlockHeaders with this height: ', + // height + // ) + // + // // Get data from ElectrumX server. + // const electrumResponse = await _this._blockHeadersFromElectrum(height, count) + // // console.log(`_transactionDetailsFromElectrum(): ${JSON.stringify(electrumResponse, null, 2)}`) + // + // // Pass the error message if ElectrumX reports an error. + // if (electrumResponse instanceof Error) { + // res.status(400) + // return res.json({ + // success: false, + // error: electrumResponse.message + // }) + // } + // + // res.status(200) + // return res.json({ + // success: true, + // headers: electrumResponse + // }) + // } catch (err) { + // // Write out error to error log. + // wlogger.error('Error in elecrumx.js/getBlockHeader().', err) + // + // return _this.errorHandler(err, res) + // } + // } - return _this.errorHandler(err, res) - } - } + /** + * @api {post} /electrumx/block/headers Get block headers for an array of height + count pairs + * @apiName Block headers for an array of height + count pairs + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Returns an array of objects with blockheaders of an array of TXIDs. + * Limited to 20 items per request. + * + * @apiExample Example usage: + * curl -X POST "https://api.fullstack.cash/v4/electrumx/block/headers" -H "accept: application/json" -H "Content-Type: application/json" -d '{"heights":[{ "height": 42, count: 2 }, { "height": 100, count: 5 }]}' + * + */ + // POST handler for bulk queries on block headers + // async blockHeadersBulk (req, res, next) { + // try { + // const heights = req.body.heights + // + // // Reject if heights is not an array. + // if (!Array.isArray(heights)) { + // res.status(400) + // return res.json({ + // success: false, + // error: 'heights needs to be an array. Use GET for single height.' + // }) + // } + // + // // Enforce array size rate limits + // if (!_this.routeUtils.validateArraySize(req, heights)) { + // res.status(400) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 + // return res.json({ + // success: false, + // error: 'Array too large.' + // }) + // } + // + // wlogger.debug( + // 'Executing electrumx.js/blockHeadersBulk with these txids: ', + // heights + // ) + // + // // Loops through each address and creates an array of Promises, querying + // // the Electrum server in parallel. + // const transactions = heights.map(async (obj) => { + // const headers = await _this._blockHeadersFromElectrum( + // obj.height, + // obj.count + // ) + // + // return { headers } + // }) + // + // // Wait for all parallel Electrum requests to return. + // const result = await Promise.all(transactions) + // + // // Return the array of retrieved transaction details. + // res.status(200) + // return res.json({ + // success: true, + // headers: result + // }) + // } catch (err) { + // wlogger.error('Error in electrumx.js/blockHeadersBulk().', 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. - async _transactionsFromElectrumx (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 transaction history from the ElectrumX server. - const electrumResponse = await _this.electrumx.request( - 'blockchain.scripthash.get_history', - 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/_transactionsFromElectrumx(): ', err) - throw err - } - } + // async _transactionsFromElectrumx (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 transaction history from the ElectrumX server. + // const electrumResponse = await _this.electrumx.request( + // 'blockchain.scripthash.get_history', + // 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/_transactionsFromElectrumx(): ', err) + // throw err + // } + // } /** * @api {get} /electrumx/transactions/{addr} Get transaction history for a single address. @@ -1003,63 +869,63 @@ class Electrum { * */ // GET handler for single balance - async getTransactions (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/getTransactions with this address: ', - cashAddr - ) - - // Get data from ElectrumX server. - const electrumResponse = await _this._transactionsFromElectrumx(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, - transactions: electrumResponse - }) - } catch (err) { - // Write out error to error log. - wlogger.error('Error in elecrumx.js/getTransactions().', err) - - return _this.errorHandler(err, res) - } - } + // async getTransactions (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/getTransactions with this address: ', + // cashAddr + // ) + // + // // Get data from ElectrumX server. + // const electrumResponse = await _this._transactionsFromElectrumx(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, + // transactions: electrumResponse + // }) + // } catch (err) { + // // Write out error to error log. + // wlogger.error('Error in elecrumx.js/getTransactions().', err) + // + // return _this.errorHandler(err, res) + // } + // } /** * @api {post} /electrumx/transactions Get the transaction history for an array of addresses. @@ -1074,115 +940,115 @@ class Electrum { * */ // POST handler for bulk queries on transaction histories for addresses. - async transactionsBulk (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/transactionsBulk 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 transactions = await _this._transactionsFromElectrumx(address) - - return { - transactions, - 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, - transactions: result - }) - } catch (err) { - wlogger.error('Error in electrumx.js/transactionsBulk().', err) - - return _this.errorHandler(err, res) - } - } + // async transactionsBulk (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/transactionsBulk 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 transactions = await _this._transactionsFromElectrumx(address) + // + // return { + // transactions, + // 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, + // transactions: result + // }) + // } catch (err) { + // wlogger.error('Error in electrumx.js/transactionsBulk().', err) + // + // return _this.errorHandler(err, res) + // } + // } // Returns a promise that resolves to unconfirmed UTXO data (mempool) for an address. // Expects input to be a cash address, and input validation to have // already been done by parent, calling function. - async _mempoolFromElectrumx (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 unconfirmed utxos from the ElectrumX server. - const electrumResponse = await _this.electrumx.request( - 'blockchain.scripthash.get_mempool', - scripthash - ) - // console.log( - // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` - // ) - - return electrumResponse - } catch (err) { - // console.log('err: ', err) - - // Write out error to error log. - wlogger.error('Error in elecrumx.js/_mempoolFromElectrumx(): ', err) - throw err - } - } + // async _mempoolFromElectrumx (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 unconfirmed utxos from the ElectrumX server. + // const electrumResponse = await _this.electrumx.request( + // 'blockchain.scripthash.get_mempool', + // scripthash + // ) + // // console.log( + // // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` + // // ) + // + // return electrumResponse + // } catch (err) { + // // console.log('err: ', err) + // + // // Write out error to error log. + // wlogger.error('Error in elecrumx.js/_mempoolFromElectrumx(): ', err) + // throw err + // } + // } /** * @api {get} /electrumx/unconfirmed/{addr} Get unconfirmed utxos for a single address. @@ -1196,63 +1062,63 @@ class Electrum { * */ // GET handler for single balance - async getMempool (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/getMempool with this address: ', - cashAddr - ) - - // Get data from ElectrumX server. - const electrumResponse = await _this._mempoolFromElectrumx(cashAddr) - // console.log(`_mempoolFromElectrumx(): ${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, - utxos: electrumResponse - }) - } catch (err) { - // Write out error to error log. - wlogger.error('Error in elecrumx.js/getMempool().', err) - - return _this.errorHandler(err, res) - } - } + // async getMempool (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/getMempool with this address: ', + // cashAddr + // ) + // + // // Get data from ElectrumX server. + // const electrumResponse = await _this._mempoolFromElectrumx(cashAddr) + // // console.log(`_mempoolFromElectrumx(): ${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, + // utxos: electrumResponse + // }) + // } catch (err) { + // // Write out error to error log. + // wlogger.error('Error in elecrumx.js/getMempool().', err) + // + // return _this.errorHandler(err, res) + // } + // } /** * @api {post} /electrumx/unconfirmed Get unconfirmed utxos for an array of addresses. @@ -1267,106 +1133,106 @@ class Electrum { * */ // POST handler for bulk queries on address details - async mempoolBulk (req, res, next) { - try { - let addresses = req.body.addresses + // async mempoolBulk (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/mempoolBulk 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 + // // Insight API in parallel. + // addresses = addresses.map(async (address, index) => { + // // console.log(`address: ${address}`) + // const utxos = await _this._mempoolFromElectrumx(address) + // + // return { + // utxos, + // 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, + // utxos: result + // }) + // } catch (err) { + // wlogger.error('Error in electrumx.js/mempoolBulk().', err) + // + // return _this.errorHandler(err, res) + // } + // } - // 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/mempoolBulk 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 - // Insight API in parallel. - addresses = addresses.map(async (address, index) => { - // console.log(`address: ${address}`) - const utxos = await _this._mempoolFromElectrumx(address) - - return { - utxos, - 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, - utxos: result - }) - } catch (err) { - wlogger.error('Error in electrumx.js/mempoolBulk().', err) - - return _this.errorHandler(err, res) + // 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) }) } - // 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 - } + // Root API endpoint. Simply acknowledges that it exists. + root (req, res, next) { + return res.json({ status: 'electrumx' }) } } diff --git a/test/v5/a01-electrumx.js b/test/v5/a01-electrumx.js index 65119cf..3fa8e19 100644 --- a/test/v5/a01-electrumx.js +++ b/test/v5/a01-electrumx.js @@ -20,7 +20,7 @@ const assert = chai.assert const sinon = require('sinon') -const ElecrumxRoute = require('../../src/routes/v4/electrumx') +const ElecrumxRoute = require('../../src/routes/v5/electrumx') // Mocking data. const { mockReq, mockRes } = require('./mocks/express-mocks') @@ -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,948 +115,6 @@ 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('#_utxosFromElectrumx', () => { - it('should throw error for invalid address', async () => { - try { - // Address has invalid checksum. - const address = 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' - - // Call the details API. - await electrumxRoute._utxosFromElectrumx(address) - - assert.equal(true, false, 'Unexpected code path') - } catch (err) { - assert.include(err.message, 'Invalid checksum') - } - }) - - it('should return empty array for address with no utxos', async () => { - // Address has invalid checksum. - const address = 'bchtest:qqtmlpspjakqlvywae226esrcdrj9auynuwadh55uf' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox.stub(electrumxRoute.electrumx, 'request').resolves([]) - } - - // Call the details API. - const result = await electrumxRoute._utxosFromElectrumx(address) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.equal(result.length, 0) - }) - - it('should get balance for a single address', async () => { - const address = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute.electrumx, 'request') - .resolves(mockData.utxos) - } - - // Call the details API. - const result = await electrumxRoute._utxosFromElectrumx(address) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.property(result[0], 'height') - assert.property(result[0], 'tx_hash') - assert.property(result[0], 'tx_pos') - assert.property(result[0], 'value') - }) - }) - - describe('#getUtxos', () => { - it('should throw 422 if address is empty', async () => { - const result = await electrumxRoute.getUtxos(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.getUtxos(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.getUtxos(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.getUtxos(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 ElectrumX to user', async () => { - // Address has invalid checksum. - req.params.address = - 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute.electrumx, 'request') - .resolves(mockData.utxos) - } - - // Call the details API. - const result = await electrumxRoute.getUtxos(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:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute, '_utxosFromElectrumx') - .resolves(mockData.utxos) - } - - // Call the details API. - const result = await electrumxRoute.getUtxos(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'utxos') - assert.isArray(result.utxos) - - assert.property(result.utxos[0], 'height') - assert.property(result.utxos[0], 'tx_hash') - assert.property(result.utxos[0], 'tx_pos') - assert.property(result.utxos[0], 'value') - }) - }) - - describe('#utxosBulk', () => { - it('should throw an error for an empty body', async () => { - req.body = {} - - const result = await electrumxRoute.utxosBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'addresses needs to be an array', - 'Proper error message' - ) - }) - - it('should error on non-array single address', async () => { - req.body = { - address: 'qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' - } - - const result = await electrumxRoute.utxosBulk(req, res) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'addresses needs to be an array', - 'Proper error message' - ) - }) - - it('should throw an error for an invalid address', async () => { - req.body = { - addresses: ['02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] - } - - const result = await electrumxRoute.utxosBulk(req, res) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'Invalid BCH address', - 'Proper error message' - ) - }) - - it('should throw 400 error if addresses array is too large', async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push('') - - req.body.addresses = testArray - - const result = await electrumxRoute.utxosBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ['error']) - assert.include(result.error, 'Array too large') - }) - - it('should detect a network mismatch', async () => { - req.body = { - addresses: ['bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'] - } - - const result = await electrumxRoute.utxosBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include(result.error, 'Invalid network', 'Proper error message') - }) - - it('should get details for a single address', async () => { - req.body = { - addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute, '_utxosFromElectrumx') - .resolves(mockData.utxos) - } - - // Call the details API. - const result = await electrumxRoute.utxosBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'utxos') - assert.isArray(result.utxos) - - assert.property(result.utxos[0], 'address') - assert.property(result.utxos[0], 'utxos') - - assert.isArray(result.utxos[0].utxos) - assert.property(result.utxos[0].utxos[0], 'height') - assert.property(result.utxos[0].utxos[0], 'tx_hash') - assert.property(result.utxos[0].utxos[0], 'tx_pos') - assert.property(result.utxos[0].utxos[0], 'value') - }) - - it('should get utxos for multiple addresses', async () => { - req.body = { - addresses: [ - 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', - 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute, '_utxosFromElectrumx') - .resolves(mockData.utxos) - } - - // Call the details API. - const result = await electrumxRoute.utxosBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.isArray(result.utxos) - assert.isArray(result.utxos[0].utxos) - assert.equal(result.utxos.length, 2, '2 outputs for 2 inputs') - }) - }) - - describe('#_transactionDetailsFromElectrum', () => { - it('should return error object for invalid txid', async () => { - const txid = - '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb25' - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - new Error('Invalid tx hash') - ) - - const result = await electrumxRoute._transactionDetailsFromElectrum(txid) - - assert.instanceOf(result, Error) - assert.include(result.message, 'Invalid tx hash') - }) - - it('should get details for a single txid', async () => { - const txid = - '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - mockData.txDetails - ) - - const result = await electrumxRoute._transactionDetailsFromElectrum(txid) - - assert.isObject(result) - assert.property(result, 'blockhash') - assert.property(result, 'hash') - assert.property(result, 'hex') - assert.property(result, 'vin') - assert.property(result, 'vout') - assert.equal(result.hash, txid) - }) - }) - - describe('#getTransactionDetails', () => { - it('should throw 400 if txid is not a string', async () => { - req.params.txid = 5 - - const result = await electrumxRoute.getTransactionDetails(req, res) - - expectRouteError(res, result, 'txid must be a string') - }) - - it('should throw 400 on array input', async () => { - req.params.txid = [ - '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' - ] - - const result = await electrumxRoute.getTransactionDetails(req, res) - - expectRouteError(res, result, 'txid must be a string') - }) - - it('should return error object for invalid txid', async () => { - req.params.txid = - '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb25' - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - new Error('Invalid tx hash') - ) - - // Call the details API. - const result = await electrumxRoute.getTransactionDetails(req, res) - - expectRouteError(res, result, 'Invalid tx hash') - }) - - it('should get details for a single txid', async () => { - req.params.txid = - '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - mockData.txDetails - ) - - // Call the details API. - const result = await electrumxRoute.getTransactionDetails(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'details') - assert.isObject(result.details) - - assert.property(result.details, 'blockhash') - assert.property(result.details, 'hash') - assert.property(result.details, 'hex') - assert.property(result.details, 'vin') - assert.property(result.details, 'vout') - assert.equal(result.details.hash, req.params.txid) - }) - }) - - describe('#transactionDetailsBulk', () => { - it('should throw an error for an empty body', async () => { - req.body = {} - - const result = await electrumxRoute.transactionDetailsBulk(req, res) - - expectRouteError(res, result, 'txids needs to be an array') - }) - - it('should error on non-array single txid', async () => { - req.body = { - txid: '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' - } - - const result = await electrumxRoute.transactionDetailsBulk(req, res) - - expectRouteError(res, result, 'txids needs to be an array') - }) - - it('should NOT throw 400 error for an invalid txid', async () => { - req.body = { - txids: [ - '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb25' - ] - } - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - mockData.txDetails - ) - - const result = await electrumxRoute.transactionDetailsBulk(req, res) - - // This should probably throw a 400 error, but to be consistent with the other - // bulk endpoints it doesn't throw. This will change in the future - // expectRouteError(res, result, 'Invalid tx hash') - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'transactions') - assert.isArray(result.transactions) - }) - - it('should throw 400 error if txid array is too large', async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push('') - - req.body.txids = testArray - - const result = await electrumxRoute.transactionDetailsBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - expectRouteError(res, result, 'Array too large', 400) - }) - - it('should get details for a single txid', async () => { - req.body = { - txids: [ - '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' - ] - } - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - mockData.txDetails - ) - - // Call the details API. - const result = await electrumxRoute.transactionDetailsBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'transactions') - assert.isArray(result.transactions) - - assert.property(result.transactions[0], 'txid') - assert.property(result.transactions[0], 'details') - - assert.property(result.transactions[0].details, 'blockhash') - assert.property(result.transactions[0].details, 'hash') - assert.property(result.transactions[0].details, 'hex') - assert.property(result.transactions[0].details, 'vin') - assert.property(result.transactions[0].details, 'vout') - }) - - it('should get details for multiple txids', async () => { - req.body = { - txids: [ - '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251', - '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' - ] - } - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - mockData.txDetails - ) - - // Call the details API. - const result = await electrumxRoute.transactionDetailsBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`)' - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.isArray(result.transactions) - assert.isObject(result.transactions[0].details) - assert.equal(result.transactions.length, 2, '2 outputs for 2 inputs') - }) - }) - - describe('#_blockHeadersFromElectrum', () => { - it('should return error object for invalid block height', async () => { - const height = -10 - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - new Error('Invalid height') - ) - - const result = await electrumxRoute._blockHeadersFromElectrum(height, 2) - - assert.instanceOf(result, Error) - assert.include(result.message, 'Invalid height') - }) - - it('should return error object for invalid count', async () => { - const height = 42 - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - new Error('Invalid count') - ) - - const result = await electrumxRoute._blockHeadersFromElectrum(height, -1) - - assert.instanceOf(result, Error) - assert.include(result.message, 'Invalid count') - }) - - it('should get block header for a single block height', async () => { - const height = 42 - - const mockedResponse = { count: 2, hex: mockData.blockHeaders.join(''), max: 2016 } - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - mockedResponse - ) - - const result = await electrumxRoute._blockHeadersFromElectrum(height, 2) - - assert.isArray(result) - assert.deepEqual(result, mockData.blockHeaders) - }) - }) - - describe('#getBlockheaders', () => { - it('should throw 400 if height is not a number', async () => { - req.params.height = 'Hello' - - const result = await electrumxRoute.getBlockHeaders(req, res) - - expectRouteError(res, result, 'height must be a positive number') - }) - - it('should throw 400 if height is negative', async () => { - req.params.height = -42 - - const result = await electrumxRoute.getBlockHeaders(req, res) - - expectRouteError(res, result, 'height must be a positive number') - }) - - it('should throw 400 if count is not a number', async () => { - req.params.height = 42 - req.query.count = 'Hello' - - const result = await electrumxRoute.getBlockHeaders(req, res) - - expectRouteError(res, result, 'count must be a positive number') - }) - - it('should throw 400 if count is negative', async () => { - req.params.height = 42 - req.query.count = -10 - - const result = await electrumxRoute.getBlockHeaders(req, res) - - expectRouteError(res, result, 'count must be a positive number') - }) - - it('should throw 400 on array input', async () => { - req.params.height = [42, 42] - - const result = await electrumxRoute.getBlockHeaders(req, res) - - expectRouteError(res, result, 'height must be a positive number') - }) - - it('should return error object for invalid height', async () => { - req.params.height = 1000000000 - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - new Error('Invalid height') - ) - - // Call the details API. - const result = await electrumxRoute.getBlockHeaders(req, res) - - expectRouteError(res, result, 'Invalid height') - }) - - it('should get headers for a single block height with count 2', async () => { - req.params.height = 42 - req.query.count = 2 - - stubMethodForUnitTests( - electrumxRoute, - '_blockHeadersFromElectrum', - mockData.blockHeaders - ) - - // Call the details API. - const result = await electrumxRoute.getBlockHeaders(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'headers') - assert.isArray(result.headers) - assert.deepEqual(result.headers, mockData.blockHeaders) - }) - }) - - describe('#blockHeadersBulk', () => { - it('should throw an error for an empty body', async () => { - req.body = {} - - const result = await electrumxRoute.blockHeadersBulk(req, res) - - expectRouteError(res, result, 'heights needs to be an array') - }) - - it('should error on non-array single height', async () => { - req.body = { - heights: 42 - } - - const result = await electrumxRoute.blockHeadersBulk(req, res) - - expectRouteError(res, result, 'heights needs to be an array') - }) - - it('should NOT throw 400 error for an invalid height', async () => { - req.body = { - heights: [ - { height: -10, count: 2 } - ] - } - - stubMethodForUnitTests( - electrumxRoute, - '_blockHeadersFromElectrum', - mockData.blockHeaders - ) - - const result = await electrumxRoute.blockHeadersBulk(req, res) - - // This should probably throw a 400 error, but to be consistent with the other - // bulk endpoints it doesn't throw. This will change in the future - // expectRouteError(res, result, 'Invalid tx hash') - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'headers') - assert.isArray(result.headers) - }) - - it('should throw 400 error if heights array is too large', async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push('') - - req.body.heights = testArray - - const result = await electrumxRoute.blockHeadersBulk(req, res) - - expectRouteError(res, result, 'Array too large', 400) - }) - - it('should get details for a single height', async () => { - req.body = { - heights: [ - { height: 42, count: 2 } - ] - } - - stubMethodForUnitTests( - electrumxRoute, - '_blockHeadersFromElectrum', - mockData.blockHeaders - ) - - // Call the details API. - const result = await electrumxRoute.blockHeadersBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'headers') - assert.isArray(result.headers) - - assert.property(result.headers[0], 'headers') - assert.isArray(result.headers[0].headers) - }) - - it('should get details for multiple txids', async () => { - req.body = { - heights: [ - { height: 42, count: 2 }, - { height: 42, count: 2 } - ] - } - - stubMethodForUnitTests( - electrumxRoute, - '_blockHeadersFromElectrum', - mockData.blockHeaders - ) - - // Call the details API. - const result = await electrumxRoute.blockHeadersBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`)' - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.isArray(result.headers) - assert.isArray(result.headers[0].headers) - assert.equal(result.headers.length, 2, '2 outputs for 2 inputs') - }) - }) - - describe('#_broadcastTransactionWithElectrum', () => { - it('should return error object for invalid formatted transaction', async () => { - const invalidHex = mockData.txDetails.hex.substring(10) - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - new Error('Error: the transaction was rejected by network rules.\n\nTX decode failed\n') - ) - - const result = await electrumxRoute._broadcastTransactionWithElectrum(invalidHex) - - assert.instanceOf(result, Error) - assert.include(result.message, 'TX decode failed') - }) - - it('should return txid for valid transaction', async function () { - // We cannot send an actual broadcast transaction to mainnet - if (process.env.TEST !== 'unit') return this.skip() - - const validHex = mockData.txDetails.hex - - stubMethodForUnitTests( - electrumxRoute.electrumx, - 'request', - mockData.txDetails.hash - ) - - const result = await electrumxRoute._broadcastTransactionWithElectrum(validHex) - - assert.typeOf(result, 'string') - assert.equal(result, mockData.txDetails.hash) - }) - }) - - describe('#broadcastTransaction', () => { - it('should throw an error for a non-string', async () => { - req.body = 456 - - stubMethodForUnitTests( - electrumxRoute, - '_broadcastTransactionWithElectrum', - new Error('request body must be a string.') - ) - - const result = await electrumxRoute.broadcastTransaction(req, res) - - expectRouteError(res, result, 'request body must be a string.') - }) - - it('should throw an error object for invalid formatted transaction', async () => { - req.body.txHex = mockData.txDetails.hex.substring(10) - - stubMethodForUnitTests( - electrumxRoute, - '_broadcastTransactionWithElectrum', - new Error('Error: the transaction was rejected by network rules.\n\nTX decode failed\n') - ) - - const result = await electrumxRoute.broadcastTransaction(req, res) - - expectRouteError(res, result, 'TX decode failed') - }) - - it('should return txid for valid transaction', async function () { - // We cannot send an actual broadcast transaction to mainnet - if (process.env.TEST !== 'unit') return this.skip() - - req.body.txHex = mockData.txDetails.hex - - stubMethodForUnitTests( - electrumxRoute, - '_broadcastTransactionWithElectrum', - mockData.txDetails.hash - ) - - const result = await electrumxRoute.broadcastTransaction(req, res) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'txid') - assert.equal(result.txid, mockData.txDetails.hash) - }) - }) - - describe('#_balanceFromElectrumx', () => { - it('should throw error for invalid address', async () => { - try { - // Address has invalid checksum. - const address = 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' - - // Mock unit tests to prevent live network calls. - // if (process.env.TEST === 'unit') { - // electrumxRoute.isReady = true // Force flag. - // - // sandbox - // .stub(electrumxRoute.electrumx, 'request') - // .throws('Invalid Argument: Invalid checksum:') - // } - - // Call the details API. - await electrumxRoute._balanceFromElectrumx(address) - - assert.equal(true, false, 'Unexpected code path') - } catch (err) { - // console.log('err2: ', err) - assert.include(err.message, 'Invalid checksum') - } - }) - - it('should get balance for a single address', async () => { - const address = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute.electrumx, 'request') - .resolves(mockData.balance) - } - - // Call the details API. - const result = await electrumxRoute._balanceFromElectrumx(address) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'confirmed') - assert.property(result, 'unconfirmed') - }) - - it('should get balance for an address with no transaction history', async () => { - const address = 'bitcoincash:qp2ew6pvrs22jtsvtjyumjgas6jkvgn2hy3ad4wpw8' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute.electrumx, 'request') - .resolves(mockData.balance) - } - - // Call the details API. - const result = await electrumxRoute._balanceFromElectrumx(address) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'confirmed') - assert.property(result, 'unconfirmed') - }) - }) - describe('#getBalance', () => { it('should throw 400 if address is empty', async () => { const result = await electrumxRoute.getBalance(req, res) @@ -1142,8 +199,8 @@ describe('#Electrumx', () => { electrumxRoute.isReady = true // Force flag. sandbox - .stub(electrumxRoute, '_balanceFromElectrumx') - .resolves(mockData.balance) + .stub(electrumxRoute.axios, 'get') + .resolves({ data: mockData.balance }) } // Call the details API. @@ -1159,736 +216,1654 @@ describe('#Electrumx', () => { }) }) - describe('#balanceBulk', () => { - it('should throw an error for an empty body', async () => { - req.body = {} - - const result = await electrumxRoute.balanceBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'addresses needs to be an array', - 'Proper error message' - ) - }) - - it('should error on non-array single address', async () => { - req.body = { - address: 'qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' - } - - const result = await electrumxRoute.balanceBulk(req, res) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'addresses needs to be an array', - 'Proper error message' - ) - }) - - it('should throw an error for an invalid address', async () => { - req.body = { - addresses: ['02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] - } - - const result = await electrumxRoute.balanceBulk(req, res) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'Invalid BCH address', - 'Proper error message' - ) - }) - - it('should throw 400 error if addresses array is too large', async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push('') - - req.body.addresses = testArray - - const result = await electrumxRoute.balanceBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ['error']) - assert.include(result.error, 'Array too large') - }) - - it('should detect a network mismatch', async () => { - req.body = { - addresses: ['bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'] - } - - const result = await electrumxRoute.balanceBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include(result.error, 'Invalid network', 'Proper error message') - }) - - it('should get details for a single address', async () => { - req.body = { - addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'] - } - - // Mock the Insight URL for unit tests. - 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.balanceBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'balances') - assert.isArray(result.balances) - - assert.property(result.balances[0], 'address') - assert.property(result.balances[0], 'balance') - - assert.property(result.balances[0].balance, 'confirmed') - assert.property(result.balances[0].balance, 'unconfirmed') - }) - - it('should get utxos for multiple addresses', async () => { - req.body = { - addresses: [ - 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', - 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' - ] - } - - // Mock the Insight URL for unit tests. - 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.balanceBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.isArray(result.balances) - assert.equal(result.balances.length, 2, '2 outputs for 2 inputs') - }) - }) - - describe('#_transactionsFromElectrumx', () => { - it('should throw error for invalid address', async () => { - try { - // Address has invalid checksum. - const address = 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' - - // Call the details API. - await electrumxRoute._transactionsFromElectrumx(address) - - assert.equal(true, false, 'Unexpected code path') - } catch (err) { - // console.log('err2: ', err) - assert.include(err.message, 'Invalid checksum') - } - }) - - it('should get transaction history for a single address', async () => { - const address = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute.electrumx, 'request') - .resolves(mockData.txHistory) - } - - // Call the details API. - const result = await electrumxRoute._transactionsFromElectrumx(address) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.property(result[0], 'height') - assert.property(result[0], 'tx_hash') - }) - - it('should get history for an address with no transaction history', async () => { - const address = 'bitcoincash:qp2ew6pvrs22jtsvtjyumjgas6jkvgn2hy3ad4wpw8' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox.stub(electrumxRoute.electrumx, 'request').resolves([]) - } - - // Call the details API. - const result = await electrumxRoute._transactionsFromElectrumx(address) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.equal(result.length, 0) - }) - }) - - describe('#getTransactions', () => { - it('should throw 400 if address is empty', async () => { - const result = await electrumxRoute.getTransactions(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.getTransactions(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.getTransactions(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.getTransactions(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 ElectrumX to user', async () => { - // Address has invalid checksum. - req.params.address = - 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' - - // Call the details API. - const result = await electrumxRoute.getTransactions(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 transactions 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, '_transactionsFromElectrumx') - .resolves(mockData.txHistory) - } - - // Call the details API. - const result = await electrumxRoute.getTransactions(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'transactions') - assert.isArray(result.transactions) - assert.property(result.transactions[0], 'height') - assert.property(result.transactions[0], 'tx_hash') - }) - }) - - describe('#balanceBulk', () => { - it('should throw an error for an empty body', async () => { - req.body = {} - - const result = await electrumxRoute.transactionsBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'addresses needs to be an array', - 'Proper error message' - ) - }) - - it('should error on non-array single address', async () => { - req.body = { - address: 'qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' - } - - const result = await electrumxRoute.transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'addresses needs to be an array', - 'Proper error message' - ) - }) - - it('should throw an error for an invalid address', async () => { - req.body = { - addresses: ['02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] - } - - const result = await electrumxRoute.transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'Invalid BCH address', - 'Proper error message' - ) - }) - - it('should throw 400 error if addresses array is too large', async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push('') - - req.body.addresses = testArray - - const result = await electrumxRoute.transactionsBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ['error']) - assert.include(result.error, 'Array too large') - }) - - it('should detect a network mismatch', async () => { - req.body = { - addresses: ['bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'] - } - - const result = await electrumxRoute.transactionsBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include(result.error, 'Invalid network', 'Proper error message') - }) - - it('should get details for a single address', async () => { - req.body = { - addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute, '_transactionsFromElectrumx') - .resolves(mockData.txHistory) - } - - // Call the details API. - const result = await electrumxRoute.transactionsBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'transactions') - assert.isArray(result.transactions) - - assert.property(result.transactions[0], 'address') - assert.property(result.transactions[0], 'transactions') - - assert.isArray(result.transactions[0].transactions) - assert.property(result.transactions[0].transactions[0], 'height') - assert.property(result.transactions[0].transactions[0], 'tx_hash') - }) - - it('should get utxos for multiple addresses', async () => { - req.body = { - addresses: [ - 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', - 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute, '_transactionsFromElectrumx') - .resolves(mockData.txHistory) - } - - // Call the details API. - const result = await electrumxRoute.transactionsBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.isArray(result.transactions) - assert.equal(result.transactions.length, 2, '2 outputs for 2 inputs') - }) - }) - - describe('#_mempoolFromElectrumx', () => { - it('should throw error for invalid address', async () => { - try { - // Address has invalid checksum. - const address = 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' - - // Call the details API. - await electrumxRoute._mempoolFromElectrumx(address) - - assert.equal(true, false, 'Unexpected code path') - } catch (err) { - assert.include(err.message, 'Invalid checksum') - } - }) - - it('should return empty array for address with no unconfirmed utxos', async () => { - // Address has invalid checksum. - const address = 'bchtest:qqtmlpspjakqlvywae226esrcdrj9auynuwadh55uf' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox.stub(electrumxRoute.electrumx, 'request').resolves([]) - } - - // Call the details API. - const result = await electrumxRoute._mempoolFromElectrumx(address) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.equal(result.length, 0) - }) - - it('should get mempool for a single address', async () => { - const address = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute.electrumx, 'request') - .resolves(mockData.mempool) - } else { - // Skip this test for integrations. Unconfirmed UTXOs are transient and - // not easy to test in real-time. - assert.equal(true, true) - return - } - - // Call the details API. - const result = await electrumxRoute._mempoolFromElectrumx(address) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.property(result[0], 'height') - assert.property(result[0], 'tx_hash') - assert.property(result[0], 'fee') - }) - }) - - describe('#getMempool', () => { - it('should throw 400 if address is empty', async () => { - const result = await electrumxRoute.getMempool(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.getMempool(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.getMempool(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.getMempool(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.getMempool(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 mempool for a single address', async () => { - req.params.address = - 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' - - // Mock unit tests to prevent live network calls. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute, '_mempoolFromElectrumx') - .resolves(mockData.mempool) - } else { - // Skip this test for integrations. Unconfirmed UTXOs are transient and - // not easy to test in real-time. - assert.equal(true, true) - return - } - - // Call the details API. - const result = await electrumxRoute.getMempool(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'utxos') - assert.isArray(result.utxos) - - assert.property(result.utxos[0], 'height') - assert.property(result.utxos[0], 'tx_hash') - assert.property(result.utxos[0], 'fee') - }) - }) - - describe('#mempoolBulk', () => { - it('should throw an error for an empty body', async () => { - req.body = {} - - const result = await electrumxRoute.mempoolBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'addresses needs to be an array', - 'Proper error message' - ) - }) - - it('should error on non-array single address', async () => { - req.body = { - address: 'qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' - } - - const result = await electrumxRoute.mempoolBulk(req, res) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'addresses needs to be an array', - 'Proper error message' - ) - }) - - it('should throw 400 error if addresses array is too large', async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push('') - - req.body.addresses = testArray - - const result = await electrumxRoute.mempoolBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ['error']) - assert.include(result.error, 'Array too large') - }) - - it('should throw an error for an invalid address', async () => { - req.body = { - addresses: ['02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] - } - - const result = await electrumxRoute.mempoolBulk(req, res) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include( - result.error, - 'Invalid BCH address', - 'Proper error message' - ) - }) - - it('should detect a network mismatch', async () => { - req.body = { - addresses: ['bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'] - } - - const result = await electrumxRoute.mempoolBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') - assert.include(result.error, 'Invalid network', 'Proper error message') - }) - - it('should get mempool details for a single address', async () => { - req.body = { - addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute, '_mempoolFromElectrumx') - .resolves(mockData.mempool) - } else { - // Skip this test for integrations. Unconfirmed UTXOs are transient and - // not easy to test in real-time. - assert.equal(true, true) - return - } - - // Call the details API. - const result = await electrumxRoute.mempoolBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.property(result, 'utxos') - assert.isArray(result.utxos) - - assert.property(result.utxos[0], 'address') - assert.property(result.utxos[0], 'utxos') - - assert.isArray(result.utxos[0].utxos) - assert.property(result.utxos[0].utxos[0], 'height') - assert.property(result.utxos[0].utxos[0], 'tx_hash') - assert.property(result.utxos[0].utxos[0], 'fee') - }) - - it('should get mempool for multiple addresses', async () => { - req.body = { - addresses: [ - 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', - 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === 'unit') { - electrumxRoute.isReady = true // Force flag. - - sandbox - .stub(electrumxRoute, '_mempoolFromElectrumx') - .resolves(mockData.mempool) - } else { - // Skip this test for integrations. Unconfirmed UTXOs are transient and - // not easy to test in real-time. - assert.equal(true, true) - return - } - - // Call the details API. - const result = await electrumxRoute.mempoolBulk(req, res) - // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.property(result, 'success') - assert.equal(result.success, true) - - assert.isArray(result.utxos) - assert.isArray(result.utxos[0].utxos) - assert.equal(result.utxos.length, 2, '2 outputs for 2 inputs') - }) - }) + // describe('#_utxosFromElectrumx', () => { + // it('should throw error for invalid address', async () => { + // try { + // // Address has invalid checksum. + // const address = 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' + // + // // Call the details API. + // await electrumxRoute._utxosFromElectrumx(address) + // + // assert.equal(true, false, 'Unexpected code path') + // } catch (err) { + // assert.include(err.message, 'Invalid checksum') + // } + // }) + // + // it('should return empty array for address with no utxos', async () => { + // // Address has invalid checksum. + // const address = 'bchtest:qqtmlpspjakqlvywae226esrcdrj9auynuwadh55uf' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox.stub(electrumxRoute.electrumx, 'request').resolves([]) + // } + // + // // Call the details API. + // const result = await electrumxRoute._utxosFromElectrumx(address) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.isArray(result) + // assert.equal(result.length, 0) + // }) + // + // it('should get balance for a single address', async () => { + // const address = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute.electrumx, 'request') + // .resolves(mockData.utxos) + // } + // + // // Call the details API. + // const result = await electrumxRoute._utxosFromElectrumx(address) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.isArray(result) + // assert.property(result[0], 'height') + // assert.property(result[0], 'tx_hash') + // assert.property(result[0], 'tx_pos') + // assert.property(result[0], 'value') + // }) + // }) + + // describe('#getUtxos', () => { + // it('should throw 422 if address is empty', async () => { + // const result = await electrumxRoute.getUtxos(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.getUtxos(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.getUtxos(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.getUtxos(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 ElectrumX to user', async () => { + // // Address has invalid checksum. + // req.params.address = + // 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute.electrumx, 'request') + // .resolves(mockData.utxos) + // } + // + // // Call the details API. + // const result = await electrumxRoute.getUtxos(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:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute, '_utxosFromElectrumx') + // .resolves(mockData.utxos) + // } + // + // // Call the details API. + // const result = await electrumxRoute.getUtxos(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'utxos') + // assert.isArray(result.utxos) + // + // assert.property(result.utxos[0], 'height') + // assert.property(result.utxos[0], 'tx_hash') + // assert.property(result.utxos[0], 'tx_pos') + // assert.property(result.utxos[0], 'value') + // }) + // }) + + // describe('#utxosBulk', () => { + // it('should throw an error for an empty body', async () => { + // req.body = {} + // + // const result = await electrumxRoute.utxosBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'addresses needs to be an array', + // 'Proper error message' + // ) + // }) + // + // it('should error on non-array single address', async () => { + // req.body = { + // address: 'qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' + // } + // + // const result = await electrumxRoute.utxosBulk(req, res) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'addresses needs to be an array', + // 'Proper error message' + // ) + // }) + // + // it('should throw an error for an invalid address', async () => { + // req.body = { + // addresses: ['02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] + // } + // + // const result = await electrumxRoute.utxosBulk(req, res) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'Invalid BCH address', + // 'Proper error message' + // ) + // }) + // + // it('should throw 400 error if addresses array is too large', async () => { + // const testArray = [] + // for (var i = 0; i < 25; i++) testArray.push('') + // + // req.body.addresses = testArray + // + // const result = await electrumxRoute.utxosBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.hasAllKeys(result, ['error']) + // assert.include(result.error, 'Array too large') + // }) + // + // it('should detect a network mismatch', async () => { + // req.body = { + // addresses: ['bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'] + // } + // + // const result = await electrumxRoute.utxosBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include(result.error, 'Invalid network', 'Proper error message') + // }) + // + // it('should get details for a single address', async () => { + // req.body = { + // addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'] + // } + // + // // Mock the Insight URL for unit tests. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute, '_utxosFromElectrumx') + // .resolves(mockData.utxos) + // } + // + // // Call the details API. + // const result = await electrumxRoute.utxosBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'utxos') + // assert.isArray(result.utxos) + // + // assert.property(result.utxos[0], 'address') + // assert.property(result.utxos[0], 'utxos') + // + // assert.isArray(result.utxos[0].utxos) + // assert.property(result.utxos[0].utxos[0], 'height') + // assert.property(result.utxos[0].utxos[0], 'tx_hash') + // assert.property(result.utxos[0].utxos[0], 'tx_pos') + // assert.property(result.utxos[0].utxos[0], 'value') + // }) + // + // it('should get utxos for multiple addresses', async () => { + // req.body = { + // addresses: [ + // 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', + // 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' + // ] + // } + // + // // Mock the Insight URL for unit tests. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute, '_utxosFromElectrumx') + // .resolves(mockData.utxos) + // } + // + // // Call the details API. + // const result = await electrumxRoute.utxosBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.isArray(result.utxos) + // assert.isArray(result.utxos[0].utxos) + // assert.equal(result.utxos.length, 2, '2 outputs for 2 inputs') + // }) + // }) + + // describe('#_transactionDetailsFromElectrum', () => { + // it('should return error object for invalid txid', async () => { + // const txid = + // '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb25' + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // new Error('Invalid tx hash') + // ) + // + // const result = await electrumxRoute._transactionDetailsFromElectrum(txid) + // + // assert.instanceOf(result, Error) + // assert.include(result.message, 'Invalid tx hash') + // }) + // + // it('should get details for a single txid', async () => { + // const txid = + // '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // mockData.txDetails + // ) + // + // const result = await electrumxRoute._transactionDetailsFromElectrum(txid) + // + // assert.isObject(result) + // assert.property(result, 'blockhash') + // assert.property(result, 'hash') + // assert.property(result, 'hex') + // assert.property(result, 'vin') + // assert.property(result, 'vout') + // assert.equal(result.hash, txid) + // }) + // }) + + // describe('#getTransactionDetails', () => { + // it('should throw 400 if txid is not a string', async () => { + // req.params.txid = 5 + // + // const result = await electrumxRoute.getTransactionDetails(req, res) + // + // expectRouteError(res, result, 'txid must be a string') + // }) + // + // it('should throw 400 on array input', async () => { + // req.params.txid = [ + // '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' + // ] + // + // const result = await electrumxRoute.getTransactionDetails(req, res) + // + // expectRouteError(res, result, 'txid must be a string') + // }) + // + // it('should return error object for invalid txid', async () => { + // req.params.txid = + // '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb25' + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // new Error('Invalid tx hash') + // ) + // + // // Call the details API. + // const result = await electrumxRoute.getTransactionDetails(req, res) + // + // expectRouteError(res, result, 'Invalid tx hash') + // }) + // + // it('should get details for a single txid', async () => { + // req.params.txid = + // '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // mockData.txDetails + // ) + // + // // Call the details API. + // const result = await electrumxRoute.getTransactionDetails(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'details') + // assert.isObject(result.details) + // + // assert.property(result.details, 'blockhash') + // assert.property(result.details, 'hash') + // assert.property(result.details, 'hex') + // assert.property(result.details, 'vin') + // assert.property(result.details, 'vout') + // assert.equal(result.details.hash, req.params.txid) + // }) + // }) + + // describe('#transactionDetailsBulk', () => { + // it('should throw an error for an empty body', async () => { + // req.body = {} + // + // const result = await electrumxRoute.transactionDetailsBulk(req, res) + // + // expectRouteError(res, result, 'txids needs to be an array') + // }) + // + // it('should error on non-array single txid', async () => { + // req.body = { + // txid: '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' + // } + // + // const result = await electrumxRoute.transactionDetailsBulk(req, res) + // + // expectRouteError(res, result, 'txids needs to be an array') + // }) + // + // it('should NOT throw 400 error for an invalid txid', async () => { + // req.body = { + // txids: [ + // '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb25' + // ] + // } + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // mockData.txDetails + // ) + // + // const result = await electrumxRoute.transactionDetailsBulk(req, res) + // + // // This should probably throw a 400 error, but to be consistent with the other + // // bulk endpoints it doesn't throw. This will change in the future + // // expectRouteError(res, result, 'Invalid tx hash') + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'transactions') + // assert.isArray(result.transactions) + // }) + // + // it('should throw 400 error if txid array is too large', async () => { + // const testArray = [] + // for (var i = 0; i < 25; i++) testArray.push('') + // + // req.body.txids = testArray + // + // const result = await electrumxRoute.transactionDetailsBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // expectRouteError(res, result, 'Array too large', 400) + // }) + // + // it('should get details for a single txid', async () => { + // req.body = { + // txids: [ + // '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' + // ] + // } + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // mockData.txDetails + // ) + // + // // Call the details API. + // const result = await electrumxRoute.transactionDetailsBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'transactions') + // assert.isArray(result.transactions) + // + // assert.property(result.transactions[0], 'txid') + // assert.property(result.transactions[0], 'details') + // + // assert.property(result.transactions[0].details, 'blockhash') + // assert.property(result.transactions[0].details, 'hash') + // assert.property(result.transactions[0].details, 'hex') + // assert.property(result.transactions[0].details, 'vin') + // assert.property(result.transactions[0].details, 'vout') + // }) + // + // it('should get details for multiple txids', async () => { + // req.body = { + // txids: [ + // '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251', + // '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251' + // ] + // } + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // mockData.txDetails + // ) + // + // // Call the details API. + // const result = await electrumxRoute.transactionDetailsBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`)' + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.isArray(result.transactions) + // assert.isObject(result.transactions[0].details) + // assert.equal(result.transactions.length, 2, '2 outputs for 2 inputs') + // }) + // }) + + // describe('#_blockHeadersFromElectrum', () => { + // it('should return error object for invalid block height', async () => { + // const height = -10 + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // new Error('Invalid height') + // ) + // + // const result = await electrumxRoute._blockHeadersFromElectrum(height, 2) + // + // assert.instanceOf(result, Error) + // assert.include(result.message, 'Invalid height') + // }) + // + // it('should return error object for invalid count', async () => { + // const height = 42 + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // new Error('Invalid count') + // ) + // + // const result = await electrumxRoute._blockHeadersFromElectrum(height, -1) + // + // assert.instanceOf(result, Error) + // assert.include(result.message, 'Invalid count') + // }) + // + // it('should get block header for a single block height', async () => { + // const height = 42 + // + // const mockedResponse = { count: 2, hex: mockData.blockHeaders.join(''), max: 2016 } + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // mockedResponse + // ) + // + // const result = await electrumxRoute._blockHeadersFromElectrum(height, 2) + // + // assert.isArray(result) + // assert.deepEqual(result, mockData.blockHeaders) + // }) + // }) + + // describe('#getBlockheaders', () => { + // it('should throw 400 if height is not a number', async () => { + // req.params.height = 'Hello' + // + // const result = await electrumxRoute.getBlockHeaders(req, res) + // + // expectRouteError(res, result, 'height must be a positive number') + // }) + // + // it('should throw 400 if height is negative', async () => { + // req.params.height = -42 + // + // const result = await electrumxRoute.getBlockHeaders(req, res) + // + // expectRouteError(res, result, 'height must be a positive number') + // }) + // + // it('should throw 400 if count is not a number', async () => { + // req.params.height = 42 + // req.query.count = 'Hello' + // + // const result = await electrumxRoute.getBlockHeaders(req, res) + // + // expectRouteError(res, result, 'count must be a positive number') + // }) + // + // it('should throw 400 if count is negative', async () => { + // req.params.height = 42 + // req.query.count = -10 + // + // const result = await electrumxRoute.getBlockHeaders(req, res) + // + // expectRouteError(res, result, 'count must be a positive number') + // }) + // + // it('should throw 400 on array input', async () => { + // req.params.height = [42, 42] + // + // const result = await electrumxRoute.getBlockHeaders(req, res) + // + // expectRouteError(res, result, 'height must be a positive number') + // }) + // + // it('should return error object for invalid height', async () => { + // req.params.height = 1000000000 + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // new Error('Invalid height') + // ) + // + // // Call the details API. + // const result = await electrumxRoute.getBlockHeaders(req, res) + // + // expectRouteError(res, result, 'Invalid height') + // }) + // + // it('should get headers for a single block height with count 2', async () => { + // req.params.height = 42 + // req.query.count = 2 + // + // stubMethodForUnitTests( + // electrumxRoute, + // '_blockHeadersFromElectrum', + // mockData.blockHeaders + // ) + // + // // Call the details API. + // const result = await electrumxRoute.getBlockHeaders(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'headers') + // assert.isArray(result.headers) + // assert.deepEqual(result.headers, mockData.blockHeaders) + // }) + // }) + + // describe('#blockHeadersBulk', () => { + // it('should throw an error for an empty body', async () => { + // req.body = {} + // + // const result = await electrumxRoute.blockHeadersBulk(req, res) + // + // expectRouteError(res, result, 'heights needs to be an array') + // }) + // + // it('should error on non-array single height', async () => { + // req.body = { + // heights: 42 + // } + // + // const result = await electrumxRoute.blockHeadersBulk(req, res) + // + // expectRouteError(res, result, 'heights needs to be an array') + // }) + // + // it('should NOT throw 400 error for an invalid height', async () => { + // req.body = { + // heights: [ + // { height: -10, count: 2 } + // ] + // } + // + // stubMethodForUnitTests( + // electrumxRoute, + // '_blockHeadersFromElectrum', + // mockData.blockHeaders + // ) + // + // const result = await electrumxRoute.blockHeadersBulk(req, res) + // + // // This should probably throw a 400 error, but to be consistent with the other + // // bulk endpoints it doesn't throw. This will change in the future + // // expectRouteError(res, result, 'Invalid tx hash') + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'headers') + // assert.isArray(result.headers) + // }) + // + // it('should throw 400 error if heights array is too large', async () => { + // const testArray = [] + // for (var i = 0; i < 25; i++) testArray.push('') + // + // req.body.heights = testArray + // + // const result = await electrumxRoute.blockHeadersBulk(req, res) + // + // expectRouteError(res, result, 'Array too large', 400) + // }) + // + // it('should get details for a single height', async () => { + // req.body = { + // heights: [ + // { height: 42, count: 2 } + // ] + // } + // + // stubMethodForUnitTests( + // electrumxRoute, + // '_blockHeadersFromElectrum', + // mockData.blockHeaders + // ) + // + // // Call the details API. + // const result = await electrumxRoute.blockHeadersBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'headers') + // assert.isArray(result.headers) + // + // assert.property(result.headers[0], 'headers') + // assert.isArray(result.headers[0].headers) + // }) + // + // it('should get details for multiple txids', async () => { + // req.body = { + // heights: [ + // { height: 42, count: 2 }, + // { height: 42, count: 2 } + // ] + // } + // + // stubMethodForUnitTests( + // electrumxRoute, + // '_blockHeadersFromElectrum', + // mockData.blockHeaders + // ) + // + // // Call the details API. + // const result = await electrumxRoute.blockHeadersBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`)' + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.isArray(result.headers) + // assert.isArray(result.headers[0].headers) + // assert.equal(result.headers.length, 2, '2 outputs for 2 inputs') + // }) + // }) + + // describe('#_broadcastTransactionWithElectrum', () => { + // it('should return error object for invalid formatted transaction', async () => { + // const invalidHex = mockData.txDetails.hex.substring(10) + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // new Error('Error: the transaction was rejected by network rules.\n\nTX decode failed\n') + // ) + // + // const result = await electrumxRoute._broadcastTransactionWithElectrum(invalidHex) + // + // assert.instanceOf(result, Error) + // assert.include(result.message, 'TX decode failed') + // }) + // + // it('should return txid for valid transaction', async function () { + // // We cannot send an actual broadcast transaction to mainnet + // if (process.env.TEST !== 'unit') return this.skip() + // + // const validHex = mockData.txDetails.hex + // + // stubMethodForUnitTests( + // electrumxRoute.electrumx, + // 'request', + // mockData.txDetails.hash + // ) + // + // const result = await electrumxRoute._broadcastTransactionWithElectrum(validHex) + // + // assert.typeOf(result, 'string') + // assert.equal(result, mockData.txDetails.hash) + // }) + // }) + + // describe('#broadcastTransaction', () => { + // it('should throw an error for a non-string', async () => { + // req.body = 456 + // + // stubMethodForUnitTests( + // electrumxRoute, + // '_broadcastTransactionWithElectrum', + // new Error('request body must be a string.') + // ) + // + // const result = await electrumxRoute.broadcastTransaction(req, res) + // + // expectRouteError(res, result, 'request body must be a string.') + // }) + // + // it('should throw an error object for invalid formatted transaction', async () => { + // req.body.txHex = mockData.txDetails.hex.substring(10) + // + // stubMethodForUnitTests( + // electrumxRoute, + // '_broadcastTransactionWithElectrum', + // new Error('Error: the transaction was rejected by network rules.\n\nTX decode failed\n') + // ) + // + // const result = await electrumxRoute.broadcastTransaction(req, res) + // + // expectRouteError(res, result, 'TX decode failed') + // }) + // + // it('should return txid for valid transaction', async function () { + // // We cannot send an actual broadcast transaction to mainnet + // if (process.env.TEST !== 'unit') return this.skip() + // + // req.body.txHex = mockData.txDetails.hex + // + // stubMethodForUnitTests( + // electrumxRoute, + // '_broadcastTransactionWithElectrum', + // mockData.txDetails.hash + // ) + // + // const result = await electrumxRoute.broadcastTransaction(req, res) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'txid') + // assert.equal(result.txid, mockData.txDetails.hash) + // }) + // }) + + // describe('#_balanceFromElectrumx', () => { + // it('should throw error for invalid address', async () => { + // try { + // // Address has invalid checksum. + // const address = 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' + // + // // Mock unit tests to prevent live network calls. + // // if (process.env.TEST === 'unit') { + // // electrumxRoute.isReady = true // Force flag. + // // + // // sandbox + // // .stub(electrumxRoute.electrumx, 'request') + // // .throws('Invalid Argument: Invalid checksum:') + // // } + // + // // Call the details API. + // await electrumxRoute._balanceFromElectrumx(address) + // + // assert.equal(true, false, 'Unexpected code path') + // } catch (err) { + // // console.log('err2: ', err) + // assert.include(err.message, 'Invalid checksum') + // } + // }) + // + // it('should get balance for a single address', async () => { + // const address = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute.electrumx, 'request') + // .resolves(mockData.balance) + // } + // + // // Call the details API. + // const result = await electrumxRoute._balanceFromElectrumx(address) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'confirmed') + // assert.property(result, 'unconfirmed') + // }) + // + // it('should get balance for an address with no transaction history', async () => { + // const address = 'bitcoincash:qp2ew6pvrs22jtsvtjyumjgas6jkvgn2hy3ad4wpw8' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute.electrumx, 'request') + // .resolves(mockData.balance) + // } + // + // // Call the details API. + // const result = await electrumxRoute._balanceFromElectrumx(address) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'confirmed') + // assert.property(result, 'unconfirmed') + // }) + // }) + + // describe('#balanceBulk', () => { + // it('should throw an error for an empty body', async () => { + // req.body = {} + // + // const result = await electrumxRoute.balanceBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'addresses needs to be an array', + // 'Proper error message' + // ) + // }) + // + // it('should error on non-array single address', async () => { + // req.body = { + // address: 'qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' + // } + // + // const result = await electrumxRoute.balanceBulk(req, res) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'addresses needs to be an array', + // 'Proper error message' + // ) + // }) + // + // it('should throw an error for an invalid address', async () => { + // req.body = { + // addresses: ['02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] + // } + // + // const result = await electrumxRoute.balanceBulk(req, res) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'Invalid BCH address', + // 'Proper error message' + // ) + // }) + // + // it('should throw 400 error if addresses array is too large', async () => { + // const testArray = [] + // for (var i = 0; i < 25; i++) testArray.push('') + // + // req.body.addresses = testArray + // + // const result = await electrumxRoute.balanceBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.hasAllKeys(result, ['error']) + // assert.include(result.error, 'Array too large') + // }) + // + // it('should detect a network mismatch', async () => { + // req.body = { + // addresses: ['bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'] + // } + // + // const result = await electrumxRoute.balanceBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include(result.error, 'Invalid network', 'Proper error message') + // }) + // + // it('should get details for a single address', async () => { + // req.body = { + // addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'] + // } + // + // // Mock the Insight URL for unit tests. + // 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.balanceBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'balances') + // assert.isArray(result.balances) + // + // assert.property(result.balances[0], 'address') + // assert.property(result.balances[0], 'balance') + // + // assert.property(result.balances[0].balance, 'confirmed') + // assert.property(result.balances[0].balance, 'unconfirmed') + // }) + // + // it('should get utxos for multiple addresses', async () => { + // req.body = { + // addresses: [ + // 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', + // 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' + // ] + // } + // + // // Mock the Insight URL for unit tests. + // 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.balanceBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.isArray(result.balances) + // assert.equal(result.balances.length, 2, '2 outputs for 2 inputs') + // }) + // }) + + // describe('#_transactionsFromElectrumx', () => { + // it('should throw error for invalid address', async () => { + // try { + // // Address has invalid checksum. + // const address = 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' + // + // // Call the details API. + // await electrumxRoute._transactionsFromElectrumx(address) + // + // assert.equal(true, false, 'Unexpected code path') + // } catch (err) { + // // console.log('err2: ', err) + // assert.include(err.message, 'Invalid checksum') + // } + // }) + // + // it('should get transaction history for a single address', async () => { + // const address = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute.electrumx, 'request') + // .resolves(mockData.txHistory) + // } + // + // // Call the details API. + // const result = await electrumxRoute._transactionsFromElectrumx(address) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.isArray(result) + // assert.property(result[0], 'height') + // assert.property(result[0], 'tx_hash') + // }) + // + // it('should get history for an address with no transaction history', async () => { + // const address = 'bitcoincash:qp2ew6pvrs22jtsvtjyumjgas6jkvgn2hy3ad4wpw8' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox.stub(electrumxRoute.electrumx, 'request').resolves([]) + // } + // + // // Call the details API. + // const result = await electrumxRoute._transactionsFromElectrumx(address) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.isArray(result) + // assert.equal(result.length, 0) + // }) + // }) + + // describe('#getTransactions', () => { + // it('should throw 400 if address is empty', async () => { + // const result = await electrumxRoute.getTransactions(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.getTransactions(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.getTransactions(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.getTransactions(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 ElectrumX to user', async () => { + // // Address has invalid checksum. + // req.params.address = + // 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' + // + // // Call the details API. + // const result = await electrumxRoute.getTransactions(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 transactions 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, '_transactionsFromElectrumx') + // .resolves(mockData.txHistory) + // } + // + // // Call the details API. + // const result = await electrumxRoute.getTransactions(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'transactions') + // assert.isArray(result.transactions) + // assert.property(result.transactions[0], 'height') + // assert.property(result.transactions[0], 'tx_hash') + // }) + // }) + + // describe('#balanceBulk', () => { + // it('should throw an error for an empty body', async () => { + // req.body = {} + // + // const result = await electrumxRoute.transactionsBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'addresses needs to be an array', + // 'Proper error message' + // ) + // }) + // + // it('should error on non-array single address', async () => { + // req.body = { + // address: 'qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' + // } + // + // const result = await electrumxRoute.transactionsBulk(req, res) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'addresses needs to be an array', + // 'Proper error message' + // ) + // }) + // + // it('should throw an error for an invalid address', async () => { + // req.body = { + // addresses: ['02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] + // } + // + // const result = await electrumxRoute.transactionsBulk(req, res) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'Invalid BCH address', + // 'Proper error message' + // ) + // }) + // + // it('should throw 400 error if addresses array is too large', async () => { + // const testArray = [] + // for (var i = 0; i < 25; i++) testArray.push('') + // + // req.body.addresses = testArray + // + // const result = await electrumxRoute.transactionsBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.hasAllKeys(result, ['error']) + // assert.include(result.error, 'Array too large') + // }) + // + // it('should detect a network mismatch', async () => { + // req.body = { + // addresses: ['bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'] + // } + // + // const result = await electrumxRoute.transactionsBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include(result.error, 'Invalid network', 'Proper error message') + // }) + // + // it('should get details for a single address', async () => { + // req.body = { + // addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'] + // } + // + // // Mock the Insight URL for unit tests. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute, '_transactionsFromElectrumx') + // .resolves(mockData.txHistory) + // } + // + // // Call the details API. + // const result = await electrumxRoute.transactionsBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'transactions') + // assert.isArray(result.transactions) + // + // assert.property(result.transactions[0], 'address') + // assert.property(result.transactions[0], 'transactions') + // + // assert.isArray(result.transactions[0].transactions) + // assert.property(result.transactions[0].transactions[0], 'height') + // assert.property(result.transactions[0].transactions[0], 'tx_hash') + // }) + // + // it('should get utxos for multiple addresses', async () => { + // req.body = { + // addresses: [ + // 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', + // 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' + // ] + // } + // + // // Mock the Insight URL for unit tests. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute, '_transactionsFromElectrumx') + // .resolves(mockData.txHistory) + // } + // + // // Call the details API. + // const result = await electrumxRoute.transactionsBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.isArray(result.transactions) + // assert.equal(result.transactions.length, 2, '2 outputs for 2 inputs') + // }) + // }) + + // describe('#_mempoolFromElectrumx', () => { + // it('should throw error for invalid address', async () => { + // try { + // // Address has invalid checksum. + // const address = 'bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur2' + // + // // Call the details API. + // await electrumxRoute._mempoolFromElectrumx(address) + // + // assert.equal(true, false, 'Unexpected code path') + // } catch (err) { + // assert.include(err.message, 'Invalid checksum') + // } + // }) + // + // it('should return empty array for address with no unconfirmed utxos', async () => { + // // Address has invalid checksum. + // const address = 'bchtest:qqtmlpspjakqlvywae226esrcdrj9auynuwadh55uf' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox.stub(electrumxRoute.electrumx, 'request').resolves([]) + // } + // + // // Call the details API. + // const result = await electrumxRoute._mempoolFromElectrumx(address) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.isArray(result) + // assert.equal(result.length, 0) + // }) + // + // it('should get mempool for a single address', async () => { + // const address = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute.electrumx, 'request') + // .resolves(mockData.mempool) + // } else { + // // Skip this test for integrations. Unconfirmed UTXOs are transient and + // // not easy to test in real-time. + // assert.equal(true, true) + // return + // } + // + // // Call the details API. + // const result = await electrumxRoute._mempoolFromElectrumx(address) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.isArray(result) + // assert.property(result[0], 'height') + // assert.property(result[0], 'tx_hash') + // assert.property(result[0], 'fee') + // }) + // }) + + // describe('#getMempool', () => { + // it('should throw 400 if address is empty', async () => { + // const result = await electrumxRoute.getMempool(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.getMempool(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.getMempool(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.getMempool(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.getMempool(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 mempool for a single address', async () => { + // req.params.address = + // 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' + // + // // Mock unit tests to prevent live network calls. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute, '_mempoolFromElectrumx') + // .resolves(mockData.mempool) + // } else { + // // Skip this test for integrations. Unconfirmed UTXOs are transient and + // // not easy to test in real-time. + // assert.equal(true, true) + // return + // } + // + // // Call the details API. + // const result = await electrumxRoute.getMempool(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'utxos') + // assert.isArray(result.utxos) + // + // assert.property(result.utxos[0], 'height') + // assert.property(result.utxos[0], 'tx_hash') + // assert.property(result.utxos[0], 'fee') + // }) + // }) + + // describe('#mempoolBulk', () => { + // it('should throw an error for an empty body', async () => { + // req.body = {} + // + // const result = await electrumxRoute.mempoolBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'addresses needs to be an array', + // 'Proper error message' + // ) + // }) + // + // it('should error on non-array single address', async () => { + // req.body = { + // address: 'qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c' + // } + // + // const result = await electrumxRoute.mempoolBulk(req, res) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'addresses needs to be an array', + // 'Proper error message' + // ) + // }) + // + // it('should throw 400 error if addresses array is too large', async () => { + // const testArray = [] + // for (var i = 0; i < 25; i++) testArray.push('') + // + // req.body.addresses = testArray + // + // const result = await electrumxRoute.mempoolBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.hasAllKeys(result, ['error']) + // assert.include(result.error, 'Array too large') + // }) + // + // it('should throw an error for an invalid address', async () => { + // req.body = { + // addresses: ['02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'] + // } + // + // const result = await electrumxRoute.mempoolBulk(req, res) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include( + // result.error, + // 'Invalid BCH address', + // 'Proper error message' + // ) + // }) + // + // it('should detect a network mismatch', async () => { + // req.body = { + // addresses: ['bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'] + // } + // + // const result = await electrumxRoute.mempoolBulk(req, res) + // // console.log(`result: ${util.inspect(result)}`) + // + // assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') + // assert.include(result.error, 'Invalid network', 'Proper error message') + // }) + // + // it('should get mempool details for a single address', async () => { + // req.body = { + // addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'] + // } + // + // // Mock the Insight URL for unit tests. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute, '_mempoolFromElectrumx') + // .resolves(mockData.mempool) + // } else { + // // Skip this test for integrations. Unconfirmed UTXOs are transient and + // // not easy to test in real-time. + // assert.equal(true, true) + // return + // } + // + // // Call the details API. + // const result = await electrumxRoute.mempoolBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.property(result, 'utxos') + // assert.isArray(result.utxos) + // + // assert.property(result.utxos[0], 'address') + // assert.property(result.utxos[0], 'utxos') + // + // assert.isArray(result.utxos[0].utxos) + // assert.property(result.utxos[0].utxos[0], 'height') + // assert.property(result.utxos[0].utxos[0], 'tx_hash') + // assert.property(result.utxos[0].utxos[0], 'fee') + // }) + // + // it('should get mempool for multiple addresses', async () => { + // req.body = { + // addresses: [ + // 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', + // 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf' + // ] + // } + // + // // Mock the Insight URL for unit tests. + // if (process.env.TEST === 'unit') { + // electrumxRoute.isReady = true // Force flag. + // + // sandbox + // .stub(electrumxRoute, '_mempoolFromElectrumx') + // .resolves(mockData.mempool) + // } else { + // // Skip this test for integrations. Unconfirmed UTXOs are transient and + // // not easy to test in real-time. + // assert.equal(true, true) + // return + // } + // + // // Call the details API. + // const result = await electrumxRoute.mempoolBulk(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.property(result, 'success') + // assert.equal(result.success, true) + // + // assert.isArray(result.utxos) + // assert.isArray(result.utxos[0].utxos) + // assert.equal(result.utxos.length, 2, '2 outputs for 2 inputs') + // }) + // }) }) diff --git a/test/v5/blockchain.js b/test/v5/blockchain.js index 3853f7f..5b2eee5 100644 --- a/test/v5/blockchain.js +++ b/test/v5/blockchain.js @@ -12,7 +12,7 @@ const chai = require('chai') const assert = chai.assert const sinon = require('sinon') -const Blockchain = require('../../src/routes/v4/full-node/blockchain') +const Blockchain = require('../../src/routes/v5/full-node/blockchain') const uut = new Blockchain() const util = require('util') diff --git a/test/v5/control.js b/test/v5/control.js index b5bc020..61dd38e 100644 --- a/test/v5/control.js +++ b/test/v5/control.js @@ -11,7 +11,7 @@ const chai = require('chai') const assert = chai.assert -const ControlRoute = require('../../src/routes/v4/full-node/control') +const ControlRoute = require('../../src/routes/v5/full-node/control') const uut = new ControlRoute() const sinon = require('sinon') diff --git a/test/v5/encryption.js b/test/v5/encryption.js index 735baba..4afaf8d 100644 --- a/test/v5/encryption.js +++ b/test/v5/encryption.js @@ -19,7 +19,7 @@ const sinon = require('sinon') if (!process.env.TEST) process.env.TEST = 'unit' // Only load blockbook library after setting BLOCKBOOK_URL env var. -const EncryptionRoute = require('../../src/routes/v4/encryption') +const EncryptionRoute = require('../../src/routes/v5/encryption') const encryptionRoute = new EncryptionRoute() // Mocking data. diff --git a/test/v5/mining.js b/test/v5/mining.js index 1c323ac..65edc63 100644 --- a/test/v5/mining.js +++ b/test/v5/mining.js @@ -10,7 +10,7 @@ const chai = require('chai') const assert = chai.assert -const MiningRoute = require('../../src/routes/v4/full-node/mining') +const MiningRoute = require('../../src/routes/v5/full-node/mining') const uut = new MiningRoute() // const nock = require('nock') // HTTP mocking 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' diff --git a/test/v5/price.js b/test/v5/price.js index b9615bb..1e795f9 100644 --- a/test/v5/price.js +++ b/test/v5/price.js @@ -13,7 +13,7 @@ const chai = require('chai') const assert = chai.assert const sinon = require('sinon') -const Price = require('../../src/routes/v4/price') +const Price = require('../../src/routes/v5/price') let uut // Mocking data. diff --git a/test/v5/raw-transactions.js b/test/v5/raw-transactions.js index 587a12c..06dc8ed 100644 --- a/test/v5/raw-transactions.js +++ b/test/v5/raw-transactions.js @@ -14,7 +14,7 @@ const chai = require('chai') const assert = chai.assert -const Rawtransactions = require('../../src/routes/v4/full-node/rawtransactions') +const Rawtransactions = require('../../src/routes/v5/full-node/rawtransactions') const uut = new Rawtransactions() // const nock = require('nock') // HTTP mocking diff --git a/test/v5/slp.js b/test/v5/slp.js index 3cb48e5..9ce7561 100644 --- a/test/v5/slp.js +++ b/test/v5/slp.js @@ -40,11 +40,11 @@ if (process.env.TEST === 'unit') { } // Prepare the slpRoute for stubbing dependcies on slpjs. -const SlpRoute = require('../../src/routes/v4/slp') +const SlpRoute = require('../../src/routes/v5/slp') const slpRoute = new SlpRoute() // const pathStub = {} // Used to stub methods within slpjs. -// const slpRouteStub = proxyquire('../../src/routes/v4/slp', { slpjs: pathStub }) +// const slpRouteStub = proxyquire('../../src/routes/v5/slp', { slpjs: pathStub }) // Mocking data. const { mockReq, mockRes } = require('./mocks/express-mocks') diff --git a/test/v5/util.js b/test/v5/util.js index 939936b..24bad9f 100644 --- a/test/v5/util.js +++ b/test/v5/util.js @@ -19,8 +19,8 @@ const nock = require('nock') // HTTP mocking const sinon = require('sinon') // Local libraries -const Electrumx = require('../../src/routes/v4/electrumx') -const UtilRoute = require('../../src/routes/v4/util') +const Electrumx = require('../../src/routes/v5/electrumx') +const UtilRoute = require('../../src/routes/v5/util') let originalEnvVars // Used during transition from integration to unit tests. diff --git a/test/v5/xpub.js b/test/v5/xpub.js index c5723d4..91b797b 100644 --- a/test/v5/xpub.js +++ b/test/v5/xpub.js @@ -10,7 +10,7 @@ const chai = require('chai') const assert = chai.assert -const xpubRoute = require('../../src/routes/v4/xpub') +const xpubRoute = require('../../src/routes/v5/xpub') const nock = require('nock') // HTTP mocking // let originalUrl // Used during transition from integration to unit tests. @@ -119,7 +119,8 @@ describe('#XPUBRouter', () => { }) */ it('should create an address from xpub', async () => { - req.params.xpub = 'tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM' + req.params.xpub = + 'tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM' // Mock the Insight URL for unit tests. // TODO add unit test