feat(insight): removing insight API as a downstream service

This commit is contained in:
Chris Troutner
2019-12-06 16:12:29 -08:00
parent fb6a54f8f5
commit 0c31036940
11 changed files with 5 additions and 1508 deletions
-8
View File
@@ -32,10 +32,6 @@ const slpV3 = require("./routes/v3/slp")
const xpubV3 = require("./routes/v3/xpub")
const bitcoreV3 = require("./routes/v3/bitcore")
const blockbookV3 = require("./routes/v3/blockbook")
const insightV3 = require("./routes/v3/insight")
const insightBlockV3 = require("./routes/v3/insight/block")
const insightTranactionV3 = require("./routes/v3/insight/transaction")
const insightAddressV3 = require("./routes/v3/insight/address")
require("dotenv").config()
@@ -98,10 +94,6 @@ app.use(`/${v3prefix}/` + `slp`, slpV3.router)
app.use(`/${v3prefix}/` + `xpub`, xpubV3.router)
app.use(`/${v3prefix}/` + `bitcore`, bitcoreV3.router)
app.use(`/${v3prefix}/` + `blockbook`, blockbookV3.router)
app.use(`/${v3prefix}/` + `insight`, insightV3.router)
app.use(`/${v3prefix}/` + `insight/block`, insightBlockV3.router)
app.use(`/${v3prefix}/` + `insight/transaction`, insightTranactionV3.router)
app.use(`/${v3prefix}/` + `insight/address`, insightAddressV3.router)
// catch 404 and forward to error handler
app.use((req, res, next) => {
-1
View File
@@ -5,7 +5,6 @@
"use strict"
const express = require("express")
const requestUtils = require("./services/requestUtils")
const axios = require("axios")
const routeUtils = require("./route-utils")
const wlogger = require("../../util/winston-logging")
-1
View File
@@ -5,7 +5,6 @@
"use strict"
const express = require("express")
const requestUtils = require("./services/requestUtils")
const axios = require("axios")
const routeUtils = require("./route-utils")
const wlogger = require("../../util/winston-logging")
-891
View File
@@ -1,891 +0,0 @@
/*
Address route
*/
"use strict"
const express = require("express")
const requestUtils = require("../services/requestUtils")
const axios = require("axios")
const routeUtils = require("../route-utils")
const wlogger = require("../../../util/winston-logging")
//const router = express.Router()
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("@chris.troutner/bch-js")
const bchjs = new BCHJS()
// Use the default (and max) page size of 1000
// https://github.com/bitpay/insight-api#notes-on-upgrading-from-v03
const PAGE_SIZE = 1000
// Connect the route endpoints to their handler functions.
router.get("/", root)
router.get("/details/:address", detailsSingle)
router.post("/details", detailsBulk)
router.post("/utxo", utxoBulk)
router.get("/utxo/:address", utxoSingle)
router.post("/unconfirmed", unconfirmedBulk)
router.get("/unconfirmed/:address", unconfirmedSingle)
router.get("/transactions/:address", transactionsSingle)
router.post("/transactions", transactionsBulk)
router.get("/fromXPub/:xpub", fromXPubSingle)
// Root API endpoint. Simply acknowledges that it exists.
function root(req, res, next) {
return res.json({ status: "address" })
}
// Query the Insight API for details on a single BCH address.
// Returns a Promise.
async function detailsFromInsight(thisAddress, currentPage = 0) {
try {
const addr = bchjs.Address.toLegacyAddress(thisAddress)
// const addr = bchjs.Address.toCashAddress(thisAddress)
let path = `${process.env.BITCOINCOM_BASEURL}addr/${addr}`
// Set from and to params based on currentPage and pageSize
// https://github.com/bitpay/insight-api/blob/master/README.md#notes-on-upgrading-from-v02
const from = currentPage * PAGE_SIZE
const to = from + PAGE_SIZE
path = `${path}?from=${from}&to=${to}`
// console.log(`insight path: ${path}`)
// Query the Insight server.
const axiosResponse = await axios.get(path)
const retData = axiosResponse.data
// console.log(`retData: ${util.inspect(retData)}`)
// Calculate pagesTotal from response
const pagesTotal = Math.ceil(retData.txApperances / PAGE_SIZE)
// Append different address formats to the return data.
retData.legacyAddress = bchjs.Address.toLegacyAddress(retData.addrStr)
retData.cashAddress = bchjs.Address.toCashAddress(retData.addrStr)
delete retData.addrStr
// Append pagination information to the return data.
retData.currentPage = currentPage
retData.pagesTotal = pagesTotal
return retData
} catch (err) {
// Dev Note: Do not log error messages here. Throw them instead and let the
// parent function handle it.
throw err
}
}
// POST handler for bulk queries on address details
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details
/**
* @api {post} /address/details Get Address details bulk.
* @apiName Address details bulk
* @apiGroup Address
* @apiDescription Returns the details of multiple addresses.
*
*
* @apiExample Example usage:
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/details
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/details
*
*/
async function detailsBulk(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 (!routeUtils.validateArraySize(req, addresses)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
})
}
wlogger.debug(`Executing address/details 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 {
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 = 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) =>
detailsFromInsight(address, currentPage)
)
// Wait for all parallel Insight requests to return.
const result = await axios.all(addresses)
// Return the array of retrieved address information.
res.status(200)
return res.json(result)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
//logger.error(`Error in detailsBulk(): `, err)
wlogger.error(`Error in address.ts/detailsBulk().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
// GET handler for single address details
/**
* @api {get} insight/address/details/{address} Get Address details single.
* @apiName Address details single
* @apiGroup Address
* @apiDescription Returns the details of an address including balance.
*
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/details/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
*
*
*/
async function detailsSingle(req, res, next) {
try {
const address = req.params.address
const currentPage = req.query.page ? parseInt(req.query.page, 10) : 0
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 address/detailsSingle with this address: `,
address
)
// Ensure the input is a valid BCH address.
try {
var legacyAddr = 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 = 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 Insight API.
const retData = await detailsFromInsight(address, currentPage)
// Return the retrieved address information.
res.status(200)
return res.json(retData)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in address.ts/detailsSingle: `, err)
wlogger.error(`Error in address.ts/detailsSingle().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
// Retrieve UTXO data from the Insight API
async function utxoFromInsight(thisAddress) {
try {
const addr = bchjs.Address.toLegacyAddress(thisAddress)
// const addr = bchjs.Address.toCashAddress(thisAddress)
const path = `${process.env.BITCOINCOM_BASEURL}addr/${addr}/utxo`
// Query the Insight server.
const response = await axios.get(path)
// Append different address formats to the return data.
const retData = {
utxos: Array,
legacyAddress: String,
cashAddress: String,
scriptPubKey: String
}
if (response.data.length && response.data[0].scriptPubKey) {
const spk = response.data[0].scriptPubKey
retData.scriptPubKey = spk
}
retData.legacyAddress = bchjs.Address.toLegacyAddress(thisAddress)
retData.cashAddress = bchjs.Address.toCashAddress(thisAddress)
retData.utxos = response.data.map(utxo => {
delete utxo.address
delete utxo.scriptPubKey
return utxo
})
//console.log(`utxoFromInsight 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.
throw err
}
}
/**
* @api {post} /address/utxo Get Address utxos bulk.
* @apiName Address utxos bulk.
* @apiGroup Address
* @apiDescription Returns the utxo of multiple addresses.
*
*
* @apiExample Example usage:
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/utxo
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/utxo
*
*/
// Retrieve UTXO information for an address.
async function utxoBulk(req, res, next) {
try {
let addresses = req.body.addresses
// Reject if address is not an array.
if (!Array.isArray(addresses)) {
res.status(400)
return res.json({ error: "addresses needs to be an array" })
}
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, addresses)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
})
}
// 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 {
bchjs.Address.toLegacyAddress(thisAddress)
} catch (er) {
//if (er.message.includes("Unsupported address format"))
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 = 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.`
})
}
}
wlogger.debug(
`Executing address/utxoBulk with these addresses: `,
addresses
)
// Loops through each address and creates an array of Promises, querying
// Insight API in parallel.
addresses = addresses.map(async (address, index) =>
utxoFromInsight(address)
)
// Wait for all parallel Insight requests to return.
const result = await axios.all(addresses)
res.status(200)
return res.json(result)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error(`Error in address.ts/utxoBulk().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
// GET handler for single address details
/**
* @api {get} /insight/address/utxo/{address} Get Address utxos single.
* @apiName Address utxos single.
* @apiGroup Address
* @apiDescription Returns the list of utxo for an address.
*
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/utxo/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
*
*
*/
async function utxoSingle(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 address/utxoSingle with this address: `, address)
// Ensure the input is a valid BCH address.
try {
var legacyAddr = 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 = 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 Insight API.
const retData = await utxoFromInsight(address)
// Return the array of retrieved address information.
res.status(200)
return res.json(retData)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error(`Error in address.ts/utxoSingle().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
/**
* @api {post} /address/utxo Get Unconfirmed transactions bulk.
* @apiName Unconfirmed transactions bulk
* @apiGroup Address
* @apiDescription Returns the list of unconfirmed transactions for an array of addresses.
*
*
* @apiExample Example usage:
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/unconfirmed
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/unconfirmed
*
*/
// Retrieve any unconfirmed TX information for a given address.
async function unconfirmedBulk(req, res, next) {
try {
const addresses = req.body.addresses
// Reject if address is not an array.
if (!Array.isArray(addresses)) {
res.status(400)
return res.json({ error: "addresses needs to be an array" })
}
wlogger.debug(`Executing address/utxo with these addresses: `, addresses)
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, addresses)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
})
}
// 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 {
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 = 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.`
})
}
}
// Collect an array of promises.
const promises = addresses.map(address => utxoFromInsight(address))
// Wait for all parallel Insight requests to return.
const result = await axios.all(promises)
// Loop through each result
const finalResult = result.map(elem => {
//console.log(`elem: ${util.inspect(elem)}`)
// Filter out confirmed transactions.
const unconfirmedUtxos = elem.utxos.filter(
utxo => utxo.confirmations === 0
)
elem.utxos = unconfirmedUtxos
return elem
})
// Return the array of retrieved address information.
res.status(200)
return res.json(finalResult)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error(`Error in address.ts/unconfirmedBulk().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
/**
* @api {get} /insight/address/utxo/{address} Get Unconfirmed transactions single.
* @apiName Unconfirmed transactions single.
* @apiGroup Address
* @apiDescription Returns the list of unconfirmed transactions for an address.
*
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/unconfirmed/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
*
*
*/
// GET handler. Retrieve any unconfirmed TX information for a given address.
async function unconfirmedSingle(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 address/utxoSingle with this address: `, address)
// Ensure the input is a valid BCH address.
try {
var legacyAddr = 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 = 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 Insight API.
const retData = await utxoFromInsight(address)
//console.log(`retData: ${JSON.stringify(retData,null,2)}`)
// Loop through each returned UTXO.
const unconfirmedUTXOs = []
for (let j = 0; j < retData.utxos.length; j++) {
const thisUtxo = retData.utxos[j]
// Only interested in UTXOs with no confirmations.
if (thisUtxo.confirmations === 0) unconfirmedUTXOs.push(thisUtxo)
}
retData.utxos = unconfirmedUTXOs
// Return the array of retrieved address information.
res.status(200)
return res.json(retData)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error(`Error in address.ts/unconfirmedSingle().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
// Retrieve transaction data from the Insight API
async function transactionsFromInsight(thisAddress, currentPage = 0) {
try {
const path = `${process.env.BITCOINCOM_BASEURL}txs/?address=${thisAddress}&pageNum=${currentPage}`
// Query the Insight server.
const response = await axios.get(path)
// Append different address formats to the return data.
const retData = response.data
retData.legacyAddress = bchjs.Address.toLegacyAddress(thisAddress)
retData.cashAddress = bchjs.Address.toCashAddress(thisAddress)
retData.currentPage = currentPage
return retData
} catch (err) {
// Dev Note: Do not log error messages here. Throw them instead and let the
// parent function handle it.
throw err
}
}
/**
* @api {post} /address/transactions Get Transactions bulk.
* @apiName Transactions bulk.
* @apiGroup Address
* @apiDescription Returns the list of transactions for an array of addresses.
*
*
* @apiExample Example usage:
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/transactions
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/transactions
*
*/
// Get an array of TX information for a given address.
async function transactionsBulk(req, res, next) {
try {
let addresses = req.body.addresses
const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0
// Reject if address is not an array.
if (!Array.isArray(addresses)) {
res.status(400)
return res.json({ error: "addresses needs to be an array" })
}
wlogger.debug(`Executing address/utxo with these addresses: `, addresses)
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, addresses)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
})
}
// 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 {
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 = 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.`
})
}
}
// Loop through each address and collect an array of promises.
addresses = addresses.map(async (address, index) =>
transactionsFromInsight(address, currentPage)
)
// Wait for all parallel Insight requests to return.
const result = await axios.all(addresses)
// Return the array of retrieved address information.
res.status(200)
return res.json(result)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error(`Error in address.ts/transactionsBulk().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
/**
* @api {get} /insight/address/transactions/{address} Get Transactions single.
* @apiName Transactions single.
* @apiGroup Address
* @apiDescription Returns the list of transactions for an address.
*
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/transactions/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
*
*
*/
// GET handler. Retrieve any unconfirmed TX information for a given address.
async function transactionsSingle(req, res, next) {
try {
const address = req.params.address
const currentPage = req.query.page ? parseInt(req.query.page, 10) : 0
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 address/transactionsSingle with this address: `,
address
)
// Ensure the input is a valid BCH address.
try {
var legacyAddr = 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 = 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 Insight API.
const retData = await transactionsFromInsight(address, currentPage)
//console.log(`retData: ${JSON.stringify(retData,null,2)}`)
// Return the array of retrieved address information.
res.status(200)
return res.json(retData)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error(`Error in address.ts/transactionsSingle().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
/**
* @api {get} /insight/address/fromXPub/{xpub} Get FromXPub
* @apiName FromXPub
* @apiGroup Address
* @apiDescription Returns an address for an extended public key and hdpath.
*
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/fromXPub/xpub661MyMwAqRbcG4CnhNYoK1r1TKLwQQ1UdC3LHoWFK61rsnzh7Hx35qQ9Z53ucYcE5WvA7GEDXhqqKjSY2e6Y8n7WNVLYHpXCuuX945VPuYn" -H "accept: application/json"
*
*
*/
async function fromXPubSingle(req, res, next) {
try {
const xpub = req.params.xpub
const hdPath = req.query.hdPath ? req.query.hdPath : "0"
if (!xpub || xpub === "") {
res.status(400)
return res.json({ error: "xpub can not be empty" })
}
// Reject if xpub is an array.
if (Array.isArray(xpub)) {
res.status(400)
return res.json({
error: "xpub can not be an array. Use POST for bulk upload."
})
}
wlogger.debug(`Executing address/fromXPub with this xpub: `, xpub)
const cashAddr = bchjs.Address.fromXPub(xpub, hdPath)
const legacyAddr = bchjs.Address.toLegacyAddress(cashAddr)
res.status(200)
return res.json({
cashAddress: cashAddr,
legacyAddress: legacyAddr
})
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error(`Error in address.ts/fromXPubSingle().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
module.exports = {
router,
testableComponents: {
root,
detailsBulk,
detailsSingle,
utxoBulk,
utxoSingle,
unconfirmedBulk,
unconfirmedSingle,
transactionsBulk,
transactionsSingle,
fromXPubSingle
}
}
-324
View File
@@ -1,324 +0,0 @@
"use strict"
const express = require("express")
const requestUtils = require("../services/requestUtils")
const axios = require("axios")
const bitbox = require("../services/bitbox")
const wlogger = require("../../../util/winston-logging")
const routeUtils = require("../route-utils")
// Used for processing error messages before sending them to the user.
const util = require("util")
util.inspect.defaultOptions = { depth: 3 }
const router = express.Router()
//const BitboxHTTP = bitbox.getInstance()
router.get("/", root)
router.get("/detailsByHash/:hash", detailsByHashSingle)
router.post("/detailsByHash", detailsByHashBulk)
router.get("/detailsByHeight/:height", detailsByHeightSingle)
router.post("/detailsByHeight", detailsByHeightBulk)
function root(req, res, next) {
return res.json({ status: "block" })
}
/**
* @api {get} /block/detailsByHash/{hash} Get Block details single.
* @apiName Block details single
* @apiGroup Block
* @apiDescription Returns details about a single block by hash.
*
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/block/detailsByHash/000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201" -H "accept: application/json"
*
*
*/
// Call the insight server to get block details based on the hash.
async function detailsByHashSingle(req, res, next) {
try {
const hash = req.params.hash
// Reject if hash is empty
if (!hash || hash === "") {
res.status(400)
return res.json({ error: "hash must not be empty" })
}
const response = await axios.get(
`${process.env.BITCOINCOM_BASEURL}block/${hash}`
)
//console.log(`response.data: ${JSON.stringify(response.data,null,2)}`)
const parsed = response.data
return res.json(parsed)
} catch (error) {
//console.log(`error object: ${util.inspect(error)}`)
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(error)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
if (error.response && error.response.status === 404) {
res.status(404)
return res.json({ error: "Not Found" })
}
// Write out error to error log.
//logger.error(`Error in block/detailsByHash: `, error)
wlogger.error(`Error in block.ts/detailsByHashSingle().`, error)
res.status(500)
return res.json({ error: util.inspect(error) })
}
}
/**
* @api {post} /block/detailsByHash Get Block details bulk.
* @apiName Block details bulk
* @apiGroup Block
* @apiDescription Returns retails about multiple blocks by hashes.
*
*
* @apiExample Example usage:
* curl -d '{"hashes":["0000000000000000040e83398a79a16390897f0d18c92bada6350a19a32ec984","000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"]}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHash"
* curl -d '{"hashes":["0000000000000000040e83398a79a16390897f0d18c92bada6350a19a32ec984","000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"], "from": 1, "to": 5}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHash"
*
*/
async function detailsByHashBulk(req, res, next) {
try {
const hashes = req.body.hashes
//console.log(`hashes: ${JSON.stringify(hashes, null, 2)}`)
// Reject if hashes is not an array.
if (!Array.isArray(hashes)) {
res.status(400)
return res.json({
error: "hashes needs to be an array. Use GET for single address."
})
}
// 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.`
})
}
// Validate each hash in the array.
for (let i = 0; i < hashes.length; i++) {
const thisHash = hashes[i]
if (thisHash.length !== 64) {
res.status(400)
return res.json({
error: `Invalid hash. Double check your hash is valid: ${thisHash}`
})
}
}
// Loop through each hash and creates an array of promises
const axiosPromises = hashes.map(async hash =>
axios.get(`${process.env.BITCOINCOM_BASEURL}block/${hash}`)
)
// Wait for all parallel promises to return.
const axiosResult = await axios.all(axiosPromises)
// Extract the data component from the axios response.
const result = axiosResult.map(x => x.data)
//console.log(`result: ${util.inspect(result)}`)
res.status(200)
return res.json(result)
} catch (error) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(error)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
if (error.response && error.response.status === 404) {
res.status(404)
return res.json({ error: "Not Found" })
}
// Write out error to error log.
//logger.error(`Error in block/detailsByHash: `, error)
wlogger.error(`Error in block.ts/detailsByHashBulk().`, error)
res.status(500)
return res.json({ error: util.inspect(error) })
}
}
/**
* @api {get} /block/detailsByHeight/{hash} Get Block details single by height.
* @apiName Block details single by height
* @apiGroup Block
* @apiDescription Returns Details about a single block by height.
*
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/block/block/detailsByHeight/500000" -H "accept: application/json"
*
*
*/
// Call the Full Node to get block hash based on height, then call the Insight
// server to get details from that hash.
async function detailsByHeightSingle(req, res, next) {
try {
const height = req.params.height
// Reject if id is empty
if (!height || height === "") {
res.status(400)
return res.json({ error: "height must not be empty" })
}
const {
BitboxHTTP,
username,
password,
requestConfig
} = routeUtils.setEnvVars()
requestConfig.data.id = "getblockhash"
requestConfig.data.method = "getblockhash"
requestConfig.data.params = [parseInt(height)]
const response = await BitboxHTTP(requestConfig)
const hash = response.data.result
//console.log(`response.data: ${util.inspect(response.data)}`)
// Call detailsByHashSingle now that the hash has been retrieved.
req.params.hash = hash
return detailsByHashSingle(req, res, next)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in control/getInfo: `, error)
wlogger.error(`Error in block.ts/detailsByHeightSingle().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
/**
* @api {post} /block/detailsByHash Get Block details bulk by height.
* @apiName Block details bulk by height
* @apiGroup Block
* @apiDescription Returns Bulk details about blocks by heights.
*
*
* @apiExample Example usage:
* curl -d '{"heights":[499000,500000]}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHeight"
* curl -d '{"heights":[499000,500000], "from": 1, "to": 5}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHeight"
*
*/
async function detailsByHeightBulk(req, res, next) {
try {
const heights = req.body.heights
// Reject if heights is not an array.
if (!Array.isArray(heights)) {
res.status(400)
return res.json({
error: "heights needs to be an array. Use GET for single height."
})
}
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, heights)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
})
}
wlogger.debug(`Executing detailsByHeight with these heights: `, heights)
// Validate each element in the address array.
for (let i = 0; i < heights.length; i++) {
const thisHeight = heights[i]
// Reject if id is empty
if (!thisHeight || thisHeight === "") {
res.status(400)
return res.json({ error: "height must not be empty" })
}
}
const {
BitboxHTTP,
username,
password,
requestConfig
} = routeUtils.setEnvVars()
// Loop through each height and creates an array of requests to call in parallel
const promises = heights.map(async height => {
requestConfig.data.id = "getblockhash"
requestConfig.data.method = "getblockhash"
requestConfig.data.params = [parseInt(height)]
const response = await BitboxHTTP(requestConfig)
const hash = response.data.result
const axiosResult = await axios.get(
`${process.env.BITCOINCOM_BASEURL}block/${hash}`
)
return axiosResult.data
})
// Wait for all parallel Insight requests to return.
const result = await axios.all(promises)
res.status(200)
return res.json(result)
} catch (error) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(error)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
if (error.response && error.response.status === 404) {
res.status(404)
return res.json({ error: "Not Found" })
}
// Write out error to error log.
//logger.error(`Error in block/detailsByHash: `, error)
wlogger.error(`Error in block.ts/detailsByHeightBulk().`, error)
res.status(500)
return res.json({ error: util.inspect(error) })
}
}
module.exports = {
router,
testableComponents: {
root,
detailsByHashSingle,
detailsByHashBulk,
detailsByHeightSingle,
detailsByHeightBulk
}
}
-38
View File
@@ -1,38 +0,0 @@
/*
Insight API route
A parent route that encompases the other libraries.
*/
"use strict"
const express = require("express")
const requestUtils = require("../services/requestUtils")
const axios = require("axios")
const routeUtils = require("../route-utils")
const wlogger = require("../../../util/winston-logging")
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("@chris.troutner/bch-js")
//const bchjs = new BCHJS()
//const BITCORE_URL = process.env.BITCORE_URL
// Connect the route endpoints to their handler functions.
router.get("/", root)
// Root API endpoint. Simply acknowledges that it exists.
function root(req, res, next) {
return res.json({ status: "address" })
}
module.exports = {
router,
testableComponents: {
root
}
}
-210
View File
@@ -1,210 +0,0 @@
"use strict"
const express = require("express")
const router = express.Router()
const axios = require("axios")
const routeUtils = require("../route-utils")
const wlogger = require("../../../util/winston-logging")
const BCHJS = require("@chris.troutner/bch-js")
const bchjs = new BCHJS()
// Used to convert error messages to strings, to safely pass to users.
const util = require("util")
util.inspect.defaultOptions = { depth: 3 }
// Manipulates and formats the raw data comming from Insight API.
const processInputs = tx => {
// Add legacy and cashaddr to tx vin
if (tx.vin) {
tx.vin.forEach(vin => {
if (!vin.coinbase) {
vin.value = vin.valueSat
const address = vin.addr
if (address) {
vin.legacyAddress = bchjs.Address.toLegacyAddress(address)
vin.cashAddress = bchjs.Address.toCashAddress(address)
delete vin.addr
}
delete vin.valueSat
delete vin.doubleSpentTxID
}
})
}
// Add legacy and cashaddr to tx vout
if (tx.vout) {
tx.vout.forEach(vout => {
// Overwrite value string with value in satoshis
//vout.value = parseFloat(vout.value) * 100000000
if (vout.scriptPubKey) {
if (vout.scriptPubKey.addresses) {
const cashAddrs = []
vout.scriptPubKey.addresses.forEach(addr => {
const cashAddr = bchjs.Address.toCashAddress(addr)
cashAddrs.push(cashAddr)
})
vout.scriptPubKey.cashAddrs = cashAddrs
}
}
})
}
}
router.get("/", root)
router.post("/details", detailsBulk)
router.get("/details/:txid", detailsSingle)
function root(req, res, next) {
return res.json({ status: "transaction" })
}
// Retrieve transaction data from the Insight API
// This function is also used by the SLP route library.
async function transactionsFromInsight(txid) {
try {
const path = `${process.env.BITCOINCOM_BASEURL}tx/${txid}`
// Query the Insight server.
const response = await axios.get(path)
//console.log(`Insight output: ${JSON.stringify(response.data, null, 2)}`)
// Parse the data.
const parsed = response.data
if (parsed) processInputs(parsed)
return parsed
} catch (err) {
// Dev Note: Do not log error messages here. Throw them instead and let the
// parent function handle it.
throw err
}
}
/**
* @api {post} /transaction/details Get Transaction details bulk.
* @apiName Transaction details bulk
* @apiGroup Transaction
* @apiDescription Returns Bulk Details about a transaction.
*
*
* @apiExample Example usage:
* curl -X POST "https://mainnet.bchjs.cash/v3/insight/transaction/details" -H "Content-Type: application/json" -d "{"txids":["a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1","5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e"]}"
*
*/
async function detailsBulk(req, res, next) {
try {
const txids = req.body.txids
// Reject if address is not an array.
if (!Array.isArray(txids)) {
res.status(400)
return res.json({ error: "txids needs to be an array" })
}
// 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 transaction/details with these txids: `, txids)
// Collect an array of promises
const promises = txids.map(
async txid => await transactionsFromInsight(txid)
)
// Wait for all parallel promises to return.
const result = await Promise.all(promises)
// Return the array of retrieved transaction information.
res.status(200)
return res.json(result)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
wlogger.error(`Error in transactions.ts/detailsBulk().`, err)
//console.log(`Error in transaction details: `, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
// GET handler. Retrieve any unconfirmed TX information for a given address.
/**
* @api {get} insight/transaction/details/{txid} Get Transaction details single.
* @apiName Transaction details single
* @apiGroup Transaction
* @apiDescription Returns the details about a single transaction.
*
*
* @apiExample Example usage:
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/transaction/details/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
*
*
*/
async function detailsSingle(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."
})
}
wlogger.debug(
`Executing transaction.ts/detailsSingle with this txid: `,
txid
)
// Query the Insight API.
const retData = await transactionsFromInsight(txid)
//console.log(`retData: ${JSON.stringify(retData,null,2)}`)
// Return the array of retrieved address information.
res.status(200)
return res.json(retData)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error(`Error in transactions.ts/detailsSingle().`, err)
res.status(500)
return res.json({ error: util.inspect(err) })
}
}
module.exports = {
router,
transactionsFromInsight,
testableComponents: {
root,
detailsBulk,
detailsSingle
}
}
-11
View File
@@ -1,11 +0,0 @@
"use strict"
const axios = require("axios")
const BitboxHTTP = axios.create({
baseURL: process.env.RPC_BASEURL
})
const getInstance = () => BitboxHTTP
module.exports = getInstance
-20
View File
@@ -1,20 +0,0 @@
"use strict"
const username = process.env.RPC_USERNAME
const password = process.env.RPC_PASSWORD
const getRequestConfig = (method, params) => ({
method: "post",
auth: {
username,
password
},
data: {
jsonrpc: "1.0",
id: method,
method,
params
}
})
module.exports = getRequestConfig
+5 -3
View File
@@ -27,7 +27,8 @@ const password = process.env.RPC_PASSWORD
// https://gist.github.com/christroutner/fc717ca704dec3dded8b52fae387eab2
const SLPDB_PASS = process.env.SLPDB_PASS ? process.env.SLPDB_PASS : "BITBOX"
const transactions = require("./insight/transaction")
// const transactions = require("./insight/transaction")
const rawtransactions = require("./full-node/rawtransactions")
// Setup REST and TREST URLs used by slpjs
// Dev note: this allows for unit tests to mock the URL.
@@ -1201,8 +1202,9 @@ async function txDetails(req, res, next) {
// console.log(`formatted: ${JSON.stringify(formatted,null,2)}`)
// Get information on the transaction from Insight API.
const retData = await transactions.transactionsFromInsight(txid)
// console.log(`retData: ${JSON.stringify(retData,null,2)}`)
// const retData = await transactions.transactionsFromInsight(txid)
const retData = await rawtransactions.getRawTransactionsFromNode(txid, true)
// console.log(`retData: ${JSON.stringify(retData, null, 2)}`)
// Return both the tx data from Insight and the formatted token information.
const response = {
-1
View File
@@ -5,7 +5,6 @@
"use strict"
const express = require("express")
const requestUtils = require("./services/requestUtils")
const axios = require("axios")
const routeUtils = require("./route-utils")
const wlogger = require("../../util/winston-logging")