refactor(blockchain.js): refactoring blockchain.js library [WIP]

This commit is contained in:
danielhumgon
2020-02-08 22:31:56 -04:00
parent 9582df3f71
commit e83aaad1e3
+748
View File
@@ -34,6 +34,21 @@ class Blockchain {
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", getBlockHeaderBulk)
this.router.get("/getChainTips", getChainTips)
this.router.get("/getDifficulty", getDifficulty)
this.router.get("/getMempoolEntry/:txid", getMempoolEntrySingle)
this.router.post("/getMempoolEntry", getMempoolEntryBulk)
this.router.get("/getMempoolAncestors/:txid", getMempoolAncestorsSingle)
this.router.get("/getMempoolInfo", getMempoolInfo)
this.router.get("/getRawMempool", getRawMempool)
this.router.get("/getTxOut/:txid/:n", getTxOut)
this.router.get("/getTxOutProof/:txid", getTxOutProofSingle)
this.router.post("/getTxOutProof", getTxOutProofBulk)
this.router.get("/verifyTxOutProof/:proof", verifyTxOutProofSingle)
this.router.post("/verifyTxOutProof", verifyTxOutProofBulk)
}
root(req, res, next) {
@@ -128,6 +143,739 @@ class Blockchain {
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getBlockCount Get Block Count
* @apiName GetBlockCount
* @apiGroup Blockchain
* @apiDescription Returns the number of blocks in the longest blockchain.
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getBlockCount" -H "accept: application/json"
*
* @apiSuccess {Number} bestBlockCount 587665
*/
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 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/getBlockCount().`, err)
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getBlockHeader/:hash Get single block header
* @apiName GetSingleBlockHeader
* @apiGroup Blockchain
* @apiDescription If verbose is false (default), returns a string that is
* serialized, hex-encoded data for blockheader 'hash'. If verbose is true,
* returns an Object with information about blockheader hash.
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getBlockHeader/000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201?verbose=true" -H "accept: application/json"
*
* @apiParam {String} hash block hash
* @apiParam {Boolean} verbose Return verbose data
*
* @apiSuccess {Object} object Object containing data
* @apiSuccess {String} object.hash "000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"
* @apiSuccess {Number} object.confirmations 61839
* @apiSuccess {Number} object.height 500000
* @apiSuccess {Number} object.version 536870912
* @apiSuccess {String} object.versionHex "20000000"
* @apiSuccess {String} object.merkleroot "4af279645e1b337e655ae3286fc2ca09f58eb01efa6ab27adedd1e9e6ec19091"
* @apiSuccess {Number} object.time 1509343584
* @apiSuccess {Number} object.mediantime 1509336533
* @apiSuccess {Number} object.nonce 3604508752
* @apiSuccess {String} object.bits "1809b91a"
* @apiSuccess {Number} object.difficulty 113081236211.4533
* @apiSuccess {String} object.chainwork "0000000000000000000000000000000000000000007ae48aca46e3b449ad9714"
* @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523"
* @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3"
*/
async getBlockHeaderSingle(req, res, next) {
try {
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" })
}
// Axios options
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)
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)
return this.errorHandler(err, res)
}
}
/**
* @api {post} /blockchain/getBlockHeader Get multiple block headers
* @apiName GetBulkBlockHeader
* @apiGroup Blockchain
* @apiDescription If verbose is false (default), returns a string that is
* serialized, hex-encoded data for blockheader 'hash'. If verbose is true,
* returns an Object with information about blockheader hash.
*
* @apiExample Example usage:
* curl -X POST "https://rest.bitcoin.com/v3/blockchain/getBlockHeader" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"hashes\":[\"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201\",\"00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3\"],\"verbose\":true}"
*
* @apiParam {String} hash block hash
* @apiParam {Boolean} verbose Return verbose data
*
* @apiSuccess {Array} array array containing objects
* @apiSuccess {String} object.hash "000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"
* @apiSuccess {Number} object.confirmations 61839
* @apiSuccess {Number} object.height 500000
* @apiSuccess {Number} object.version 536870912
* @apiSuccess {String} object.versionHex "20000000"
* @apiSuccess {String} object.merkleroot "4af279645e1b337e655ae3286fc2ca09f58eb01efa6ab27adedd1e9e6ec19091"
* @apiSuccess {Number} object.time 1509343584
* @apiSuccess {Number} object.mediantime 1509336533
* @apiSuccess {Number} object.nonce 3604508752
* @apiSuccess {String} object.bits "1809b91a"
* @apiSuccess {Number} object.difficulty 113081236211.4533
* @apiSuccess {String} object.chainwork "0000000000000000000000000000000000000000007ae48aca46e3b449ad9714"
* @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523"
* @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3"
*/
async getBlockHeaderBulk(req, res, next) {
try {
const hashes = req.body.hashes
const verbose = req.body.verbose ? req.body.verbose : false
if (!Array.isArray(hashes)) {
res.status(400)
return res.json({
error: "hashes needs to be an array. Use GET for single hash."
})
}
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, hashes)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
})
}
wlogger.debug(
`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]
if (hash.length !== 64) {
res.status(400)
return res.json({ error: `This is not a hash: ${hash}` })
}
}
// Axios options
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]
return await this.axios.request(options)
})
const axiosResult = await this.axios.all(promises)
// Extract the data component from the axios response.
const result = axiosResult.map(x => x.data.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)
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getChainTips Get Chain Tips
* @apiName getChainTips
* @apiGroup Blockchain
* @apiDescription Return information about all known tips in the block tree,
* including the main chain as well as orphaned branches.
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getChainTips" -H "accept: application/json"
*
*/
async getChainTips(req, res, next) {
try {
// Axios options
const options = this.routeUtils.getAxiosOptions()
options.data.id = "getchaintips"
options.data.method = "getchaintips"
options.data.params = []
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)
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getDifficulty Get difficulty
* @apiName getDifficulty
* @apiGroup Blockchain
* @apiDescription Get the current difficulty value, used to regulate mining
* power on the network.
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getDifficulty" -H "accept: application/json"
*
*/
async getDifficulty(req, res, next) {
try {
// Axios options
const options = this.routeUtils.getAxiosOptions()
options.data.id = "getdifficulty"
options.data.method = "getdifficulty"
options.data.params = []
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/getDifficulty().`, err)
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getMempoolEntry/:txid Get single mempool entry
* @apiName getMempoolEntry
* @apiGroup Blockchain
* @apiDescription Returns mempool data for given transaction. TXID must be in
* mempool (unconfirmed)
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getMempoolEntry/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
*
*/
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" })
}
// Axios options
const options = this.routeUtils.getAxiosOptions()
options.data.id = "getmempoolentry"
options.data.method = "getmempoolentry"
options.data.params = [txid]
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/getMempoolEntrySingle().`, err)
return this.errorHandler(err, res)
}
}
/**
* @api {post} /blockchain/getMempoolEntry Get bulk mempool entry
* @apiName getMempoolEntryBulk
* @apiGroup Blockchain
* @apiDescription Returns mempool data for multiple transactions
*
* @apiExample Example usage:
* curl -X POST https://mainnet.bchjs.cash/v3/blockchain/getMempoolEntry -H "Content-Type: application/json" -d "{\"txids\":[\"a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1\",\"5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e\"]}"
*/
async getMempoolEntryBulk(req, res, next) {
try {
const txids = req.body.txids
if (!Array.isArray(txids)) {
res.status(400)
return res.json({
error: "txids needs to be an array. Use GET for single txid."
})
}
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, txids)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
})
}
wlogger.debug(
`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]
if (txid.length !== 64) {
res.status(400)
return res.json({ error: "This is not a txid" })
}
}
// Axios options
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]
return await this.axios.request(options)
})
const axiosResult = await this.axios.all(promises)
// Extract the data component from the axios response.
const result = axiosResult.map(x => x.data.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)
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getMempoolAncestors/:txid Get Mempool Ancestors
* @apiName getMempoolAncestors
* @apiGroup Blockchain
* @apiDescription Returns mempool ancestors data for given TXID. It must be in
* mempool (unconfirmed). This call is handy to tell if a UTXO is bumping up
* against the 25 ancestor chain-limit.
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getMempoolAncestors/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
*
*/
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" })
}
let verbose = req.params.verbose
if (verbose === undefined) verbose = false
// Axios options
const options = this.routeUtils.getAxiosOptions()
options.data.id = "getmempoolancestors"
options.data.method = "getmempoolancestors"
options.data.params = [txid, verbose]
const response = await this.axios.request(requestConfig)
// console.log(`response: ${util.inspect(response)}`)
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)
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getMempoolInfo Get mempool info
* @apiName getMempoolInfo
* @apiGroup Blockchain
* @apiDescription Returns details on the active state of the TX memory pool.
*
* @apiExample Example usage:
* curl -X GET https://mainnet.bchjs.cash/v3/getMempoolInfo -H "accept: application/json"
*
*/
async getMempoolInfo(req, res, next) {
try {
// Axios options
const options = this.routeUtils.getAxiosOptions()
options.data.id = "getmempoolinfo"
options.data.method = "getmempoolinfo"
options.data.params = []
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)
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getRawMempool Get mempool info
* @apiName getMempoolInfo
* @apiGroup Blockchain
* @apiDescription Returns details on the active state of the TX memory pool.
*
* @apiExample Example usage:
* curl -X GET https://mainnet.bchjs.cash/v3/getMempoolInfo -H "accept: application/json"
*
*/
/**
* @api {get} /blockchain/getRawMempool/?verbose= Get raw mempool
* @apiName getRawMempool
* @apiGroup Blockchain
* @apiDescription Returns all transaction ids in memory pool as a json array
* of string transaction ids.
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/getRawMempool/?verbose=true" -H "accept: application/json"
*
* @apiParam {Boolean} verbose Return verbose data
*
*/
async getRawMempool(req, res, next) {
try {
// Axios options
const options = this.routeUtils.getAxiosOptions()
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]
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/getRawMempool().`, err)
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getTxOut/:txid/:n?mempool= Get Tx Out
* @apiName getTxOut
* @apiGroup Blockchain
* @apiDescription Returns details about an unspent transaction output.
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getTxOut/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33/0?mempool=false" -H "accept: application/json"
*
* @apiParam {String} txid Transaction id (required)
* @apiParam {Number} n Output number (required)
* @apiParam {Boolean} mempool Check mempool or not (optional)
*
*/
// Returns details about an unspent transaction output.
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" })
}
let n = req.params.n
if (n === undefined || n === "") {
res.status(400)
return res.json({ error: "n can not be empty" })
}
n = parseInt(n)
let include_mempool = false
if (req.query.include_mempool && req.query.include_mempool === "true")
include_mempool = true
// Axios options
const options = this.routeUtils.getAxiosOptions()
options.data.id = "gettxout"
options.data.method = "gettxout"
options.data.params = [txid, n, include_mempool]
// console.log(`requestConfig: ${JSON.stringify(requestConfig, null, 2)}`)
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/getTxOut().`, err)
return this.errorHandler(err, res)
}
}
/**
* @api {get} /blockchain/getTxOutProofSingle/:txid Get Tx Out Proof
* @apiName getTxOutProofSingle
* @apiGroup Blockchain
* @apiDescription Returns a hex-encoded proof that 'txid' was included in a block.
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getTxOutProofSingle/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
*
* @apiParam {String} txid Transaction id (required)
*
*/
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" })
}
// Axios options
const options = this.routeUtils.getAxiosOptions()
options.data.id = "gettxoutproof"
options.data.method = "gettxoutproof"
options.data.params = [[txid]]
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/getTxOutProofSingle().`, err)
return this.errorHandler(err, res)
}
}
// Returns a hex-encoded proof that 'txid' was included in a block.
async getTxOutProofBulk(req, res, next) {
try {
const txids = req.body.txids
// Reject if txids is not an array.
if (!Array.isArray(txids)) {
res.status(400)
return res.json({
error: "txids needs to be an array. Use GET for single txid."
})
}
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, txids)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
})
}
// Axios options
const options = this.routeUtils.getAxiosOptions()
// Validate each element in the array.
for (let i = 0; i < txids.length; i++) {
const txid = txids[i]
if (txid.length !== 64) {
res.status(400)
return res.json({
error: `Invalid txid. Double check your txid is valid: ${txid}`
})
}
}
wlogger.debug(
`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]]
return await this.axios.request(options)
})
// Wait for all parallel promisses to resolve.
const axiosResult = await this.axios.all(promises)
// Extract the data component from the axios response.
const result = axiosResult.map(x => x.data.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)
return this.errorHandler(err, res)
}
}
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" })
}
// Axios options
const options = this.routeUtils.getAxiosOptions()
options.data.id = "verifytxoutproof"
options.data.method = "verifytxoutproof"
options.data.params = [req.params.proof]
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/verifyTxOutProofSingle().`, err)
return this.errorHandler(err, res)
}
}
async verifyTxOutProofBulk(req, res, next) {
try {
const proofs = req.body.proofs
// Reject if proofs is not an array.
if (!Array.isArray(proofs)) {
res.status(400)
return res.json({
error: "proofs needs to be an array. Use GET for single proof."
})
}
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, proofs)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
})
}
// Axios options
const options = this.routeUtils.getAxiosOptions()
// Validate each element in the array.
for (let i = 0; i < proofs.length; i++) {
const proof = proofs[i]
if (!proof || proof === "") {
res.status(400)
return res.json({ error: `proof can not be empty: ${proof}` })
}
}
wlogger.debug(
`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]
return await this.axios.request(options)
})
// Wait for all parallel promisses to resolve.
const axiosResult = await this.axios.all(promises)
// Extract the data component from the axios response.
const result = axiosResult.map(x => x.data.result[0])
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)
return this.errorHandler(err, res)
}
}
}
module.exports = Blockchain