diff --git a/src/routes/v5/full-node/blockchain.js b/src/routes/v5/full-node/blockchain.js index 7c902aa..9355301 100644 --- a/src/routes/v5/full-node/blockchain.js +++ b/src/routes/v5/full-node/blockchain.js @@ -2,75 +2,76 @@ A library for interacting with the Full Node */ -'use strict' +"use strict"; -const express = require('express') -const router = express.Router() +const express = require("express"); +const router = express.Router(); -const axios = require('axios') -const wlogger = require('../../../util/winston-logging') +const axios = require("axios"); +const wlogger = require("../../../util/winston-logging"); -const RouteUtils = require('../../../util/route-utils') -const routeUtils = new RouteUtils() +const RouteUtils = require("../../../util/route-utils"); +const routeUtils = new RouteUtils(); // Used to convert error messages to strings, to safely pass to users. -const util = require('util') -util.inspect.defaultOptions = { depth: 1 } +const util = require("util"); +util.inspect.defaultOptions = { depth: 1 }; -const BCHJS = require('@psf/bch-js') -const bchjs = new BCHJS() +const BCHJS = require("@psf/bch-js"); +const bchjs = new BCHJS(); -let _this +let _this; class Blockchain { - constructor () { - _this = this + constructor() { + _this = this; - this.bchjs = bchjs - this.axios = axios - this.routeUtils = routeUtils + this.bchjs = bchjs; + this.axios = axios; + this.routeUtils = routeUtils; - this.router = router - this.router.get('/', this.root) - this.router.get('/getBestBlockHash', this.getBestBlockHash) - this.router.get('/getBlockchainInfo', this.getBlockchainInfo) - this.router.get('/getBlockCount', this.getBlockCount) - this.router.get('/getBlockHeader/:hash', this.getBlockHeaderSingle) - this.router.post('/getBlockHeader', this.getBlockHeaderBulk) - this.router.get('/getChainTips', this.getChainTips) - this.router.get('/getDifficulty', this.getDifficulty) - this.router.get('/getMempoolEntry/:txid', this.getMempoolEntrySingle) - this.router.post('/getMempoolEntry', this.getMempoolEntryBulk) + this.router = router; + this.router.get("/", this.root); + this.router.get("/getBestBlockHash", this.getBestBlockHash); + this.router.get("/getBlockchainInfo", this.getBlockchainInfo); + this.router.get("/getBlockCount", this.getBlockCount); + this.router.get("/getBlockHeader/:hash", this.getBlockHeaderSingle); + this.router.post("/getBlockHeader", this.getBlockHeaderBulk); + this.router.get("/getChainTips", this.getChainTips); + this.router.get("/getDifficulty", this.getDifficulty); + this.router.get("/getMempoolEntry/:txid", this.getMempoolEntrySingle); + this.router.post("/getMempoolEntry", this.getMempoolEntryBulk); this.router.get( - '/getMempoolAncestors/:txid', + "/getMempoolAncestors/:txid", this.getMempoolAncestorsSingle - ) - this.router.get('/getMempoolInfo', this.getMempoolInfo) - this.router.get('/getRawMempool', this.getRawMempool) - this.router.get('/getTxOut/:txid/:n', this.getTxOut) - this.router.post('/getTxOut', this.getTxOutPost) - this.router.get('/getTxOutProof/:txid', this.getTxOutProofSingle) - this.router.post('/getTxOutProof', this.getTxOutProofBulk) - this.router.get('/verifyTxOutProof/:proof', this.verifyTxOutProofSingle) - this.router.post('/verifyTxOutProof', this.verifyTxOutProofBulk) - this.router.post('/getBlock', this.getBlock) + ); + this.router.get("/getMempoolInfo", this.getMempoolInfo); + this.router.get("/getRawMempool", this.getRawMempool); + this.router.get("/getTxOut/:txid/:n", this.getTxOut); + this.router.post("/getTxOut", this.getTxOutPost); + this.router.get("/getTxOutProof/:txid", this.getTxOutProofSingle); + this.router.post("/getTxOutProof", this.getTxOutProofBulk); + this.router.get("/verifyTxOutProof/:proof", this.verifyTxOutProofSingle); + this.router.post("/verifyTxOutProof", this.verifyTxOutProofBulk); + this.router.post("/getBlock", this.getBlock); + this.router.get("/getBlockHash/:height", this.getBlockHash); } - root (req, res, next) { - return res.json({ status: 'blockchain' }) + root(req, res, next) { + return res.json({ status: "blockchain" }); } // DRY error handler. - errorHandler (err, res) { + errorHandler(err, res) { // Attempt to decode the error message. - const { msg, status } = _this.routeUtils.decodeError(err) + const { msg, status } = _this.routeUtils.decodeError(err); if (msg) { - res.status(status) - return res.json({ error: msg }) + res.status(status); + return res.json({ error: msg }); } - res.status(500) - return res.json({ error: util.inspect(err) }) + res.status(500); + return res.json({ error: util.inspect(err) }); } /** @@ -85,23 +86,23 @@ class Blockchain { * * @apiSuccess {String} bestBlockHash 000000000000000002bc884334336d99c9a9c616670a9244c6a8c1fc35aa91a1 */ - async getBestBlockHash (req, res, next) { + async getBestBlockHash(req, res, next) { try { // Axios options - const options = _this.routeUtils.getAxiosOptions() - options.data.id = 'getbestblockhash' - options.data.method = 'getbestblockhash' - options.data.params = [] + const options = _this.routeUtils.getAxiosOptions(); + options.data.id = "getbestblockhash"; + options.data.method = "getbestblockhash"; + options.data.params = []; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); // console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`) - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. - wlogger.error('Error in blockchain.ts/getBestBlockHash().', err) + wlogger.error("Error in blockchain.ts/getBestBlockHash().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -130,22 +131,22 @@ class Blockchain { * @apiSuccess {Object} object.softforks.reject * @apiSuccess {String} object.softforks.reject.status true */ - async getBlockchainInfo (req, res, next) { + async getBlockchainInfo(req, res, next) { try { // Axios options - const options = _this.routeUtils.getAxiosOptions() - options.data.id = 'getblockchaininfo' - options.data.method = 'getblockchaininfo' - options.data.params = [] + const options = _this.routeUtils.getAxiosOptions(); + options.data.id = "getblockchaininfo"; + options.data.method = "getblockchaininfo"; + options.data.params = []; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. - wlogger.error('Error in blockchain.ts/getBlockchainInfo().', err) + wlogger.error("Error in blockchain.ts/getBlockchainInfo().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -160,23 +161,23 @@ class Blockchain { * * @apiSuccess {Number} bestBlockCount 587665 */ - async getBlockCount (req, res, next) { + async getBlockCount(req, res, next) { try { // Axios options - const options = _this.routeUtils.getAxiosOptions() - options.data.id = 'getblockcount' - options.data.method = 'getblockcount' - options.data.params = [] + const options = _this.routeUtils.getAxiosOptions(); + options.data.id = "getblockcount"; + options.data.method = "getblockcount"; + options.data.params = []; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getBlockCount().', err) + wlogger.error("Error in blockchain.ts/getBlockCount().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -210,34 +211,34 @@ class Blockchain { * @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523" * @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3" */ - async getBlockHeaderSingle (req, res, next) { + async getBlockHeaderSingle(req, res, next) { try { - let verbose = false - if (req.query.verbose && req.query.verbose.toString() === 'true') { - verbose = true + let verbose = false; + if (req.query.verbose && req.query.verbose.toString() === "true") { + verbose = true; } - const hash = req.params.hash - if (!hash || hash === '') { - res.status(400) - return res.json({ error: 'hash can not be empty' }) + const hash = req.params.hash; + if (!hash || hash === "") { + res.status(400); + return res.json({ error: "hash can not be empty" }); } // Axios options - const options = _this.routeUtils.getAxiosOptions() - options.data.id = 'getblockheader' - options.data.method = 'getblockheader' - options.data.params = [hash, verbose] + const options = _this.routeUtils.getAxiosOptions(); + options.data.id = "getblockheader"; + options.data.method = "getblockheader"; + options.data.params = [hash, verbose]; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getBlockHeaderSingle().', err) + wlogger.error("Error in blockchain.ts/getBlockHeaderSingle().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -271,66 +272,66 @@ class Blockchain { * @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523" * @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3" */ - async getBlockHeaderBulk (req, res, next) { + async getBlockHeaderBulk(req, res, next) { try { - const hashes = req.body.hashes - const verbose = req.body.verbose ? req.body.verbose : false + const hashes = req.body.hashes; + const verbose = req.body.verbose ? req.body.verbose : false; if (!Array.isArray(hashes)) { - res.status(400) + res.status(400); return res.json({ - error: 'hashes needs to be an array. Use GET for single hash.' - }) + error: "hashes needs to be an array. Use GET for single hash.", + }); } // Enforce array size rate limits if (!routeUtils.validateArraySize(req, hashes)) { - res.status(400) // https://github.com/Bitcoin-com/api.fullstack.cash/issues/330 + res.status(400); // https://github.com/Bitcoin-com/api.fullstack.cash/issues/330 return res.json({ - error: 'Array too large.' - }) + error: "Array too large.", + }); } wlogger.debug( - 'Executing blockchain/getBlockHeaderBulk with these hashes: ', + "Executing blockchain/getBlockHeaderBulk with these hashes: ", hashes - ) + ); // Validate each hash in the array. for (let i = 0; i < hashes.length; i++) { - const hash = hashes[i] + const hash = hashes[i]; if (hash.length !== 64) { - res.status(400) - return res.json({ error: `This is not a hash: ${hash}` }) + res.status(400); + return res.json({ error: `This is not a hash: ${hash}` }); } } // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); // Loop through each hash and creates an array of requests to call in parallel const promises = hashes.map(async (hash) => { - options.data.id = 'getblockheader' - options.data.method = 'getblockheader' - options.data.params = [hash, verbose] + options.data.id = "getblockheader"; + options.data.method = "getblockheader"; + options.data.params = [hash, verbose]; - return _this.axios.request(options) - }) + return _this.axios.request(options); + }); - const axiosResult = await _this.axios.all(promises) + const axiosResult = await _this.axios.all(promises); // Extract the data component from the axios response. - const result = axiosResult.map((x) => x.data.result) + const result = axiosResult.map((x) => x.data.result); - res.status(200) - return res.json(result) + res.status(200); + return res.json(result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getBlockHeaderBulk().', err) + wlogger.error("Error in blockchain.ts/getBlockHeaderBulk().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -345,23 +346,23 @@ class Blockchain { * curl -X GET "https://api.fullstack.cash/v5/blockchain/getChainTips" -H "accept: application/json" * */ - async getChainTips (req, res, next) { + async getChainTips(req, res, next) { try { // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'getchaintips' - options.data.method = 'getchaintips' - options.data.params = [] + options.data.id = "getchaintips"; + options.data.method = "getchaintips"; + options.data.params = []; - const response = await _this.axios.request(options) - return res.json(response.data.result) + const response = await _this.axios.request(options); + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getChainTips().', err) + wlogger.error("Error in blockchain.ts/getChainTips().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -376,24 +377,24 @@ class Blockchain { * curl -X GET "https://api.fullstack.cash/v5/blockchain/getDifficulty" -H "accept: application/json" * */ - async getDifficulty (req, res, next) { + async getDifficulty(req, res, next) { try { // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'getdifficulty' - options.data.method = 'getdifficulty' - options.data.params = [] + options.data.id = "getdifficulty"; + options.data.method = "getdifficulty"; + options.data.params = []; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getDifficulty().', err) + wlogger.error("Error in blockchain.ts/getDifficulty().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -408,31 +409,31 @@ class Blockchain { * curl -X GET "https://api.fullstack.cash/v5/blockchain/getMempoolEntry/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json" * */ - async getMempoolEntrySingle (req, res, next) { + async getMempoolEntrySingle(req, res, next) { try { // Validate input parameter - const txid = req.params.txid - if (!txid || txid === '') { - res.status(400) - return res.json({ error: 'txid can not be empty' }) + const txid = req.params.txid; + if (!txid || txid === "") { + res.status(400); + return res.json({ error: "txid can not be empty" }); } // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'getmempoolentry' - options.data.method = 'getmempoolentry' - options.data.params = [txid] + options.data.id = "getmempoolentry"; + options.data.method = "getmempoolentry"; + options.data.params = [txid]; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getMempoolEntrySingle().', err) + wlogger.error("Error in blockchain.ts/getMempoolEntrySingle().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -445,65 +446,65 @@ class Blockchain { * @apiExample Example usage: * curl -X POST https://api.fullstack.cash/v5/blockchain/getMempoolEntry -H "Content-Type: application/json" -d "{\"txids\":[\"a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1\",\"5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e\"]}" */ - async getMempoolEntryBulk (req, res, next) { + async getMempoolEntryBulk(req, res, next) { try { - const txids = req.body.txids + const txids = req.body.txids; if (!Array.isArray(txids)) { - res.status(400) + res.status(400); return res.json({ - error: 'txids needs to be an array. Use GET for single txid.' - }) + error: "txids needs to be an array. Use GET for single txid.", + }); } // Enforce array size rate limits if (!routeUtils.validateArraySize(req, txids)) { - res.status(400) // https://github.com/Bitcoin-com/api.fullstack.cash/issues/330 + res.status(400); // https://github.com/Bitcoin-com/api.fullstack.cash/issues/330 return res.json({ - error: 'Array too large.' - }) + error: "Array too large.", + }); } wlogger.debug( - 'Executing blockchain/getMempoolEntry with these txids: ', + "Executing blockchain/getMempoolEntry with these txids: ", txids - ) + ); // Validate each element in the array for (let i = 0; i < txids.length; i++) { - const txid = txids[i] + const txid = txids[i]; if (txid.length !== 64) { - res.status(400) - return res.json({ error: 'This is not a txid' }) + res.status(400); + return res.json({ error: "This is not a txid" }); } } // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); // Loop through each txid and creates an array of requests to call in parallel const promises = txids.map(async (txid) => { - options.data.id = 'getmempoolentry' - options.data.method = 'getmempoolentry' - options.data.params = [txid] + options.data.id = "getmempoolentry"; + options.data.method = "getmempoolentry"; + options.data.params = [txid]; - return _this.axios.request(options) - }) + return _this.axios.request(options); + }); - const axiosResult = await _this.axios.all(promises) + const axiosResult = await _this.axios.all(promises); // Extract the data component from the axios response. - const result = axiosResult.map((x) => x.data.result) + const result = axiosResult.map((x) => x.data.result); - res.status(200) - return res.json(result) + res.status(200); + return res.json(result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getMempoolEntryBulk().', err) + wlogger.error("Error in blockchain.ts/getMempoolEntryBulk().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -519,35 +520,35 @@ class Blockchain { * curl -X GET "https://api.fullstack.cash/v5/blockchain/getMempoolAncestors/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json" * */ - async getMempoolAncestorsSingle (req, res, next) { + async getMempoolAncestorsSingle(req, res, next) { try { // Validate input parameter - const txid = req.params.txid - if (!txid || txid === '') { - res.status(400) - return res.json({ error: 'txid can not be empty' }) + const txid = req.params.txid; + if (!txid || txid === "") { + res.status(400); + return res.json({ error: "txid can not be empty" }); } - let verbose = req.params.verbose - if (verbose === undefined) verbose = false + let verbose = req.params.verbose; + if (verbose === undefined) verbose = false; // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'getmempoolancestors' - options.data.method = 'getmempoolancestors' - options.data.params = [txid, verbose] + options.data.id = "getmempoolancestors"; + options.data.method = "getmempoolancestors"; + options.data.params = [txid, verbose]; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); // console.log(`response: ${util.inspect(response)}`) - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getMempoolAncestorsSingle().', err) + wlogger.error("Error in blockchain.ts/getMempoolAncestorsSingle().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -561,23 +562,23 @@ class Blockchain { * curl -X GET https://api.fullstack.cash/v5/getMempoolInfo -H "accept: application/json" * */ - async getMempoolInfo (req, res, next) { + async getMempoolInfo(req, res, next) { try { // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'getmempoolinfo' - options.data.method = 'getmempoolinfo' - options.data.params = [] + options.data.id = "getmempoolinfo"; + options.data.method = "getmempoolinfo"; + options.data.params = []; - const response = await _this.axios.request(options) - return res.json(response.data.result) + const response = await _this.axios.request(options); + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getMempoolInfo().', err) + wlogger.error("Error in blockchain.ts/getMempoolInfo().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -605,27 +606,27 @@ class Blockchain { * @apiParam {Boolean} verbose Return verbose data * */ - async getRawMempool (req, res, next) { + async getRawMempool(req, res, next) { try { // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - let verbose = false - if (req.query.verbose && req.query.verbose === 'true') verbose = true + let verbose = false; + if (req.query.verbose && req.query.verbose === "true") verbose = true; - options.data.id = 'getrawmempool' - options.data.method = 'getrawmempool' - options.data.params = [verbose] + options.data.id = "getrawmempool"; + options.data.method = "getrawmempool"; + options.data.params = [verbose]; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getRawMempool().', err) + wlogger.error("Error in blockchain.ts/getRawMempool().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -644,45 +645,45 @@ class Blockchain { * */ // Returns details about an unspent transaction output. - async getTxOut (req, res, next) { + async getTxOut(req, res, next) { try { // Validate input parameter - const txid = req.params.txid - if (!txid || txid === '') { - res.status(400) - return res.json({ error: 'txid can not be empty' }) + const txid = req.params.txid; + if (!txid || txid === "") { + res.status(400); + return res.json({ error: "txid can not be empty" }); } - let n = req.params.n - if (n === undefined || n === '') { - res.status(400) - return res.json({ error: 'n can not be empty' }) + let n = req.params.n; + if (n === undefined || n === "") { + res.status(400); + return res.json({ error: "n can not be empty" }); } - n = parseInt(n) + n = parseInt(n); - let includeMempool = false - if (req.query.includeMempool && req.query.includeMempool === 'true') { - includeMempool = true + let includeMempool = false; + if (req.query.includeMempool && req.query.includeMempool === "true") { + includeMempool = true; } // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'gettxout' - options.data.method = 'gettxout' - options.data.params = [txid, n, includeMempool] + options.data.id = "gettxout"; + options.data.method = "gettxout"; + options.data.params = [txid, n, includeMempool]; // console.log(`requestConfig: ${JSON.stringify(requestConfig, null, 2)}`) - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getTxOut().', err) + wlogger.error("Error in blockchain.ts/getTxOut().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -701,42 +702,42 @@ class Blockchain { * */ // Returns details about an unspent transaction output. - async getTxOutPost (req, res, next) { + async getTxOutPost(req, res, next) { try { - const txid = req.body.txid - let n = req.body.vout - const mempool = req.body.mempool ? req.body.mempool : true + const txid = req.body.txid; + let n = req.body.vout; + const mempool = req.body.mempool ? req.body.mempool : true; // Validate input parameter - if (!txid || txid === '') { - res.status(400) - return res.json({ error: 'txid can not be empty' }) + if (!txid || txid === "") { + res.status(400); + return res.json({ error: "txid can not be empty" }); } - if (n === undefined || n === '') { - res.status(400) - return res.json({ error: 'vout can not be empty' }) + if (n === undefined || n === "") { + res.status(400); + return res.json({ error: "vout can not be empty" }); } - n = parseInt(n) + n = parseInt(n); // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'gettxout' - options.data.method = 'gettxout' - options.data.params = [txid, n, mempool] + options.data.id = "gettxout"; + options.data.method = "gettxout"; + options.data.params = [txid, n, mempool]; // console.log(`requestConfig: ${JSON.stringify(requestConfig, null, 2)}`) - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getTxOutPost().', err) + wlogger.error("Error in blockchain.ts/getTxOutPost().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -752,190 +753,190 @@ class Blockchain { * @apiParam {String} txid Transaction id (required) * */ - async getTxOutProofSingle (req, res, next) { + async getTxOutProofSingle(req, res, next) { try { // Validate input parameter - const txid = req.params.txid - if (!txid || txid === '') { - res.status(400) - return res.json({ error: 'txid can not be empty' }) + const txid = req.params.txid; + if (!txid || txid === "") { + res.status(400); + return res.json({ error: "txid can not be empty" }); } // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'gettxoutproof' - options.data.method = 'gettxoutproof' - options.data.params = [[txid]] + options.data.id = "gettxoutproof"; + options.data.method = "gettxoutproof"; + options.data.params = [[txid]]; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getTxOutProofSingle().', err) + wlogger.error("Error in blockchain.ts/getTxOutProofSingle().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } // Returns a hex-encoded proof that 'txid' was included in a block. - async getTxOutProofBulk (req, res, next) { + async getTxOutProofBulk(req, res, next) { try { - const txids = req.body.txids + const txids = req.body.txids; // Reject if txids is not an array. if (!Array.isArray(txids)) { - res.status(400) + res.status(400); return res.json({ - error: 'txids needs to be an array. Use GET for single txid.' - }) + error: "txids needs to be an array. Use GET for single txid.", + }); } // Enforce array size rate limits if (!routeUtils.validateArraySize(req, txids)) { - res.status(400) // https://github.com/Bitcoin-com/api.fullstack.cash/issues/330 + res.status(400); // https://github.com/Bitcoin-com/api.fullstack.cash/issues/330 return res.json({ - error: 'Array too large.' - }) + error: "Array too large.", + }); } // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); // Validate each element in the array. for (let i = 0; i < txids.length; i++) { - const txid = txids[i] + const txid = txids[i]; if (txid.length !== 64) { - res.status(400) + res.status(400); return res.json({ - error: `Invalid txid. Double check your txid is valid: ${txid}` - }) + error: `Invalid txid. Double check your txid is valid: ${txid}`, + }); } } wlogger.debug( - 'Executing blockchain/getTxOutProof with these txids: ', + "Executing blockchain/getTxOutProof with these txids: ", txids - ) + ); // Loop through each txid and creates an array of requests to call in parallel const promises = txids.map(async (txid) => { - options.data.id = 'gettxoutproof' - options.data.method = 'gettxoutproof' - options.data.params = [[txid]] + options.data.id = "gettxoutproof"; + options.data.method = "gettxoutproof"; + options.data.params = [[txid]]; - return _this.axios.request(options) - }) + return _this.axios.request(options); + }); // Wait for all parallel promisses to resolve. - const axiosResult = await _this.axios.all(promises) + const axiosResult = await _this.axios.all(promises); // Extract the data component from the axios response. - const result = axiosResult.map((x) => x.data.result) + const result = axiosResult.map((x) => x.data.result); - res.status(200) - return res.json(result) + res.status(200); + return res.json(result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/getTxOutProofBulk().', err) + wlogger.error("Error in blockchain.ts/getTxOutProofBulk().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } - async verifyTxOutProofSingle (req, res, next) { + async verifyTxOutProofSingle(req, res, next) { try { // Validate input parameter - const proof = req.params.proof - if (!proof || proof === '') { - res.status(400) - return res.json({ error: 'proof can not be empty' }) + const proof = req.params.proof; + if (!proof || proof === "") { + res.status(400); + return res.json({ error: "proof can not be empty" }); } // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'verifytxoutproof' - options.data.method = 'verifytxoutproof' - options.data.params = [req.params.proof] + options.data.id = "verifytxoutproof"; + options.data.method = "verifytxoutproof"; + options.data.params = [req.params.proof]; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/verifyTxOutProofSingle().', err) + wlogger.error("Error in blockchain.ts/verifyTxOutProofSingle().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } - async verifyTxOutProofBulk (req, res, next) { + async verifyTxOutProofBulk(req, res, next) { try { - const proofs = req.body.proofs + const proofs = req.body.proofs; // Reject if proofs is not an array. if (!Array.isArray(proofs)) { - res.status(400) + res.status(400); return res.json({ - error: 'proofs needs to be an array. Use GET for single proof.' - }) + error: "proofs needs to be an array. Use GET for single proof.", + }); } // Enforce array size rate limits if (!routeUtils.validateArraySize(req, proofs)) { - res.status(400) // https://github.com/Bitcoin-com/api.fullstack.cash/issues/330 + res.status(400); // https://github.com/Bitcoin-com/api.fullstack.cash/issues/330 return res.json({ - error: 'Array too large.' - }) + error: "Array too large.", + }); } // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); // Validate each element in the array. for (let i = 0; i < proofs.length; i++) { - const proof = proofs[i] + const proof = proofs[i]; - if (!proof || proof === '') { - res.status(400) - return res.json({ error: `proof can not be empty: ${proof}` }) + if (!proof || proof === "") { + res.status(400); + return res.json({ error: `proof can not be empty: ${proof}` }); } } wlogger.debug( - 'Executing blockchain/verifyTxOutProof with these proofs: ', + "Executing blockchain/verifyTxOutProof with these proofs: ", proofs - ) + ); // Loop through each proof and creates an array of requests to call in parallel const promises = proofs.map(async (proof) => { - options.data.id = 'verifytxoutproof' - options.data.method = 'verifytxoutproof' - options.data.params = [proof] + options.data.id = "verifytxoutproof"; + options.data.method = "verifytxoutproof"; + options.data.params = [proof]; - return _this.axios.request(options) - }) + return _this.axios.request(options); + }); // Wait for all parallel promisses to resolve. - const axiosResult = await _this.axios.all(promises) + const axiosResult = await _this.axios.all(promises); // Extract the data component from the axios response. - const result = axiosResult.map((x) => x.data.result[0]) + const result = axiosResult.map((x) => x.data.result[0]); - res.status(200) - return res.json(result) + res.status(200); + return res.json(result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.ts/verifyTxOutProofBulk().', err) + wlogger.error("Error in blockchain.ts/verifyTxOutProofBulk().", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); } } @@ -952,38 +953,79 @@ class Blockchain { * @apiParam {Number} verbosity Default 1 (optional) * */ - async getBlock (req, res, next) { + async getBlock(req, res, next) { try { // Validate input parameter - const blockhash = req.body.blockhash - let verbosity = req.body.verbosity + const blockhash = req.body.blockhash; + let verbosity = req.body.verbosity; // Default to a value of 1 if another verbosity level is not defined. - if (!verbosity && verbosity !== 0) verbosity = 1 + if (!verbosity && verbosity !== 0) verbosity = 1; - if (!blockhash || blockhash === '') { - res.status(400) - return res.json({ error: 'blockhash can not be empty' }) + if (!blockhash || blockhash === "") { + res.status(400); + return res.json({ error: "blockhash can not be empty" }); } // Axios options - const options = _this.routeUtils.getAxiosOptions() + const options = _this.routeUtils.getAxiosOptions(); - options.data.id = 'getblock' - options.data.method = 'getblock' - options.data.params = [blockhash, verbosity] + options.data.id = "getblock"; + options.data.method = "getblock"; + options.data.params = [blockhash, verbosity]; - const response = await _this.axios.request(options) + const response = await _this.axios.request(options); - return res.json(response.data.result) + return res.json(response.data.result); } catch (err) { // Write out error to error log. // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error('Error in blockchain.js/getBlock()', err) + wlogger.error("Error in blockchain.js/getBlock()", err); - return _this.errorHandler(err, res) + return _this.errorHandler(err, res); + } + } + + /** + * @api {post} /blockchain/getBlockHash/ Get block hash + * @apiName getBlockHash + * @apiGroup Blockchain + * @apiDescription Returns the hash of a block, given its block height. + * + * @apiExample Example usage: + * curl -X GET "https://api.fullstack.cash/v5/blockchain/getBlockHash/544444" -H "accept: application/json" + * + * @apiParam {String} height Block height (required) + * + * + */ + async getBlockHash(req, res, next) { + try { + // Validate input parameter + const height = req.params.height; + if (!height || height === "") { + res.status(400); + return res.json({ error: "height can not be empty" }); + } + + // Axios options + const options = _this.routeUtils.getAxiosOptions(); + + options.data.id = "getblockhash"; + options.data.method = "getblockhash"; + options.data.params = [parseInt(height)]; + + const response = await _this.axios.request(options); + + return res.json(response.data.result); + } catch (err) { + // Write out error to error log. + // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) + wlogger.error("Error in blockchain.js/getBlockHash()", err); + + return _this.errorHandler(err, res); } } } -module.exports = Blockchain +module.exports = Blockchain;