mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-21 16:52:04 -07:00
Merge pull request #124 from Permissionless-Software-Foundation/ct-unstable
fix(blockbook): Removing final piece of blockbook
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user