Compare commits

...
8 Commits
6 changed files with 334 additions and 524 deletions
+4
View File
@@ -0,0 +1,4 @@
{
"sampleUrl": null
}
-521
View File
@@ -1,521 +0,0 @@
/*
Blockbook API route
*/
'use strict'
const express = require('express')
const axios = require('axios')
const wlogger = require('../../util/winston-logging')
const RouteUtils = require('../../util/route-utils')
const routeUtils = new RouteUtils()
// Library for easily switching the API paths to use different instances of
// Blockbook.
const BlockbookPath = require('../../util/blockbook-path')
const BLOCKBOOKPATH = new BlockbookPath()
// BLOCKBOOKPATH.toOpenBazaar()
const router = express.Router()
// Used for processing error messages before sending them to the user.
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const BCHJS = require('@psf/bch-js')
const bchjs = new BCHJS()
let _this
class Blockbook {
constructor () {
_this = this
_this.axios = axios
_this.routeUtils = routeUtils
_this.bchjs = bchjs
_this.BLOCKBOOKPATH = BLOCKBOOKPATH
_this.router = router
_this.router.get('/', _this.root)
_this.router.get('/balance/:address', _this.balanceSingle)
_this.router.post('/balance', _this.balanceBulk)
_this.router.get('/utxos/:address', _this.utxosSingle)
_this.router.post('/utxos', _this.utxosBulk)
_this.router.get('/tx/:txid', _this.txSingle)
_this.router.post('/tx', _this.txBulk)
}
// 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({ error: msg })
}
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: 'address' })
}
// Query the Blockbook Node API for a balance on a single BCH address.
// Returns a Promise.
async balanceFromBlockbook (thisAddress) {
try {
// console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
// Convert the address to a cashaddr without a prefix.
const addr = _this.bchjs.Address.toCashAddress(thisAddress)
const path = `${_this.BLOCKBOOKPATH.addrPath}${addr}`
// console.log(`path: ${path}`)
// Query the Blockbook Node API.
const options = {
method: 'get',
baseURL: path
}
const axiosResponse = await _this.axios.request(options)
const retData = axiosResponse.data
// console.log(`retData: ${util.inspect(retData)}`)
return retData
} catch (err) {
// Dev Note: Do not log error messages here. Throw them instead and let the
// parent function handle it.
wlogger.debug('Error in blockbook.js/balanceFromBlockbook()')
throw err
}
}
async balanceSingle (req, res, next) {
try {
const address = req.params.address
if (!address || address === '') {
res.status(400)
return res.json({ error: 'address can not be empty' })
}
// Reject if address is an array.
if (Array.isArray(address)) {
res.status(400)
return res.json({
error: 'address can not be an array. Use POST for bulk upload.'
})
}
wlogger.debug(
'Executing blockbook/balanceSingle with this address: ',
address
)
// Ensure the input is a valid BCH address.
try {
// const legacyAddr = bchjs.Address.toLegacyAddress(address)
_this.bchjs.Address.toLegacyAddress(address)
} catch (err) {
res.status(400)
return res.json({
error: `Invalid BCH address. Double check your address is valid: ${address}`
})
}
// Prevent a common user error. Ensure they are using the correct network address.
const networkIsValid = _this.routeUtils.validateNetwork(address)
if (!networkIsValid) {
res.status(400)
return res.json({
error:
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
})
}
// Query the Blockbook Node API.
const retData = await _this.balanceFromBlockbook(address)
// Return the retrieved address information.
res.status(200)
return res.json(retData)
} catch (err) {
// Write out error to error log.
wlogger.error('Error in blockbook.js/balanceSingle().', err)
return _this.errorHandler(err, res)
}
}
// POST handler for bulk queries on address details
async balanceBulk (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 blockbook.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
// Insight API in parallel.
addresses = addresses.map(async (address, index) =>
// console.log(`address: ${address}`)
_this.balanceFromBlockbook(address)
)
// Wait for all parallel Insight requests to return.
const result = await _this.axios.all(addresses)
// Return the array of retrieved address information.
res.status(200)
return res.json(result)
} catch (err) {
wlogger.error('Error in blockbook.js/balanceBulk().', err)
return _this.errorHandler(err, res)
}
}
// Query the Blockbook API for utxos associated with a BCH address.
// Returns a Promise.
async utxosFromBlockbook (thisAddress) {
try {
// console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
// Convert the address to a cashaddr without a prefix.
const addr = _this.bchjs.Address.toCashAddress(thisAddress)
const path = `${_this.BLOCKBOOKPATH.utxoPath}${addr}`
// console.log(`path: ${path}`)
// Query the Blockbook API.
// Query the Blockbook Node API.
const options = {
method: 'get',
baseURL: path
}
const axiosResponse = await _this.axios.request(options)
const retData = axiosResponse.data
// console.log(`retData: ${util.inspect(retData)}`)
// Add the satoshis property to each UTXO.
for (let i = 0; i < retData.length; i++) {
retData[i].satoshis = Number(retData[i].value)
}
return retData
} catch (err) {
// Dev Note: Do not log error messages here. Throw them instead and let the
// parent function handle it.
wlogger.debug('Error in blockbook.js/utxosFromBlockbook()')
throw err
}
}
// GET handler for single balance
async utxosSingle (req, res, next) {
try {
const address = req.params.address
if (!address || address === '') {
res.status(400)
return res.json({ error: 'address can not be empty' })
}
// Reject if address is an array.
if (Array.isArray(address)) {
res.status(400)
return res.json({
error: 'address can not be an array. Use POST for bulk upload.'
})
}
wlogger.debug(
'Executing blockbook/utxosSingle with this address: ',
address
)
// Ensure the input is a valid BCH address.
try {
// const legacyAddr = bchjs.Address.toLegacyAddress(address)
_this.bchjs.Address.toLegacyAddress(address)
} catch (err) {
res.status(400)
return res.json({
error: `Invalid BCH address. Double check your address is valid: ${address}`
})
}
// Prevent a common user error. Ensure they are using the correct network address.
const networkIsValid = _this.routeUtils.validateNetwork(address)
if (!networkIsValid) {
res.status(400)
return res.json({
error:
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
})
}
// Query the Blockbook API.
const retData = await _this.utxosFromBlockbook(address)
// Return the retrieved address information.
res.status(200)
return res.json(retData)
} catch (err) {
// Write out error to error log.
wlogger.error('Error in blockbook.js/utxosSingle().', err)
return _this.errorHandler(err, res)
}
}
// POST handler for bulk queries on address utxos
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 blockbook.js/utxosBulk 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}`)
_this.utxosFromBlockbook(address)
)
// Wait for all parallel Insight requests to return.
const result = await _this.axios.all(addresses)
// Return the array of retrieved address information.
res.status(200)
return res.json(result)
} catch (err) {
wlogger.error('Error in blockbook.js/utxosBulk().', err)
return _this.errorHandler(err, res)
}
}
// Query the Blockbook Node API for transactions on a single TXID.
// Returns a Promise.
async transactionsFromBlockbook (txid) {
try {
// console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
const path = `${_this.BLOCKBOOKPATH.txPath}${txid}`
// console.log(`path: ${path}`)
// Query the Blockbook Node API.
const options = {
method: 'get',
baseURL: path
}
const axiosResponse = await _this.axios.request(options)
const retPromise = axiosResponse.data
// console.log(`retData: ${util.inspect(retData)}`)
return retPromise
} catch (err) {
// Dev Note: Do not log error messages here. Throw them instead and let the
// parent function handle it.
wlogger.debug('Error in blockbook.js/transactionsFromBlockbook()')
throw err
}
}
// GET handler for single transaction details.
async txSingle (req, res, next) {
try {
const txid = req.params.txid
if (!txid || txid === '') {
res.status(400)
return res.json({ error: 'txid can not be empty' })
}
// Reject if address is an array.
if (Array.isArray(txid)) {
res.status(400)
return res.json({
error: 'txid can not be an array. Use POST for bulk upload.'
})
}
// TODO: Add regex comparison of txid to ensure it's valid.
if (txid.length !== 64) {
res.status(400)
return res.json({
error: `txid must be of length 64 (not ${txid.length})`
})
}
wlogger.debug('Executing blockbook/txSingle with this txid: ', txid)
// Query the Blockbook Node API.
const retData = await _this.transactionsFromBlockbook(txid)
// Return the retrieved address information.
res.status(200)
return res.json(retData)
} catch (err) {
// Write out error to error log.
wlogger.error('Error in blockbook.js/txSingle().', err)
return _this.errorHandler(err, res)
}
}
// POST handler for bulk queries on tx details
async txBulk (req, res, next) {
try {
let txids = req.body.txids
// const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0
// Reject if txids is not an array.
if (!Array.isArray(txids)) {
res.status(400)
return res.json({
error: 'txids need to be an array. Use GET for single address.'
})
}
// 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({
error: 'Array too large.'
})
}
wlogger.debug('Executing blockbook.js/txBulk with these txids: ', txids)
// Validate each element in the txids array.
for (let i = 0; i < txids.length; i++) {
const thisTxid = txids[i]
if (!thisTxid || thisTxid === '') {
res.status(400)
return res.json({ error: 'txid can not be empty' })
}
// TODO: Add regex comparison of txid to ensure it's valid.
if (thisTxid.length !== 64) {
res.status(400)
return res.json({
error: `txid must be of length 64 (not ${thisTxid.length})`
})
}
}
// Loops through each address and creates an array of Promises, querying
// Insight API in parallel.
txids = txids.map(async (txid, index) =>
// console.log(`address: ${address}`)
_this.transactionsFromBlockbook(txid)
)
// Wait for all parallel Insight requests to return.
const result = await _this.axios.all(txids)
// Return the array of retrieved address information.
res.status(200)
return res.json(result)
} catch (err) {
wlogger.error('Error in blockbook.js/txBulk().', err)
return _this.errorHandler(err, res)
}
}
}
module.exports = Blockbook
+1 -1
View File
@@ -181,7 +181,7 @@ class Encryption {
publicKey: 'not found' publicKey: 'not found'
}) })
} catch (err) { } catch (err) {
console.log('Error in encryption.js/getPublicKey().', err) // console.log('Error in encryption.js/getPublicKey().', err)
wlogger.error('Error in encryption.js/getPublicKey().', err) wlogger.error('Error in encryption.js/getPublicKey().', err)
return _this.errorHandler(err, res) return _this.errorHandler(err, res)
+47
View File
@@ -53,6 +53,7 @@ class Blockchain {
this.router.post('/getTxOutProof', this.getTxOutProofBulk) this.router.post('/getTxOutProof', this.getTxOutProofBulk)
this.router.get('/verifyTxOutProof/:proof', this.verifyTxOutProofSingle) this.router.get('/verifyTxOutProof/:proof', this.verifyTxOutProofSingle)
this.router.post('/verifyTxOutProof', this.verifyTxOutProofBulk) this.router.post('/verifyTxOutProof', this.verifyTxOutProofBulk)
this.router.post('/getBlock', this.getBlock)
} }
root (req, res, next) { root (req, res, next) {
@@ -937,6 +938,52 @@ class Blockchain {
return _this.errorHandler(err, res) return _this.errorHandler(err, res)
} }
} }
/**
* @api {post} /blockchain/getBlock/ Get block details
* @apiName getBlock
* @apiGroup Blockchain
* @apiDescription Returns block details
*
* @apiExample Example usage:
* curl "https://api.fullstack.cash/v4/blockchain/getblock/" -X POST -H "Content-Type: application/json" --data-binary '{"blockhash":"000000000000000002a5fe0bdd6e3f04342a975c0f55e57f97e73bb90041676b","verbosity":0 }'
*
* @apiParam {String} blockhash Block hash (required)
* @apiParam {Number} verbosity Default 1 (optional)
*
*/
async getBlock (req, res, next) {
try {
// Validate input parameter
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 (!blockhash || blockhash === '') {
res.status(400)
return res.json({ error: 'blockhash can not be empty' })
}
// Axios options
const options = _this.routeUtils.getAxiosOptions()
options.data.id = 'getblock'
options.data.method = 'getblock'
options.data.params = [blockhash, verbosity]
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/getBlock()', err)
return _this.errorHandler(err, res)
}
}
} }
module.exports = Blockchain module.exports = Blockchain
+200
View File
@@ -1823,4 +1823,204 @@ describe('#BlockchainRouter', () => {
assert.equal(result.length, 2) assert.equal(result.length, 2)
}) })
}) })
describe('#getBlock()', () => {
it('returns proper error when downstream service stalls', async () => {
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNABORTED' })
req.body.blockhash =
'000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700'
const result = await uut.getBlock(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNREFUSED' })
req.body.blockhash =
'000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700'
const result = await uut.getBlock(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should throw 400 if blockhash is empty', async () => {
const result = await uut.getBlock(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ['error'])
assert.include(result.error, 'blockhash can not be empty')
})
it('should return block info with verbosity 0', async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.axios, 'request')
.resolves({ data: { result: mockData.mockBlockInfo.verbosity0 } })
}
req.body.blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
req.body.verbosity = 0
const result = await uut.getBlock(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isString(result)
})
it('should return block info with verbosity 1', async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.axios, 'request')
.resolves({ data: { result: mockData.mockBlockInfo.verbosity1 } })
}
req.body.blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
req.body.verbosity = 1
const result = await uut.getBlock(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'hash', 'hash property expected')
assert.property(
result,
'confirmations',
'confirmations property expected'
)
assert.property(result, 'size', 'size property expected')
assert.property(result, 'height', 'height property expected')
assert.property(result, 'version', 'version property expected')
assert.property(result, 'versionHex', 'versionHex property expected')
assert.property(result, 'merkleroot', 'merkleroot property expected')
assert.property(result, 'tx', 'tx property expected')
assert.property(result, 'time', 'time property expected')
assert.property(result, 'mediantime', 'mediantime property expected')
assert.property(result, 'nonce', 'nonce property expected')
assert.property(result, 'bits', 'bits property expected')
assert.property(result, 'difficulty', 'difficulty property expected')
assert.property(result, 'chainwork', 'chainwork property expected')
assert.property(result, 'nTx', 'nTx property expected')
assert.property(
result,
'previousblockhash',
'previousblockhash property expected'
)
assert.property(
result,
'nextblockhash',
'nextblockhash property expected'
)
})
it('should return block info with verbosity 2', async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.axios, 'request')
.resolves({ data: { result: mockData.mockBlockInfo.verbosity1 } })
}
req.body.blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
req.body.verbosity = 2
const result = await uut.getBlock(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'hash', 'hash property expected')
assert.property(
result,
'confirmations',
'confirmations property expected'
)
assert.property(result, 'size', 'size property expected')
assert.property(result, 'height', 'height property expected')
assert.property(result, 'version', 'version property expected')
assert.property(result, 'versionHex', 'versionHex property expected')
assert.property(result, 'merkleroot', 'merkleroot property expected')
assert.property(result, 'tx', 'tx property expected')
assert.property(result, 'time', 'time property expected')
assert.property(result, 'mediantime', 'mediantime property expected')
assert.property(result, 'nonce', 'nonce property expected')
assert.property(result, 'bits', 'bits property expected')
assert.property(result, 'difficulty', 'difficulty property expected')
assert.property(result, 'chainwork', 'chainwork property expected')
assert.property(result, 'nTx', 'nTx property expected')
assert.property(
result,
'previousblockhash',
'previousblockhash property expected'
)
assert.property(
result,
'nextblockhash',
'nextblockhash property expected'
)
})
it('should return block info without verbosity especified', async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.axios, 'request')
.resolves({ data: { result: mockData.mockBlockInfo.verbosity1 } })
}
req.body.blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
const result = await uut.getBlock(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'hash', 'hash property expected')
assert.property(
result,
'confirmations',
'confirmations property expected'
)
assert.property(result, 'size', 'size property expected')
assert.property(result, 'height', 'height property expected')
assert.property(result, 'version', 'version property expected')
assert.property(result, 'versionHex', 'versionHex property expected')
assert.property(result, 'merkleroot', 'merkleroot property expected')
assert.property(result, 'tx', 'tx property expected')
assert.property(result, 'time', 'time property expected')
assert.property(result, 'mediantime', 'mediantime property expected')
assert.property(result, 'nonce', 'nonce property expected')
assert.property(result, 'bits', 'bits property expected')
assert.property(result, 'difficulty', 'difficulty property expected')
assert.property(result, 'chainwork', 'chainwork property expected')
assert.property(result, 'nTx', 'nTx property expected')
assert.property(
result,
'previousblockhash',
'previousblockhash property expected'
)
assert.property(
result,
'nextblockhash',
'nextblockhash property expected'
)
})
})
}) })
+82 -2
View File
@@ -285,7 +285,86 @@ const mockAncestors = [
'e68dad4a7292105cfa84fcaef7f99e5d4f2ece9613ca625d4d2ebf61efa84118', 'e68dad4a7292105cfa84fcaef7f99e5d4f2ece9613ca625d4d2ebf61efa84118',
'fe94caf5da672be3772d2304a6272eb8bc3d3f5cb4a886f39b7981e1485cf74b' 'fe94caf5da672be3772d2304a6272eb8bc3d3f5cb4a886f39b7981e1485cf74b'
] ]
const mockBlockInfo = {
verbosity0: '20000000',
verbosity1: {
hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
confirmations: 1,
size: 3725,
strippedsize: 3725,
weight: 3725,
height: 6725,
version: 1,
versionHex: '00000000',
merkleroot: 'xxxx',
tx: [
'2afb8264508e2bf3e5288ccad01ed2ab766745b6b9747666b519d59212012c01',
'18f40b1ae56bba3fa1934b737fbe46ed8d5ca40fa9aed95073eeb5a119530cd3',
'349720d878547752607a69eb19e330592fee271fb5376cdfd811bee423558ed8',
'35571c80e7d0e9247b467454ef147d1d5833c775bc2d4164b1bebd4c1f69164f',
'480937e8efacdafeeb97d401ff0dd9ea8e8ddb27244cefa67a03621315bdb0e6',
'523327469d0b90c0de9a905c2fe6e227278fc5b55b9d9911ca151e1c26647065',
'61de4af971d94dbc741762f21dcef08b74b62863a56a1cb3496becdd8d47a858',
'69f70a288403b5bba23030ccd05d2e5cb00394620fdae3b050cff9432cc590ce',
'94472a90fbdba2eb2cba68308415181faedf3585c66b89efcbdb927a0c10ba23',
'bc6f781f9e2f2df460f89995c5e1c7224e48ccbe1aa2a575961f3f5330259864',
'f2d945a79bec5454a9ab4d570a150d81124daa308f1c81106a977d6413476944'
],
time: 111,
mediantime: 111,
nonce: 111,
bits: '1d00ffff',
difficulty: 99.999,
chainwork: 'xxxx',
nTx: 1,
previousblockhash:
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
nextblockhash:
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
},
verbosity2: {
hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
confirmations: 1,
size: 3725,
strippedsize: 3725,
weight: 3725,
height: 6725,
version: 1,
versionHex: '00000000',
merkleroot: 'xxxx',
tx: [
{
hex:
'01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000',
txid:
'0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098',
hash:
'0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098',
size: 134,
version: 1,
locktime: 0,
vin: [],
vout: [],
blockhash:
'00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048',
confirmations: 581882,
time: 1231469665,
blocktime: 1231469665
}
],
time: 111,
mediantime: 111,
nonce: 111,
bits: '1d00ffff',
difficulty: 99.999,
chainwork: 'xxxx',
nTx: 1,
previousblockhash:
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
nextblockhash:
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
}
}
module.exports = { module.exports = {
mockBlockHash, mockBlockHash,
mockBlockchainInfo, mockBlockchainInfo,
@@ -296,5 +375,6 @@ module.exports = {
mockBlockHeader, mockBlockHeader,
mockTxOut, mockTxOut,
mockTxOutProof, mockTxOutProof,
mockAncestors mockAncestors,
mockBlockInfo
} }