fix(linting): Switching to standard linter. Fixed linting errors

This commit is contained in:
Chris Troutner
2020-02-16 06:38:25 -08:00
parent 7a6a033eb9
commit bb1bac5ada
48 changed files with 4547 additions and 3702 deletions
+62 -61
View File
@@ -1,41 +1,41 @@
"use strict"
'use strict'
const express = require("express")
const express = require('express')
// Middleware
// const { routeRateLimit } = require("./middleware/route-ratelimit")
const RateLimits = require("./middleware/route-ratelimit")
const RateLimits = require('./middleware/route-ratelimit')
const rateLimits = new RateLimits()
const path = require("path")
const logger = require("morgan")
const wlogger = require("./util/winston-logging")
const cookieParser = require("cookie-parser")
const bodyParser = require("body-parser")
const basicAuth = require("express-basic-auth")
const helmet = require("helmet")
const debug = require("debug")("rest-cloud:server")
const http = require("http")
const cors = require("cors")
const path = require('path')
const logger = require('morgan')
const wlogger = require('./util/winston-logging')
const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser')
// const basicAuth = require("express-basic-auth")
const helmet = require('helmet')
const debug = require('debug')('rest-cloud:server')
const http = require('http')
const cors = require('cors')
// Auth and rate limiting middleware libraries.
const AuthMW = require("./middleware/auth")
const jwtAuth = require("./middleware/jwt-auth")
const AuthMW = require('./middleware/auth')
const jwtAuth = require('./middleware/jwt-auth')
// v3
const healthCheckV3 = require("./routes/v3/health-check")
const BlockchainV3 = require("./routes/v3/full-node/blockchain")
const controlV3 = require("./routes/v3/full-node/control")
const miningV3 = require("./routes/v3/full-node/mining")
const networkV3 = require("./routes/v3/full-node/network")
const rawtransactionsV3 = require("./routes/v3/full-node/rawtransactions")
const utilV3 = require("./routes/v3/util")
const slpV3 = require("./routes/v3/slp")
const xpubV3 = require("./routes/v3/xpub")
const blockbookV3 = require("./routes/v3/blockbook")
const Ninsight = require("./routes/v3/ninsight")
const healthCheckV3 = require('./routes/v3/health-check')
const BlockchainV3 = require('./routes/v3/full-node/blockchain')
const controlV3 = require('./routes/v3/full-node/control')
const miningV3 = require('./routes/v3/full-node/mining')
const networkV3 = require('./routes/v3/full-node/network')
const rawtransactionsV3 = require('./routes/v3/full-node/rawtransactions')
const utilV3 = require('./routes/v3/util')
const slpV3 = require('./routes/v3/slp')
const xpubV3 = require('./routes/v3/xpub')
const blockbookV3 = require('./routes/v3/blockbook')
const Ninsight = require('./routes/v3/ninsight')
require("dotenv").config()
require('dotenv').config()
// Instantiate route libraries.
const blockchainV3 = new BlockchainV3()
@@ -44,28 +44,29 @@ const app = express()
app.locals.env = process.env
//app.use(swStats.getMiddleware({ swaggerSpec: apiSpec }))
// app.use(swStats.getMiddleware({ swaggerSpec: apiSpec }))
app.use(helmet())
app.use(cors())
app.enable("trust proxy")
app.enable('trust proxy')
// view engine setup
app.set("views", path.join(__dirname, "views"))
app.set("view engine", "jade")
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'jade')
// Mount the docs
app.use("/docs", express.static(`${__dirname}/../docs`))
app.use('/docs', express.static(`${__dirname}/../docs`))
// Log each request to the console with IP addresses.
// app.use(logger("dev"))
const morganFormat = `:remote-addr :remote-user :method :url :status :response-time ms - :res[content-length] :user-agent`
const morganFormat =
':remote-addr :remote-user :method :url :status :response-time ms - :res[content-length] :user-agent'
app.use(logger(morganFormat))
// Log the same data to the winston logs.
const logStream = {
write: function(message, encoding) {
write: function (message, encoding) {
wlogger.info(`request: ${message}`)
}
}
@@ -74,10 +75,10 @@ app.use(logger(morganFormat, { stream: logStream }))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, "public")))
app.use(express.static(path.join(__dirname, 'public')))
// const v2prefix = "v2"
const v3prefix = "v3"
const v3prefix = 'v3'
// Inspect the header for a JWT token.
app.use(`/${v3prefix}/`, jwtAuth.getTokenFromHeaders)
@@ -92,24 +93,24 @@ app.use(`/${v3prefix}/`, auth.mw())
// app.use(`/${v3prefix}/`, rateLimits.routeRateLimit)
app.use(`/${v3prefix}/`, rateLimits.rateLimitByResource)
app.use(`/${v3prefix}/` + `health-check`, healthCheckV3)
app.use(`/${v3prefix}/` + `blockchain`, blockchainV3.router)
app.use(`/${v3prefix}/` + `control`, controlV3.router)
app.use(`/${v3prefix}/` + `mining`, miningV3.router)
app.use(`/${v3prefix}/` + `network`, networkV3)
app.use(`/${v3prefix}/` + `rawtransactions`, rawtransactionsV3.router)
app.use(`/${v3prefix}/` + `util`, utilV3.router)
app.use(`/${v3prefix}/` + `slp`, slpV3.router)
app.use(`/${v3prefix}/` + `xpub`, xpubV3.router)
app.use(`/${v3prefix}/` + `blockbook`, blockbookV3.router)
app.use(`/${v3prefix}/` + 'health-check', healthCheckV3)
app.use(`/${v3prefix}/` + 'blockchain', blockchainV3.router)
app.use(`/${v3prefix}/` + 'control', controlV3.router)
app.use(`/${v3prefix}/` + 'mining', miningV3.router)
app.use(`/${v3prefix}/` + 'network', networkV3)
app.use(`/${v3prefix}/` + 'rawtransactions', rawtransactionsV3.router)
app.use(`/${v3prefix}/` + 'util', utilV3.router)
app.use(`/${v3prefix}/` + 'slp', slpV3.router)
app.use(`/${v3prefix}/` + 'xpub', xpubV3.router)
app.use(`/${v3prefix}/` + 'blockbook', blockbookV3.router)
const ninsight = new Ninsight()
app.use(`/${v3prefix}/` + `ninsight`, ninsight.router)
app.use(`/${v3prefix}/` + 'ninsight', ninsight.router)
// catch 404 and forward to error handler
app.use((req, res, next) => {
const err = {
message: "Not Found",
message: 'Not Found',
status: 404
}
@@ -122,7 +123,7 @@ app.use((err, req, res, next) => {
// set locals, only providing error in development
res.locals.message = err.message
res.locals.error = req.app.get("env") === "development" ? err : {}
res.locals.error = req.app.get('env') === 'development' ? err : {}
// render the error page
res.status(status)
@@ -135,8 +136,8 @@ app.use((err, req, res, next) => {
/**
* Get port from environment and store in Express.
*/
const port = normalizePort(process.env.PORT || "3000")
app.set("port", port)
const port = normalizePort(process.env.PORT || '3000')
app.set('port', port)
console.log(`bch-api started on port ${port}`)
/**
@@ -149,8 +150,8 @@ const server = http.createServer(app)
*/
server.listen(port)
server.on("error", onError)
server.on("listening", onListening)
server.on('error', onError)
server.on('listening', onListening)
// Set the time before a timeout error is generated. This impacts testing and
// the handling of timeout errors. Is 10 seconds too agressive?
@@ -160,7 +161,7 @@ server.setTimeout(30 * 1000)
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
function normalizePort (val) {
const port = parseInt(val, 10)
if (isNaN(port)) {
@@ -179,18 +180,18 @@ function normalizePort(val) {
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== "listen") throw error
function onError (error) {
if (error.syscall !== 'listen') throw error
const bind = typeof port === "string" ? `Pipe ${port}` : `Port ${port}`
const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`
// handle specific listen errors with friendly messages
switch (error.code) {
case "EACCES":
case 'EACCES':
console.error(`${bind} requires elevated privileges`)
process.exit(1)
break
case "EADDRINUSE":
case 'EADDRINUSE':
console.error(`${bind} is already in use`)
process.exit(1)
break
@@ -203,9 +204,9 @@ function onError(error) {
* Event listener for HTTP server "listening" event.
*/
function onListening() {
function onListening () {
const addr = server.address()
const bind = typeof addr === "string" ? `pipe ${addr}` : `port ${addr.port}`
const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`
debug(`Listening on ${bind}`)
}
//
+21 -21
View File
@@ -19,45 +19,45 @@
is set and passed to the route-ratelimits.ts middleware.
*/
"use strict"
'use strict'
const passport = require("passport")
const BasicStrategy = require("passport-http").BasicStrategy
const AnonymousStrategy = require("passport-anonymous")
const wlogger = require("../util/winston-logging")
const passport = require('passport')
const BasicStrategy = require('passport-http').BasicStrategy
const AnonymousStrategy = require('passport-anonymous')
const wlogger = require('../util/winston-logging')
// Used for debugging and iterrogating JS objects.
const util = require("util")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
let _this
// let _this
// Set default rate limit value for testing
const PRO_PASSes = process.env.PRO_PASS ? process.env.PRO_PASS : "BITBOX"
const PRO_PASSES = process.env.PRO_PASS ? process.env.PRO_PASS : 'BITBOX'
// Convert the pro-tier password string into an array split by ':'.
const PRO_PASS = PRO_PASSes.split(":")
const PRO_PASS = PRO_PASSES.split(':')
//wlogger.verbose(`PRO_PASS set to: ${PRO_PASS}`)
// wlogger.verbose(`PRO_PASS set to: ${PRO_PASS}`)
// Auth Middleware
class AuthMW {
constructor() {
_this = this
constructor () {
// _this = this
// Initialize passport for 'anonymous' authentication.
passport.use(new AnonymousStrategy())
// Initialize passport for 'basic' authentication.
passport.use(
new BasicStrategy({ passReqToCallback: true }, function(
new BasicStrategy({ passReqToCallback: true }, function (
req,
username,
password,
done
) {
//console.log(`req: ${util.inspect(req)}`)
//console.log(`username: ${username}`)
//console.log(`password: ${password}`)
// console.log(`req: ${util.inspect(req)}`)
// console.log(`username: ${username}`)
// console.log(`password: ${password}`)
// Create the req.locals property if it does not yet exist.
if (!req.locals) {
@@ -72,8 +72,8 @@ class AuthMW {
req.locals.proLimit = false
// Evaluate the username and password and set the rate limit accordingly.
//if (username === "BITBOX" && password === PRO_PASS) {
if (username === "BITBOX") {
// if (username === "BITBOX" && password === PRO_PASS) {
if (username === 'BITBOX') {
for (let i = 0; i < PRO_PASS.length; i++) {
const thisPass = PRO_PASS[i]
@@ -87,7 +87,7 @@ class AuthMW {
}
}
//console.log(`req.locals: ${util.inspect(req.locals)}`)
// console.log(`req.locals: ${util.inspect(req.locals)}`)
return done(null, true)
})
@@ -95,8 +95,8 @@ class AuthMW {
}
// Middleware called by the route.
mw() {
return passport.authenticate(["basic", "anonymous"], {
mw () {
return passport.authenticate(['basic', 'anonymous'], {
session: false
})
}
+4 -4
View File
@@ -5,7 +5,7 @@
If found, will populate req.locals.jwtToken with the JWT token.
*/
"use strict"
'use strict'
// This function searches the header for the a JWT token in the authorization header.
// If one is found, this middleware passes the JWT token through the
@@ -18,8 +18,8 @@ const getTokenFromHeaders = (req, res, next) => {
const authStr = req.headers.authorization
// If the header is proceeded by the word 'Token'
if (authStr.split(" ")[0] === "Token") {
const token = authStr.split(" ")[1]
if (authStr.split(' ')[0] === 'Token') {
const token = authStr.split(' ')[1]
// console.log(`JWT found: ${token}`)
@@ -36,7 +36,7 @@ const getTokenFromHeaders = (req, res, next) => {
}
}
} catch (err) {
console.log(`Error in getTokenFromHeaders: `, err)
console.log('Error in getTokenFromHeaders: ', err)
}
next()
+59 -62
View File
@@ -1,28 +1,28 @@
"use strict"
'use strict'
const express = require("express")
const RateLimit = require("express-rate-limit")
const axios = require("axios")
// const express = require('express')
const RateLimit = require('express-rate-limit')
const axios = require('axios')
const wlogger = require("../util/winston-logging")
const wlogger = require('../util/winston-logging')
const jwt = require("jsonwebtoken")
const KeyEncoder = require("key-encoder").default
const keyEncoder = new KeyEncoder("secp256k1")
const jwt = require('jsonwebtoken')
const KeyEncoder = require('key-encoder').default
const keyEncoder = new KeyEncoder('secp256k1')
// Redis
const redisOptions = {
enableOfflineQueue: false,
port: process.env.REDIS_PORT ? process.env.REDIS_PORT : 6379,
host: process.env.REDIS_HOST ? process.env.REDIS_HOST : "127.0.0.1"
host: process.env.REDIS_HOST ? process.env.REDIS_HOST : '127.0.0.1'
}
console.log(`redisOptions: ${JSON.stringify(redisOptions, null, 2)}`)
const Redis = require("ioredis")
const Redis = require('ioredis')
const redisClient = new Redis(redisOptions)
// Rate limiter middleware lib.
const { RateLimiterRedis } = require("rate-limiter-flexible")
const { RateLimiterRedis } = require('rate-limiter-flexible')
const rateLimitOptions = {
storeClient: redisClient,
points: 100, // Number of points
@@ -32,7 +32,7 @@ const rateLimitOptions = {
// This hard-coded value is temporary. It will be swapped out with an environment
// variable when moved to production.
const publicKey =
"03e6c358092a459f7da9420de770eef3e16cf3c9c54a3d3d14ac2d7f0b82af4d7d"
'03e6c358092a459f7da9420de770eef3e16cf3c9c54a3d3d14ac2d7f0b82af4d7d'
// Set max requests per minute
const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS
@@ -48,7 +48,7 @@ const uniqueRateLimits = {}
let _this
class RateLimits {
constructor() {
constructor () {
_this = this
this.jwt = jwt
@@ -57,7 +57,7 @@ class RateLimits {
// Used to disconnect from the Redis DB.
// Called by unit tests so that node.js thread doesn't live forever.
closeRedis() {
closeRedis () {
redisClient.disconnect()
}
@@ -79,7 +79,7 @@ class RateLimits {
will be downgraded to 0 on-the-fly. Indexer endpoints will effectively be
downgraded to the anonymous access tier.
*/
async routeRateLimit(req, res, next) {
async routeRateLimit (req, res, next) {
// Disable rate limiting if 0 passed from RATE_LIMIT_MAX_REQUESTS
if (maxRequests === 0) return next()
@@ -87,7 +87,7 @@ class RateLimits {
if (!req.locals) {
req.locals = {
// default values
jwtToken: "",
jwtToken: '',
proLimit: false,
apiLevel: 0
}
@@ -95,9 +95,9 @@ class RateLimits {
// Warn if JWT_AUTH_SERVER env var is not set.
const authServer = process.env.JWT_AUTH_SERVER
if (!authServer || authServer === "") {
if (!authServer || authServer === '') {
console.warn(
"JWT_AUTH_SERVER env var is not set. JWT tokens not being evaluated."
'JWT_AUTH_SERVER env var is not set. JWT tokens not being evaluated.'
)
} else {
// If a JWT token is passed in, validate it and enable pro-tier rate limits
@@ -129,7 +129,7 @@ class RateLimits {
}
// Current route
const rateLimitTier = req.locals.proLimit ? "PRO" : "BASIC"
const rateLimitTier = req.locals.proLimit ? 'PRO' : 'BASIC'
const path = req.baseUrl + req.path
// Create a unique string as a route identifier.
@@ -138,10 +138,10 @@ class RateLimits {
req.method +
req.locals.apiLevel + // Generates new rate limit when user upgrades JWT token.
path
.split("/")
.split('/')
.slice(0, 4)
.join("/")
//console.log(`route identifier: ${JSON.stringify(route, null, 2)}`)
.join('/')
// console.log(`route identifier: ${JSON.stringify(route, null, 2)}`)
// console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`)
@@ -166,8 +166,8 @@ class RateLimits {
windowMs: 60 * 1000, // 1 minute window
delayMs: 0, // disable delaying - full speed until the max limit is reached
max: PRO_RPM, // start blocking after this many requests per minute
handler: function(req, res) {
//console.log(`pro-tier rate-handler triggered.`)
handler: function (req, res) {
// console.log(`pro-tier rate-handler triggered.`)
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
@@ -188,8 +188,8 @@ class RateLimits {
windowMs: 60 * 1000, // 1 minute window
delayMs: 0, // disable delaying - full speed until the max limit is reached
max: maxRequests, // start blocking after maxRequests
handler: function(req, res) {
//console.log(`freemium rate-handler triggered.`)
handler: function (req, res) {
// console.log(`freemium rate-handler triggered.`)
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
@@ -200,7 +200,7 @@ class RateLimits {
}
}
//console.log(`calling uniqueRateLimits() on this route: ${route}`)
// console.log(`calling uniqueRateLimits() on this route: ${route}`)
// Call rate limit for this route
uniqueRateLimits[route](req, res, next)
@@ -212,7 +212,7 @@ class RateLimits {
// It does fine-grane analysis on the data coming from the auth servers and
// uses its output to adjust rate limits on-the-fly based on the users
// permission level.
evalUserPermissioins(req, authData) {
evalUserPermissioins (req, authData) {
// console.log(`authData: ${JSON.stringify(authData, null, 2)}`)
// Return object with default values
@@ -224,9 +224,9 @@ class RateLimits {
// if apiLevel = 0 (free tier), then return the default values.
if (retObj.apiLevel === 0) return retObj
const level20Routes = ["insight", "bitcore", "blockbook"]
const level20Routes = ['insight', 'bitcore', 'blockbook']
const locals = req.locals
// const locals = req.locals
// console.log(`locals: ${JSON.stringify(locals, null, 2)}`)
const url = req.url
// console.log(`url: ${JSON.stringify(url, null, 2)}`)
@@ -249,7 +249,7 @@ class RateLimits {
// This is the new rate limit function that uses the rate-limiter-flexible npm
// library. It uses fine-grain rate limiting based on the resources being
// consumed.
async rateLimitByResource(req, res, next) {
async rateLimitByResource (req, res, next) {
try {
let userId
let decoded = {}
@@ -258,7 +258,7 @@ class RateLimits {
if (!req.locals) {
req.locals = {
// default values
jwtToken: "",
jwtToken: '',
proLimit: false,
apiLevel: 0
}
@@ -267,10 +267,10 @@ class RateLimits {
// Decode the JWT token if one exists.
if (req.locals.jwtToken) {
const jwtOptions = {
algorithms: ["ES256"]
algorithms: ['ES256']
}
const pemPublicKey = keyEncoder.encodePublic(publicKey, "raw", "pem")
const pemPublicKey = keyEncoder.encodePublic(publicKey, 'raw', 'pem')
// Validate the JWT token.
decoded = _this.jwt.verify(
@@ -282,7 +282,7 @@ class RateLimits {
userId = decoded.id
} else {
wlogger.debug(`No JWT token found!`)
wlogger.debug('No JWT token found!')
}
// Code here for the rate limiter is adapted from this example:
@@ -292,7 +292,7 @@ class RateLimits {
const resource = _this.getResource(req.url)
wlogger.debug(`resource: ${resource}`)
let key = userId ? userId : req.ip
let key = userId || req.ip
// const pointsToConsume = userId ? 1 : 30
decoded.resource = resource
@@ -316,7 +316,7 @@ class RateLimits {
})
}
} catch (err) {
wlogger.error(`Error in route-ratelimit.js/newRateLimit(): `, err)
wlogger.error('Error in route-ratelimit.js/newRateLimit(): ', err)
// throw err
}
@@ -325,7 +325,7 @@ class RateLimits {
// Calculates the points consumed, based on the jwt information and the route
// requested.
calcPoints(jwtInfo) {
calcPoints (jwtInfo) {
let retVal = 30 // By default, use anonymous tier.
try {
@@ -334,8 +334,8 @@ class RateLimits {
const apiLevel = jwtInfo.apiLevel
const resource = jwtInfo.resource
const level30Routes = ["insight", "bitcore", "blockbook"]
const level40Routes = ["slp"]
const level30Routes = ['insight', 'bitcore', 'blockbook']
const level40Routes = ['slp']
wlogger.debug(`apiLevel: ${apiLevel}`)
@@ -346,28 +346,25 @@ class RateLimits {
if (apiLevel >= 40) retVal = 1
// else if (apiLevel >= 10) retVal = 10
else retVal = 10
}
// Normal indexer routes
else if (level30Routes.includes(resource)) {
// Normal indexer routes
} else if (level30Routes.includes(resource)) {
if (apiLevel >= 30) retVal = 1
else retVal = 10
}
// Full node tier
else if (apiLevel >= 20) {
// Full node tier
} else if (apiLevel >= 20) {
retVal = 1
}
// Free tier, full node only.
else {
// Free tier, full node only.
} else {
retVal = 10
}
}
return retVal
} catch (err) {
wlogger.error(`Error in route-ratelimit.js/calcPoints()`)
wlogger.error('Error in route-ratelimit.js/calcPoints()')
// throw err
retVal = 30
}
@@ -379,16 +376,16 @@ class RateLimits {
// the user is requesting.
// This was created as a function so that it can be unit tested. Not sure
// what kind of variations will be seen in production.
getResource(url) {
getResource (url) {
try {
wlogger.debug(`url: ${JSON.stringify(url, null, 2)}`)
const splitUrl = url.split("/")
const splitUrl = url.split('/')
const resource = splitUrl[1]
return resource
} catch (err) {
wlogger.error(`Error in getResource().`)
wlogger.error('Error in getResource().')
throw err
}
}
@@ -397,7 +394,7 @@ class RateLimits {
// potentially be used by Bitcoin.com.
// Rather than using apiLevel, the rateLimit is explicitly recorded in the
// JWT token.
async rateLimitSimple(req, res, next) {
async rateLimitSimple (req, res, next) {
try {
let userId
let decoded = {}
@@ -406,7 +403,7 @@ class RateLimits {
if (!req.locals) {
req.locals = {
// default values
jwtToken: "",
jwtToken: '',
proLimit: false,
rateLimit: 3
}
@@ -415,10 +412,10 @@ class RateLimits {
// Decode the JWT token if one exists.
if (req.locals.jwtToken) {
const jwtOptions = {
algorithms: ["ES256"]
algorithms: ['ES256']
}
const pemPublicKey = keyEncoder.encodePublic(publicKey, "raw", "pem")
const pemPublicKey = keyEncoder.encodePublic(publicKey, 'raw', 'pem')
// Validate the JWT token.
decoded = _this.jwt.verify(
@@ -430,14 +427,14 @@ class RateLimits {
userId = decoded.id
} else {
wlogger.debug(`No JWT token found!`)
wlogger.debug('No JWT token found!')
}
// Code here for the rate limiter is adapted from this example:
// https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#authorized-and-not-authorized-users
try {
// Key for Redis key/value pair.
const key = userId ? userId : req.ip
const key = userId || req.ip
const pointsToConsume = _this.calcPoints2(decoded)
@@ -454,7 +451,7 @@ class RateLimits {
})
}
} catch (err) {
wlogger.error(`Error in route-ratelimit.js/rateLimitSimple(): `, err)
wlogger.error('Error in route-ratelimit.js/rateLimitSimple(): ', err)
// throw err
}
@@ -463,7 +460,7 @@ class RateLimits {
// Calculates the points consumed, based on the explicit rateLimit defined
// in the JWT token.
calcPoints2(jwtInfo) {
calcPoints2 (jwtInfo) {
let retVal = 30 // By default, use anonymous tier.
try {
@@ -480,7 +477,7 @@ class RateLimits {
retVal = points
}
} catch (err) {
wlogger.error(`Error in route-ratelimit.js/calcPoints2()`)
wlogger.error('Error in route-ratelimit.js/calcPoints2()')
// throw err
retVal = 30
}
+77 -73
View File
@@ -2,49 +2,49 @@
Blockbook API route
*/
"use strict"
'use strict'
const express = require("express")
const axios = require("axios")
const routeUtils = require("./route-utils")
const wlogger = require("../../util/winston-logging")
const express = require('express')
const axios = require('axios')
const routeUtils = require('./route-utils')
const wlogger = require('../../util/winston-logging')
// Library for easily switching the API paths to use different instances of
// Blockbook.
const BlockbookPath = require("../../util/blockbook-path")
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")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const BCHJS = require("@chris.troutner/bch-js")
const BCHJS = require('@chris.troutner/bch-js')
const bchjs = new BCHJS()
//const BLOCKBOOK_URL = process.env.BLOCKBOOK_URL
// const BLOCKBOOK_URL = process.env.BLOCKBOOK_URL
// Connect the route endpoints to their handler functions.
router.get("/", root)
router.get("/balance/:address", balanceSingle)
router.post("/balance", balanceBulk)
router.get("/utxos/:address", utxosSingle)
router.post("/utxos", utxosBulk)
router.get("/tx/:txid", txSingle)
router.post("/tx", txBulk)
router.get('/', root)
router.get('/balance/:address', balanceSingle)
router.post('/balance', balanceBulk)
router.get('/utxos/:address', utxosSingle)
router.post('/utxos', utxosBulk)
router.get('/tx/:txid', txSingle)
router.post('/tx', txBulk)
// Root API endpoint. Simply acknowledges that it exists.
function root(req, res, next) {
return res.json({ status: "address" })
function 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 function balanceFromBlockbook(thisAddress) {
async function balanceFromBlockbook (thisAddress) {
try {
//console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
// console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
// Convert the address to a cashaddr without a prefix.
const addr = bchjs.Address.toCashAddress(thisAddress)
@@ -55,12 +55,13 @@ async function balanceFromBlockbook(thisAddress) {
// Query the Blockbook Node API.
const axiosResponse = await axios.get(path)
const retData = axiosResponse.data
//console.log(`retData: ${util.inspect(retData)}`)
// 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
}
}
@@ -77,31 +78,32 @@ async function balanceFromBlockbook(thisAddress) {
*
*/
// GET handler for single balance
async function balanceSingle(req, res, next) {
async function balanceSingle (req, res, next) {
try {
const address = req.params.address
if (!address || address === "") {
if (!address || address === '') {
res.status(400)
return res.json({ error: "address can not be empty" })
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."
error: 'address can not be an array. Use POST for bulk upload.'
})
}
wlogger.debug(
`Executing blockbook/balanceSingle with this address: `,
'Executing blockbook/balanceSingle with this address: ',
address
)
// Ensure the input is a valid BCH address.
try {
const legacyAddr = bchjs.Address.toLegacyAddress(address)
// const legacyAddr = bchjs.Address.toLegacyAddress(address)
bchjs.Address.toLegacyAddress(address)
} catch (err) {
res.status(400)
return res.json({
@@ -114,7 +116,7 @@ async function balanceSingle(req, res, next) {
if (!networkIsValid) {
res.status(400)
return res.json({
error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.`
error: 'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
})
}
@@ -133,7 +135,7 @@ async function balanceSingle(req, res, next) {
}
// Write out error to error log.
wlogger.error(`Error in blockbook.js/balanceSingle().`, err)
wlogger.error('Error in blockbook.js/balanceSingle().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -153,16 +155,16 @@ async function balanceSingle(req, res, next) {
*
*/
// POST handler for bulk queries on address details
async function balanceBulk(req, res, next) {
async function balanceBulk (req, res, next) {
try {
let addresses = req.body.addresses
const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0
// 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."
error: 'addresses needs to be an array. Use GET for single address.'
})
}
@@ -170,12 +172,12 @@ async function balanceBulk(req, res, next) {
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.`
error: 'Array too large.'
})
}
wlogger.debug(
`Executing blockbook.js/balanceBulk with these addresses: `,
'Executing blockbook.js/balanceBulk with these addresses: ',
addresses
)
@@ -206,7 +208,7 @@ async function balanceBulk(req, res, next) {
// 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}`)
// console.log(`address: ${address}`)
balanceFromBlockbook(address)
)
@@ -224,7 +226,7 @@ async function balanceBulk(req, res, next) {
return res.json({ error: msg })
}
wlogger.error(`Error in blockbook.js/balanceBulk().`, err)
wlogger.error('Error in blockbook.js/balanceBulk().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -233,9 +235,9 @@ async function balanceBulk(req, res, next) {
// Query the Blockbook API for utxos associated with a BCH address.
// Returns a Promise.
async function utxosFromBlockbook(thisAddress) {
async function utxosFromBlockbook (thisAddress) {
try {
//console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
// console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
// Convert the address to a cashaddr without a prefix.
const addr = bchjs.Address.toCashAddress(thisAddress)
@@ -249,13 +251,13 @@ async function utxosFromBlockbook(thisAddress) {
// 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)
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
}
}
@@ -272,31 +274,32 @@ async function utxosFromBlockbook(thisAddress) {
*
*/
// GET handler for single balance
async function utxosSingle(req, res, next) {
async function utxosSingle (req, res, next) {
try {
const address = req.params.address
if (!address || address === "") {
if (!address || address === '') {
res.status(400)
return res.json({ error: "address can not be empty" })
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."
error: 'address can not be an array. Use POST for bulk upload.'
})
}
wlogger.debug(
`Executing blockbook/utxosSingle with this address: `,
'Executing blockbook/utxosSingle with this address: ',
address
)
// Ensure the input is a valid BCH address.
try {
const legacyAddr = bchjs.Address.toLegacyAddress(address)
// const legacyAddr = bchjs.Address.toLegacyAddress(address)
bchjs.Address.toLegacyAddress(address)
} catch (err) {
res.status(400)
return res.json({
@@ -309,7 +312,7 @@ async function utxosSingle(req, res, next) {
if (!networkIsValid) {
res.status(400)
return res.json({
error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.`
error: 'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
})
}
@@ -328,7 +331,7 @@ async function utxosSingle(req, res, next) {
}
// Write out error to error log.
wlogger.error(`Error in blockbook.js/utxosSingle().`, err)
wlogger.error('Error in blockbook.js/utxosSingle().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -348,16 +351,16 @@ async function utxosSingle(req, res, next) {
*
*/
// POST handler for bulk queries on address utxos
async function utxosBulk(req, res, next) {
async function utxosBulk (req, res, next) {
try {
let addresses = req.body.addresses
const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0
// 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."
error: 'addresses needs to be an array. Use GET for single address.'
})
}
@@ -365,12 +368,12 @@ async function utxosBulk(req, res, next) {
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.`
error: 'Array too large.'
})
}
wlogger.debug(
`Executing blockbook.js/utxosBulk with these addresses: `,
'Executing blockbook.js/utxosBulk with these addresses: ',
addresses
)
@@ -401,7 +404,7 @@ async function utxosBulk(req, res, next) {
// 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}`)
// console.log(`address: ${address}`)
utxosFromBlockbook(address)
)
@@ -419,7 +422,7 @@ async function utxosBulk(req, res, next) {
return res.json({ error: msg })
}
wlogger.error(`Error in blockbook.js/utxosBulk().`, err)
wlogger.error('Error in blockbook.js/utxosBulk().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -428,9 +431,9 @@ async function utxosBulk(req, res, next) {
// Query the Blockbook Node API for transactions on a single TXID.
// Returns a Promise.
async function transactionsFromBlockbook(txid) {
async function transactionsFromBlockbook (txid) {
try {
//console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
// console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
const path = `${BLOCKBOOKPATH.txPath}${txid}`
// console.log(`path: ${path}`)
@@ -438,12 +441,13 @@ async function transactionsFromBlockbook(txid) {
// Query the Blockbook Node API.
const axiosResponse = await axios.get(path)
const retPromise = axiosResponse.data
//console.log(`retData: ${util.inspect(retData)}`)
// 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
}
}
@@ -460,20 +464,20 @@ async function transactionsFromBlockbook(txid) {
*
*/
// GET handler for single transaction details.
async function txSingle(req, res, next) {
async function txSingle (req, res, next) {
try {
const txid = req.params.txid
if (!txid || txid === "") {
if (!txid || txid === '') {
res.status(400)
return res.json({ error: "txid can not be empty" })
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."
error: 'txid can not be an array. Use POST for bulk upload.'
})
}
@@ -485,7 +489,7 @@ async function txSingle(req, res, next) {
})
}
wlogger.debug(`Executing blockbook/txSingle with this txid: `, txid)
wlogger.debug('Executing blockbook/txSingle with this txid: ', txid)
// Query the Blockbook Node API.
const retData = await transactionsFromBlockbook(txid)
@@ -502,7 +506,7 @@ async function txSingle(req, res, next) {
}
// Write out error to error log.
wlogger.error(`Error in blockbook.js/txSingle().`, err)
wlogger.error('Error in blockbook.js/txSingle().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -522,16 +526,16 @@ async function txSingle(req, res, next) {
*
*/
// POST handler for bulk queries on tx details
async function txBulk(req, res, next) {
async function txBulk (req, res, next) {
try {
let txids = req.body.txids
const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0
// 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."
error: 'txids need to be an array. Use GET for single address.'
})
}
@@ -539,19 +543,19 @@ async function txBulk(req, res, next) {
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.`
error: 'Array too large.'
})
}
wlogger.debug(`Executing blockbook.js/txBulk with these txids: `, txids)
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 === "") {
if (!thisTxid || thisTxid === '') {
res.status(400)
return res.json({ error: "txid can not be empty" })
return res.json({ error: 'txid can not be empty' })
}
// TODO: Add regex comparison of txid to ensure it's valid.
@@ -566,7 +570,7 @@ async function txBulk(req, res, next) {
// 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}`)
// console.log(`address: ${address}`)
transactionsFromBlockbook(txid)
)
@@ -584,7 +588,7 @@ async function txBulk(req, res, next) {
return res.json({ error: msg })
}
wlogger.error(`Error in blockbook.js/txBulk().`, err)
wlogger.error('Error in blockbook.js/txBulk().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
+137 -135
View File
@@ -2,28 +2,28 @@
A library for interacting with the Full Node
*/
"use strict"
'use strict'
const express = require("express")
const express = require('express')
const router = express.Router()
const axios = require("axios")
const wlogger = require("../../../util/winston-logging")
const axios = require('axios')
const wlogger = require('../../../util/winston-logging')
const RouteUtils = require("../route-utils2")
const RouteUtils = require('../route-utils2')
const routeUtils = new RouteUtils()
// Used to convert error messages to strings, to safely pass to users.
const util = require("util")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const BCHJS = require("@chris.troutner/bch-js")
const BCHJS = require('@chris.troutner/bch-js')
const bchjs = new BCHJS()
let _this
class Blockchain {
constructor() {
constructor () {
_this = this
this.bchjs = bchjs
@@ -31,35 +31,35 @@ class Blockchain {
this.routeUtils = routeUtils
this.router = router
this.router.get("/", this.root)
this.router.get("/getBestBlockHash", this.getBestBlockHash)
this.router.get("/getBlockchainInfo", this.getBlockchainInfo)
this.router.get("/getBlockCount", this.getBlockCount)
this.router.get("/getBlockHeader/:hash", this.getBlockHeaderSingle)
this.router.post("/getBlockHeader", this.getBlockHeaderBulk)
this.router.get("/getChainTips", this.getChainTips)
this.router.get("/getDifficulty", this.getDifficulty)
this.router.get("/getMempoolEntry/:txid", this.getMempoolEntrySingle)
this.router.post("/getMempoolEntry", this.getMempoolEntryBulk)
this.router.get('/', this.root)
this.router.get('/getBestBlockHash', this.getBestBlockHash)
this.router.get('/getBlockchainInfo', this.getBlockchainInfo)
this.router.get('/getBlockCount', this.getBlockCount)
this.router.get('/getBlockHeader/:hash', this.getBlockHeaderSingle)
this.router.post('/getBlockHeader', this.getBlockHeaderBulk)
this.router.get('/getChainTips', this.getChainTips)
this.router.get('/getDifficulty', this.getDifficulty)
this.router.get('/getMempoolEntry/:txid', this.getMempoolEntrySingle)
this.router.post('/getMempoolEntry', this.getMempoolEntryBulk)
this.router.get(
"/getMempoolAncestors/:txid",
'/getMempoolAncestors/:txid',
this.getMempoolAncestorsSingle
)
this.router.get("/getMempoolInfo", this.getMempoolInfo)
this.router.get("/getRawMempool", this.getRawMempool)
this.router.get("/getTxOut/:txid/:n", this.getTxOut)
this.router.get("/getTxOutProof/:txid", this.getTxOutProofSingle)
this.router.post("/getTxOutProof", this.getTxOutProofBulk)
this.router.get("/verifyTxOutProof/:proof", this.verifyTxOutProofSingle)
this.router.post("/verifyTxOutProof", this.verifyTxOutProofBulk)
this.router.get('/getMempoolInfo', this.getMempoolInfo)
this.router.get('/getRawMempool', this.getRawMempool)
this.router.get('/getTxOut/:txid/:n', this.getTxOut)
this.router.get('/getTxOutProof/:txid', this.getTxOutProofSingle)
this.router.post('/getTxOutProof', this.getTxOutProofBulk)
this.router.get('/verifyTxOutProof/:proof', this.verifyTxOutProofSingle)
this.router.post('/verifyTxOutProof', this.verifyTxOutProofBulk)
}
root(req, res, next) {
return res.json({ status: "blockchain" })
root (req, res, next) {
return res.json({ status: 'blockchain' })
}
// DRY error handler.
errorHandler(err, res) {
errorHandler (err, res) {
// Attempt to decode the error message.
const { msg, status } = _this.routeUtils.decodeError(err)
if (msg) {
@@ -83,12 +83,12 @@ class Blockchain {
*
* @apiSuccess {String} bestBlockHash 000000000000000002bc884334336d99c9a9c616670a9244c6a8c1fc35aa91a1
*/
async getBestBlockHash(req, res, next) {
async getBestBlockHash (req, res, next) {
try {
// Axios options
const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getbestblockhash"
options.data.method = "getbestblockhash"
options.data.id = 'getbestblockhash'
options.data.method = 'getbestblockhash'
options.data.params = []
const response = await _this.axios.request(options)
@@ -97,7 +97,7 @@ class Blockchain {
return res.json(response.data.result)
} catch (err) {
// Write out error to error log.
wlogger.error("Error in blockchain.ts/getBestBlockHash().", err)
wlogger.error('Error in blockchain.ts/getBestBlockHash().', err)
return _this.errorHandler(err, res)
}
@@ -128,12 +128,12 @@ class Blockchain {
* @apiSuccess {Object} object.softforks.reject
* @apiSuccess {String} object.softforks.reject.status true
*/
async getBlockchainInfo(req, res, next) {
async getBlockchainInfo (req, res, next) {
try {
// Axios options
const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getblockchaininfo"
options.data.method = "getblockchaininfo"
options.data.id = 'getblockchaininfo'
options.data.method = 'getblockchaininfo'
options.data.params = []
const response = await _this.axios.request(options)
@@ -141,7 +141,7 @@ class Blockchain {
return res.json(response.data.result)
} catch (err) {
// Write out error to error log.
wlogger.error("Error in blockchain.ts/getBlockchainInfo().", err)
wlogger.error('Error in blockchain.ts/getBlockchainInfo().', err)
return _this.errorHandler(err, res)
}
@@ -158,12 +158,12 @@ class Blockchain {
*
* @apiSuccess {Number} bestBlockCount 587665
*/
async getBlockCount(req, res, next) {
async getBlockCount (req, res, next) {
try {
// Axios options
const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getblockcount"
options.data.method = "getblockcount"
options.data.id = 'getblockcount'
options.data.method = 'getblockcount'
options.data.params = []
const response = await _this.axios.request(options)
@@ -172,7 +172,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getBlockCount().", err)
wlogger.error('Error in blockchain.ts/getBlockCount().', err)
return _this.errorHandler(err, res)
}
@@ -208,22 +208,23 @@ class Blockchain {
* @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523"
* @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3"
*/
async getBlockHeaderSingle(req, res, next) {
async getBlockHeaderSingle (req, res, next) {
try {
let verbose = false
if (req.query.verbose && req.query.verbose.toString() === "true")
if (req.query.verbose && req.query.verbose.toString() === 'true') {
verbose = true
}
const hash = req.params.hash
if (!hash || hash === "") {
if (!hash || hash === '') {
res.status(400)
return res.json({ error: "hash can not be empty" })
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.id = 'getblockheader'
options.data.method = 'getblockheader'
options.data.params = [hash, verbose]
const response = await _this.axios.request(options)
@@ -232,7 +233,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getBlockHeaderSingle().", err)
wlogger.error('Error in blockchain.ts/getBlockHeaderSingle().', err)
return _this.errorHandler(err, res)
}
@@ -268,7 +269,7 @@ class Blockchain {
* @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523"
* @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3"
*/
async getBlockHeaderBulk(req, res, next) {
async getBlockHeaderBulk (req, res, next) {
try {
const hashes = req.body.hashes
const verbose = req.body.verbose ? req.body.verbose : false
@@ -276,7 +277,7 @@ class Blockchain {
if (!Array.isArray(hashes)) {
res.status(400)
return res.json({
error: "hashes needs to be an array. Use GET for single hash."
error: 'hashes needs to be an array. Use GET for single hash.'
})
}
@@ -284,12 +285,12 @@ class Blockchain {
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."
error: 'Array too large.'
})
}
wlogger.debug(
"Executing blockchain/getBlockHeaderBulk with these hashes: ",
'Executing blockchain/getBlockHeaderBulk with these hashes: ',
hashes
)
@@ -308,11 +309,11 @@ class Blockchain {
// 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.id = 'getblockheader'
options.data.method = 'getblockheader'
options.data.params = [hash, verbose]
return await _this.axios.request(options)
return _this.axios.request(options)
})
const axiosResult = await _this.axios.all(promises)
@@ -325,7 +326,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getBlockHeaderBulk().", err)
wlogger.error('Error in blockchain.ts/getBlockHeaderBulk().', err)
return _this.errorHandler(err, res)
}
@@ -342,13 +343,13 @@ class Blockchain {
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getChainTips" -H "accept: application/json"
*
*/
async getChainTips(req, res, next) {
async getChainTips (req, res, next) {
try {
// Axios options
const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getchaintips"
options.data.method = "getchaintips"
options.data.id = 'getchaintips'
options.data.method = 'getchaintips'
options.data.params = []
const response = await _this.axios.request(options)
@@ -356,7 +357,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getChainTips().", err)
wlogger.error('Error in blockchain.ts/getChainTips().', err)
return _this.errorHandler(err, res)
}
@@ -373,13 +374,13 @@ class Blockchain {
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getDifficulty" -H "accept: application/json"
*
*/
async getDifficulty(req, res, next) {
async getDifficulty (req, res, next) {
try {
// Axios options
const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getdifficulty"
options.data.method = "getdifficulty"
options.data.id = 'getdifficulty'
options.data.method = 'getdifficulty'
options.data.params = []
const response = await _this.axios.request(options)
@@ -388,7 +389,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getDifficulty().", err)
wlogger.error('Error in blockchain.ts/getDifficulty().', err)
return _this.errorHandler(err, res)
}
@@ -405,20 +406,20 @@ class Blockchain {
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getMempoolEntry/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
*
*/
async getMempoolEntrySingle(req, res, next) {
async getMempoolEntrySingle (req, res, next) {
try {
// Validate input parameter
const txid = req.params.txid
if (!txid || txid === "") {
if (!txid || txid === '') {
res.status(400)
return res.json({ error: "txid can not be empty" })
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.id = 'getmempoolentry'
options.data.method = 'getmempoolentry'
options.data.params = [txid]
const response = await _this.axios.request(options)
@@ -427,7 +428,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getMempoolEntrySingle().", err)
wlogger.error('Error in blockchain.ts/getMempoolEntrySingle().', err)
return _this.errorHandler(err, res)
}
@@ -442,14 +443,14 @@ class Blockchain {
* @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) {
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."
error: 'txids needs to be an array. Use GET for single txid.'
})
}
@@ -457,12 +458,12 @@ class Blockchain {
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."
error: 'Array too large.'
})
}
wlogger.debug(
"Executing blockchain/getMempoolEntry with these txids: ",
'Executing blockchain/getMempoolEntry with these txids: ',
txids
)
@@ -472,7 +473,7 @@ class Blockchain {
if (txid.length !== 64) {
res.status(400)
return res.json({ error: "This is not a txid" })
return res.json({ error: 'This is not a txid' })
}
}
@@ -481,11 +482,11 @@ class Blockchain {
// 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.id = 'getmempoolentry'
options.data.method = 'getmempoolentry'
options.data.params = [txid]
return await _this.axios.request(options)
return _this.axios.request(options)
})
const axiosResult = await _this.axios.all(promises)
@@ -498,7 +499,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getMempoolEntryBulk().", err)
wlogger.error('Error in blockchain.ts/getMempoolEntryBulk().', err)
return _this.errorHandler(err, res)
}
@@ -516,13 +517,13 @@ class Blockchain {
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getMempoolAncestors/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
*
*/
async getMempoolAncestorsSingle(req, res, next) {
async getMempoolAncestorsSingle (req, res, next) {
try {
// Validate input parameter
const txid = req.params.txid
if (!txid || txid === "") {
if (!txid || txid === '') {
res.status(400)
return res.json({ error: "txid can not be empty" })
return res.json({ error: 'txid can not be empty' })
}
let verbose = req.params.verbose
@@ -531,8 +532,8 @@ class Blockchain {
// Axios options
const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getmempoolancestors"
options.data.method = "getmempoolancestors"
options.data.id = 'getmempoolancestors'
options.data.method = 'getmempoolancestors'
options.data.params = [txid, verbose]
const response = await _this.axios.request(options)
@@ -542,7 +543,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getMempoolAncestorsSingle().", err)
wlogger.error('Error in blockchain.ts/getMempoolAncestorsSingle().', err)
return _this.errorHandler(err, res)
}
@@ -558,13 +559,13 @@ class Blockchain {
* curl -X GET https://mainnet.bchjs.cash/v3/getMempoolInfo -H "accept: application/json"
*
*/
async getMempoolInfo(req, res, next) {
async getMempoolInfo (req, res, next) {
try {
// Axios options
const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getmempoolinfo"
options.data.method = "getmempoolinfo"
options.data.id = 'getmempoolinfo'
options.data.method = 'getmempoolinfo'
options.data.params = []
const response = await _this.axios.request(options)
@@ -572,7 +573,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getMempoolInfo().", err)
wlogger.error('Error in blockchain.ts/getMempoolInfo().', err)
return _this.errorHandler(err, res)
}
@@ -602,16 +603,16 @@ class Blockchain {
* @apiParam {Boolean} verbose Return verbose data
*
*/
async getRawMempool(req, res, next) {
async getRawMempool (req, res, next) {
try {
// Axios options
const options = _this.routeUtils.getAxiosOptions()
let verbose = false
if (req.query.verbose && req.query.verbose === "true") verbose = true
if (req.query.verbose && req.query.verbose === 'true') verbose = true
options.data.id = "getrawmempool"
options.data.method = "getrawmempool"
options.data.id = 'getrawmempool'
options.data.method = 'getrawmempool'
options.data.params = [verbose]
const response = await _this.axios.request(options)
@@ -620,7 +621,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getRawMempool().", err)
wlogger.error('Error in blockchain.ts/getRawMempool().', err)
return _this.errorHandler(err, res)
}
@@ -641,32 +642,33 @@ class Blockchain {
*
*/
// Returns details about an unspent transaction output.
async getTxOut(req, res, next) {
async getTxOut (req, res, next) {
try {
// Validate input parameter
const txid = req.params.txid
if (!txid || txid === "") {
if (!txid || txid === '') {
res.status(400)
return res.json({ error: "txid can not be empty" })
return res.json({ error: 'txid can not be empty' })
}
let n = req.params.n
if (n === undefined || n === "") {
if (n === undefined || n === '') {
res.status(400)
return res.json({ error: "n can not be empty" })
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
let includeMempool = false
if (req.query.includeMempool && req.query.includeMempool === 'true') {
includeMempool = true
}
// Axios options
const options = _this.routeUtils.getAxiosOptions()
options.data.id = "gettxout"
options.data.method = "gettxout"
options.data.params = [txid, n, include_mempool]
options.data.id = 'gettxout'
options.data.method = 'gettxout'
options.data.params = [txid, n, includeMempool]
// console.log(`requestConfig: ${JSON.stringify(requestConfig, null, 2)}`)
@@ -676,7 +678,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getTxOut().", err)
wlogger.error('Error in blockchain.ts/getTxOut().', err)
return _this.errorHandler(err, res)
}
@@ -694,20 +696,20 @@ class Blockchain {
* @apiParam {String} txid Transaction id (required)
*
*/
async getTxOutProofSingle(req, res, next) {
async getTxOutProofSingle (req, res, next) {
try {
// Validate input parameter
const txid = req.params.txid
if (!txid || txid === "") {
if (!txid || txid === '') {
res.status(400)
return res.json({ error: "txid can not be empty" })
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.id = 'gettxoutproof'
options.data.method = 'gettxoutproof'
options.data.params = [[txid]]
const response = await _this.axios.request(options)
@@ -716,14 +718,14 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getTxOutProofSingle().", err)
wlogger.error('Error in blockchain.ts/getTxOutProofSingle().', err)
return _this.errorHandler(err, res)
}
}
// Returns a hex-encoded proof that 'txid' was included in a block.
async getTxOutProofBulk(req, res, next) {
async getTxOutProofBulk (req, res, next) {
try {
const txids = req.body.txids
@@ -731,7 +733,7 @@ class Blockchain {
if (!Array.isArray(txids)) {
res.status(400)
return res.json({
error: "txids needs to be an array. Use GET for single txid."
error: 'txids needs to be an array. Use GET for single txid.'
})
}
@@ -739,7 +741,7 @@ class Blockchain {
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."
error: 'Array too large.'
})
}
@@ -759,17 +761,17 @@ class Blockchain {
}
wlogger.debug(
"Executing blockchain/getTxOutProof with these txids: ",
'Executing blockchain/getTxOutProof with these txids: ',
txids
)
// Loop through each txid and creates an array of requests to call in parallel
const promises = txids.map(async txid => {
options.data.id = "gettxoutproof"
options.data.method = "gettxoutproof"
options.data.id = 'gettxoutproof'
options.data.method = 'gettxoutproof'
options.data.params = [[txid]]
return await _this.axios.request(options)
return _this.axios.request(options)
})
// Wait for all parallel promisses to resolve.
@@ -783,26 +785,26 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/getTxOutProofBulk().", err)
wlogger.error('Error in blockchain.ts/getTxOutProofBulk().', err)
return _this.errorHandler(err, res)
}
}
async verifyTxOutProofSingle(req, res, next) {
async verifyTxOutProofSingle (req, res, next) {
try {
// Validate input parameter
const proof = req.params.proof
if (!proof || proof === "") {
if (!proof || proof === '') {
res.status(400)
return res.json({ error: "proof can not be empty" })
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.id = 'verifytxoutproof'
options.data.method = 'verifytxoutproof'
options.data.params = [req.params.proof]
const response = await _this.axios.request(options)
@@ -811,13 +813,13 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/verifyTxOutProofSingle().", err)
wlogger.error('Error in blockchain.ts/verifyTxOutProofSingle().', err)
return _this.errorHandler(err, res)
}
}
async verifyTxOutProofBulk(req, res, next) {
async verifyTxOutProofBulk (req, res, next) {
try {
const proofs = req.body.proofs
@@ -825,7 +827,7 @@ class Blockchain {
if (!Array.isArray(proofs)) {
res.status(400)
return res.json({
error: "proofs needs to be an array. Use GET for single proof."
error: 'proofs needs to be an array. Use GET for single proof.'
})
}
@@ -833,7 +835,7 @@ class Blockchain {
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."
error: 'Array too large.'
})
}
@@ -844,24 +846,24 @@ class Blockchain {
for (let i = 0; i < proofs.length; i++) {
const proof = proofs[i]
if (!proof || proof === "") {
if (!proof || proof === '') {
res.status(400)
return res.json({ error: `proof can not be empty: ${proof}` })
}
}
wlogger.debug(
"Executing blockchain/verifyTxOutProof with these proofs: ",
'Executing blockchain/verifyTxOutProof with these proofs: ',
proofs
)
// Loop through each proof and creates an array of requests to call in parallel
const promises = proofs.map(async proof => {
options.data.id = "verifytxoutproof"
options.data.method = "verifytxoutproof"
options.data.id = 'verifytxoutproof'
options.data.method = 'verifytxoutproof'
options.data.params = [proof]
return await _this.axios.request(options)
return _this.axios.request(options)
})
// Wait for all parallel promisses to resolve.
@@ -875,7 +877,7 @@ class Blockchain {
} catch (err) {
// Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error("Error in blockchain.ts/verifyTxOutProofBulk().", err)
wlogger.error('Error in blockchain.ts/verifyTxOutProofBulk().', err)
return _this.errorHandler(err, res)
}
+18 -19
View File
@@ -1,21 +1,21 @@
"use strict"
'use strict'
const express = require("express")
const express = require('express')
const router = express.Router()
const axios = require("axios")
// const axios = require('axios')
const routeUtils = require("../route-utils")
const wlogger = require("../../../util/winston-logging")
const routeUtils = require('../route-utils')
const wlogger = require('../../../util/winston-logging')
// Used for processing error messages before sending them to the user.
const util = require("util")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
router.get("/", root)
router.get("/getnetworkinfo", getNetworkInfo)
router.get('/', root)
router.get('/getnetworkinfo', getNetworkInfo)
function root(req, res, next) {
return res.json({ status: "control" })
function root (req, res, next) {
return res.json({ status: 'control' })
}
/**
@@ -28,16 +28,16 @@ function root(req, res, next) {
* curl -X GET "https://mainnet.bchjs.cash/v3/control/getnetworkinfo" -H "accept: application/json"
*
*/
async function getNetworkInfo(req, res, next) {
async function getNetworkInfo (req, res, next) {
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
requestConfig.data.id = "getnetworkinfo"
requestConfig.data.method = "getnetworkinfo"
requestConfig.data.id = 'getnetworkinfo'
requestConfig.data.method = 'getnetworkinfo'
requestConfig.data.params = []
try {
@@ -45,14 +45,13 @@ async function getNetworkInfo(req, res, next) {
return res.json(response.data.result)
} catch (error) {
wlogger.error(`Error in control.ts/getNetworkInfo().`, error)
wlogger.error('Error in control.ts/getNetworkInfo().', error)
// Write out error to error log.
//logger.error(`Error in control/getInfo: `, error)
// logger.error(`Error in control/getInfo: `, error)
res.status(500)
if (error.response && error.response.data && error.response.data.error)
return res.json({ error: error.response.data.error })
if (error.response && error.response.data && error.response.data.error) { return res.json({ error: error.response.data.error }) }
return res.json({ error: util.inspect(error) })
}
}
+38 -38
View File
@@ -1,39 +1,39 @@
"use strict"
'use strict'
const express = require("express")
const express = require('express')
const router = express.Router()
const axios = require("axios")
// const axios = require('axios')
const routeUtils = require("../route-utils")
const wlogger = require("../../../util/winston-logging")
const routeUtils = require('../route-utils')
const wlogger = require('../../../util/winston-logging')
// Used to convert error messages to strings, to safely pass to users.
const util = require("util")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const BitboxHTTP = axios.create({
baseURL: process.env.RPC_BASEURL
})
const username = process.env.RPC_USERNAME
const password = process.env.RPC_PASSWORD
// const BitboxHTTP = axios.create({
// baseURL: process.env.RPC_BASEURL
// })
// const username = process.env.RPC_USERNAME
// const password = process.env.RPC_PASSWORD
const requestConfig = {
method: "post",
auth: {
username: username,
password: password
},
data: {
jsonrpc: "1.0"
}
}
// const requestConfig = {
// method: 'post',
// auth: {
// username: username,
// password: password
// },
// data: {
// jsonrpc: '1.0'
// }
// }
router.get("/", root)
router.get("/getMiningInfo", getMiningInfo)
router.get("/getNetworkHashps", getNetworkHashPS)
router.get('/', root)
router.get('/getMiningInfo', getMiningInfo)
router.get('/getNetworkHashps', getNetworkHashPS)
function root(req, res, next) {
return res.json({ status: "mining" })
function root (req, res, next) {
return res.json({ status: 'mining' })
}
//
@@ -72,17 +72,17 @@ function root(req, res, next) {
*
*
*/
async function getMiningInfo(req, res, next) {
async function getMiningInfo (req, res, next) {
try {
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
requestConfig.data.id = "getmininginfo"
requestConfig.data.method = "getmininginfo"
requestConfig.data.id = 'getmininginfo'
requestConfig.data.method = 'getmininginfo'
requestConfig.data.params = []
const response = await BitboxHTTP(requestConfig)
@@ -96,7 +96,7 @@ async function getMiningInfo(req, res, next) {
return res.json({ error: msg })
}
wlogger.error(`Error in mining.ts/getMiningInfo().`, err)
wlogger.error('Error in mining.ts/getMiningInfo().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -114,7 +114,7 @@ async function getMiningInfo(req, res, next) {
*
*
*/
async function getNetworkHashPS(req, res, next) {
async function getNetworkHashPS (req, res, next) {
try {
let nblocks = 120 // Default
let height = -1 // Default
@@ -123,13 +123,13 @@ async function getNetworkHashPS(req, res, next) {
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
requestConfig.data.id = "getnetworkhashps"
requestConfig.data.method = "getnetworkhashps"
requestConfig.data.id = 'getnetworkhashps'
requestConfig.data.method = 'getnetworkhashps'
requestConfig.data.params = [nblocks, height]
const response = await BitboxHTTP(requestConfig)
@@ -143,7 +143,7 @@ async function getNetworkHashPS(req, res, next) {
return res.json({ error: msg })
}
wlogger.error(`Error in mining.ts/getNetworkHashPS().`, err)
wlogger.error('Error in mining.ts/getNetworkHashPS().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
+4 -4
View File
@@ -1,10 +1,10 @@
"use strict"
'use strict'
const express = require("express")
const express = require('express')
const router = express.Router()
router.get("/", async (req, res, next) => {
res.json({ status: "network" })
router.get('/', async (req, res, next) => {
res.json({ status: 'network' })
})
// router.post('/addNode/:node/:command', (req, res, next) => {
+119 -119
View File
@@ -1,45 +1,45 @@
"use strict"
'use strict'
const express = require("express")
const express = require('express')
const router = express.Router()
const axios = require("axios")
const axios = require('axios')
const routeUtils = require("../route-utils")
const wlogger = require("../../../util/winston-logging")
const routeUtils = require('../route-utils')
const wlogger = require('../../../util/winston-logging')
// Used to convert error messages to strings, to safely pass to users.
const util = require("util")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const BitboxHTTP = axios.create({
baseURL: process.env.RPC_BASEURL
})
const username = process.env.RPC_USERNAME
const password = process.env.RPC_PASSWORD
// const BitboxHTTP = axios.create({
// baseURL: process.env.RPC_BASEURL
// })
// const username = process.env.RPC_USERNAME
// const password = process.env.RPC_PASSWORD
const requestConfig = {
method: "post",
auth: {
username: username,
password: password
},
data: {
jsonrpc: "1.0"
}
}
// const requestConfig = {
// method: 'post',
// auth: {
// username: username,
// password: password
// },
// data: {
// jsonrpc: '1.0'
// }
// }
router.get("/", root)
router.get("/decodeRawTransaction/:hex", decodeRawTransactionSingle)
router.post("/decodeRawTransaction", decodeRawTransactionBulk)
router.get("/decodeScript/:hex", decodeScriptSingle)
router.post("/decodeScript", decodeScriptBulk)
router.post("/getRawTransaction", getRawTransactionBulk)
router.get("/getRawTransaction/:txid", getRawTransactionSingle)
router.post("/sendRawTransaction", sendRawTransactionBulk)
router.get("/sendRawTransaction/:hex", sendRawTransactionSingle)
router.get('/', root)
router.get('/decodeRawTransaction/:hex', decodeRawTransactionSingle)
router.post('/decodeRawTransaction', decodeRawTransactionBulk)
router.get('/decodeScript/:hex', decodeScriptSingle)
router.post('/decodeScript', decodeScriptBulk)
router.post('/getRawTransaction', getRawTransactionBulk)
router.get('/getRawTransaction/:txid', getRawTransactionSingle)
router.post('/sendRawTransaction', sendRawTransactionBulk)
router.get('/sendRawTransaction/:hex', sendRawTransactionSingle)
function root(req, res, next) {
return res.json({ status: "rawtransactions" })
function root (req, res, next) {
return res.json({ status: 'rawtransactions' })
}
// Decode transaction hex into a JSON object.
@@ -56,25 +56,25 @@ function root(req, res, next) {
*
*
*/
async function decodeRawTransactionSingle(req, res, next) {
async function decodeRawTransactionSingle (req, res, next) {
try {
const hex = req.params.hex
// Throw an error if hex is empty.
if (!hex || hex === "") {
if (!hex || hex === '') {
res.status(400)
return res.json({ error: "hex can not be empty" })
return res.json({ error: 'hex can not be empty' })
}
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
requestConfig.data.id = "decoderawtransaction"
requestConfig.data.method = "decoderawtransaction"
requestConfig.data.id = 'decoderawtransaction'
requestConfig.data.method = 'decoderawtransaction'
requestConfig.data.params = [hex]
const response = await BitboxHTTP(requestConfig)
@@ -88,9 +88,9 @@ async function decodeRawTransactionSingle(req, res, next) {
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error(
`Error in rawtransactions.ts/decodeRawTransactionSingle().`,
'Error in rawtransactions.ts/decodeRawTransactionSingle().',
err
)
@@ -111,50 +111,50 @@ async function decodeRawTransactionSingle(req, res, next) {
*
*/
async function decodeRawTransactionBulk(req, res, next) {
async function decodeRawTransactionBulk (req, res, next) {
try {
const hexes = req.body.hexes
if (!Array.isArray(hexes)) {
res.status(400)
return res.json({ error: "hexes must be an array" })
return res.json({ error: 'hexes must be an array' })
}
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, hexes)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
error: 'Array too large.'
})
}
const results = []
// const results = []
// Validate each element in the address array.
for (let i = 0; i < hexes.length; i++) {
const thisHex = hexes[i]
// Reject if id is empty
if (!thisHex || thisHex === "") {
if (!thisHex || thisHex === '') {
res.status(400)
return res.json({ error: "Encountered empty hex" })
return res.json({ error: 'Encountered empty hex' })
}
}
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
// Loop through each height and creates an array of requests to call in parallel
const promises = hexes.map(async hex => {
requestConfig.data.id = "decoderawtransaction"
requestConfig.data.method = "decoderawtransaction"
requestConfig.data.id = 'decoderawtransaction'
requestConfig.data.method = 'decoderawtransaction'
requestConfig.data.params = [hex]
return await BitboxHTTP(requestConfig)
return BitboxHTTP(requestConfig)
})
// Wait for all parallel Insight requests to return.
@@ -210,9 +210,9 @@ async function decodeRawTransactionBulk(req, res, next) {
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/getRawTransaction: `, err)
// logger.error(`Error in rawtransactions/getRawTransaction: `, err)
wlogger.error(
`Error in rawtransactions.ts/decodeRawTransactionBulk().`,
'Error in rawtransactions.ts/decodeRawTransactionBulk().',
err
)
@@ -235,25 +235,25 @@ async function decodeRawTransactionBulk(req, res, next) {
*
*
*/
async function decodeScriptSingle(req, res, next) {
async function decodeScriptSingle (req, res, next) {
try {
const hex = req.params.hex
// Throw an error if hex is empty.
if (!hex || hex === "") {
if (!hex || hex === '') {
res.status(400)
return res.json({ error: "hex can not be empty" })
return res.json({ error: 'hex can not be empty' })
}
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
requestConfig.data.id = "decodescript"
requestConfig.data.method = "decodescript"
requestConfig.data.id = 'decodescript'
requestConfig.data.method = 'decodescript'
requestConfig.data.params = [hex]
const response = await BitboxHTTP(requestConfig)
@@ -267,8 +267,8 @@ async function decodeScriptSingle(req, res, next) {
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeScript: `, err)
wlogger.error(`Error in rawtransactions.ts/decodeScriptSingle().`, err)
// logger.error(`Error in rawtransactions/decodeScript: `, err)
wlogger.error('Error in rawtransactions.ts/decodeScriptSingle().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -289,21 +289,21 @@ async function decodeScriptSingle(req, res, next) {
*
*
*/
async function decodeScriptBulk(req, res, next) {
async function decodeScriptBulk (req, res, next) {
try {
const hexes = req.body.hexes
// Validation
if (!Array.isArray(hexes)) {
res.status(400)
return res.json({ error: "hexes must be an array" })
return res.json({ error: 'hexes must be an array' })
}
// Enforce array size rate limits
if (!routeUtils.validateArraySize(req, hexes)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
error: 'Array too large.'
})
}
@@ -312,23 +312,23 @@ async function decodeScriptBulk(req, res, next) {
const hex = hexes[i]
// Throw an error if hex is empty.
if (!hex || hex === "") {
if (!hex || hex === '') {
res.status(400)
return res.json({ error: "Encountered empty hex" })
return res.json({ error: 'Encountered empty hex' })
}
}
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
// Loop through each hex and create an array of promises
const promises = hexes.map(async hex => {
requestConfig.data.id = "decodescript"
requestConfig.data.method = "decodescript"
requestConfig.data.id = 'decodescript'
requestConfig.data.method = 'decodescript'
requestConfig.data.params = [hex]
const response = await BitboxHTTP(requestConfig)
@@ -352,8 +352,8 @@ async function decodeScriptBulk(req, res, next) {
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/decodeScript: `, err)
wlogger.error(`Error in rawtransactions.ts/decodeScriptBulk().`, err)
// logger.error(`Error in rawtransactions/decodeScript: `, err)
wlogger.error('Error in rawtransactions.ts/decodeScriptBulk().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -362,24 +362,24 @@ async function decodeScriptBulk(req, res, next) {
// Retrieve raw transactions details from the full node.
async function getRawTransactionsFromNode(txid, verbose) {
async function getRawTransactionsFromNode (txid, verbose) {
try {
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
requestConfig.data.id = "getrawtransaction"
requestConfig.data.method = "getrawtransaction"
requestConfig.data.id = 'getrawtransaction'
requestConfig.data.method = 'getrawtransaction'
requestConfig.data.params = [txid, verbose]
const response = await BitboxHTTP(requestConfig)
return response.data.result
} catch (err) {
wlogger.error(`Error in rawtransactions.ts/getRawTransactionsFromNode().`)
wlogger.error('Error in rawtransactions.ts/getRawTransactionsFromNode().')
throw err
}
}
@@ -397,7 +397,7 @@ async function getRawTransactionsFromNode(txid, verbose) {
* curl -X POST "https://mainnet.bchjs.cash/v3/rawtransactions/getRawTransaction" -H "accept: application/json" -H "Content-Type: application/json" -d '{"txids":["a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1","5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e"],"verbose":true}'
*
*/
async function getRawTransactionBulk(req, res, next) {
async function getRawTransactionBulk (req, res, next) {
try {
let verbose = 0
if (req.body.verbose) verbose = 1
@@ -405,32 +405,32 @@ async function getRawTransactionBulk(req, res, next) {
const txids = req.body.txids
if (!Array.isArray(txids)) {
res.status(400)
return res.json({ error: "txids must be an array" })
return res.json({ error: 'txids must 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.`
error: 'Array too large.'
})
}
// stub response object
const returnResponse = {
status: 100,
json: {
error: ""
}
}
// const returnResponse = {
// status: 100,
// json: {
// error: ''
// }
// }
// Validate each txid in the array.
for (let i = 0; i < txids.length; i++) {
const txid = txids[i]
if (!txid || txid === "") {
if (!txid || txid === '') {
res.status(400)
return res.json({ error: `Encountered empty TXID` })
return res.json({ error: 'Encountered empty TXID' })
}
if (txid.length !== 64) {
@@ -460,8 +460,8 @@ async function getRawTransactionBulk(req, res, next) {
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/getRawTransaction: `, err)
wlogger.error(`Error in rawtransactions.ts/getRawTransactionBulk().`, err)
// logger.error(`Error in rawtransactions/getRawTransaction: `, err)
wlogger.error('Error in rawtransactions.ts/getRawTransactionBulk().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -482,15 +482,15 @@ async function getRawTransactionBulk(req, res, next) {
*
*
*/
async function getRawTransactionSingle(req, res, next) {
async function getRawTransactionSingle (req, res, next) {
try {
let verbose = 0
if (req.query.verbose === "true") verbose = 1
if (req.query.verbose === 'true') verbose = 1
const txid = req.params.txid
if (!txid || txid === "") {
if (!txid || txid === '') {
res.status(400)
return res.json({ error: "txid can not be empty" })
return res.json({ error: 'txid can not be empty' })
}
const data = await getRawTransactionsFromNode(txid, verbose)
@@ -505,8 +505,8 @@ async function getRawTransactionSingle(req, res, next) {
}
// Write out error to error log.
//logger.error(`Error in rawtransactions/getRawTransaction: `, err)
wlogger.error(`Error in rawtransactions.ts/getRawTransactionSingle().`, err)
// logger.error(`Error in rawtransactions/getRawTransaction: `, err)
wlogger.error('Error in rawtransactions.ts/getRawTransactionSingle().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -526,7 +526,7 @@ async function getRawTransactionSingle(req, res, next) {
*
*
*/
async function sendRawTransactionBulk(req, res, next) {
async function sendRawTransactionBulk (req, res, next) {
try {
// Validation
const hexes = req.body.hexes
@@ -534,13 +534,13 @@ async function sendRawTransactionBulk(req, res, next) {
// Reject if input is not an array
if (!Array.isArray(hexes)) {
res.status(400)
return res.json({ error: "hex must be an array" })
return res.json({ error: 'hex must be an array' })
}
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
@@ -548,7 +548,7 @@ async function sendRawTransactionBulk(req, res, next) {
if (!routeUtils.validateArraySize(req, hexes)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Array too large.`
error: 'Array too large.'
})
}
@@ -556,10 +556,10 @@ async function sendRawTransactionBulk(req, res, next) {
for (let i = 0; i < hexes.length; i++) {
const hex = hexes[i]
if (hex === "") {
if (hex === '') {
res.status(400)
return res.json({
error: `Encountered empty hex`
error: 'Encountered empty hex'
})
}
}
@@ -594,8 +594,8 @@ async function sendRawTransactionBulk(req, res, next) {
for (let i = 0; i < hexes.length; i++) {
const hex = hexes[i]
requestConfig.data.id = "sendrawtransaction"
requestConfig.data.method = "sendrawtransaction"
requestConfig.data.id = 'sendrawtransaction'
requestConfig.data.method = 'sendrawtransaction'
requestConfig.data.params = [hex]
const rpcResult = await BitboxHTTP(requestConfig)
@@ -613,7 +613,7 @@ async function sendRawTransactionBulk(req, res, next) {
return res.json({ error: msg })
}
wlogger.error(`Error in rawtransactions.ts/sendRawTransactionBulk().`, err)
wlogger.error('Error in rawtransactions.ts/sendRawTransactionBulk().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -633,34 +633,34 @@ async function sendRawTransactionBulk(req, res, next) {
*
*
*/
async function sendRawTransactionSingle(req, res, next) {
async function sendRawTransactionSingle (req, res, next) {
try {
const hex = req.params.hex // URL parameter
// Reject if input is not an array or a string
if (typeof hex !== "string") {
if (typeof hex !== 'string') {
res.status(400)
return res.json({ error: "hex must be a string" })
return res.json({ error: 'hex must be a string' })
}
// Validation
if (hex === "") {
if (hex === '') {
res.status(400)
return res.json({
error: `Encountered empty hex`
error: 'Encountered empty hex'
})
}
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
// RPC call
requestConfig.data.id = "sendrawtransaction"
requestConfig.data.method = "sendrawtransaction"
requestConfig.data.id = 'sendrawtransaction'
requestConfig.data.method = 'sendrawtransaction'
requestConfig.data.params = [hex]
const rpcResult = await BitboxHTTP(requestConfig)
@@ -678,7 +678,7 @@ async function sendRawTransactionSingle(req, res, next) {
}
wlogger.error(
`Error in rawtransactions.ts/sendRawTransactionSingle().`,
'Error in rawtransactions.ts/sendRawTransactionSingle().',
err
)
+3 -3
View File
@@ -3,13 +3,13 @@
readiness.
*/
"use strict"
'use strict'
const express = require("express")
const express = require('express')
const router = express.Router()
/* GET home page. */
router.get("/", (req, res, next) => {
router.get('/', (req, res, next) => {
res.json({ status: true })
})
+13 -13
View File
@@ -2,30 +2,30 @@
A library for interacting with the Bitcoin.com ninsight (not Insight) indexer.
*/
"use strict"
'use strict'
const express = require("express")
const axios = require("axios")
const routeUtils = require("./route-utils")
const wlogger = require("../../util/winston-logging")
const express = require('express')
// const axios = require('axios')
// const routeUtils = require('./route-utils')
// const wlogger = require('../../util/winston-logging')
const router = express.Router()
const BCHJS = require("@chris.troutner/bch-js")
const bchjs = new BCHJS()
// const BCHJS = require('@chris.troutner/bch-js')
// const bchjs = new BCHJS()
let _this
// let _this
class Ninsight {
constructor() {
_this = this
constructor () {
// _this = this
this.router = router
this.router.get("/", this.root)
this.router.get('/', this.root)
}
root(req, res, next) {
return res.json({ status: "ninsight" })
root (req, res, next) {
return res.json({ status: 'ninsight' })
}
}
+32 -30
View File
@@ -2,15 +2,15 @@
A private library of utility functions used by several different routes.
*/
"use strict"
'use strict'
const axios = require("axios")
const wlogger = require("../../util/winston-logging")
const axios = require('axios')
const wlogger = require('../../util/winston-logging')
const util = require("util")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const BCHJS = require("@chris.troutner/bch-js")
const BCHJS = require('@chris.troutner/bch-js')
const bchjs = new BCHJS()
module.exports = {
@@ -25,7 +25,7 @@ module.exports = {
// The array is then validated against freemium and pro-tier rate limiting
// requirements. A boolean is returned to indicate if the array size if valid
// or not.
function validateArraySize(req, array) {
function validateArraySize (req, array) {
const FREEMIUM_INPUT_SIZE = 20
const PRO_INPUT_SIZE = 20
@@ -43,13 +43,13 @@ function validateArraySize(req, array) {
// This prevent a common user-error issue that is easy to make: passing a
// testnet address into rest.bitcoin.com or passing a mainnet address into
// trest.bitcoin.com.
function validateNetwork(addr) {
function validateNetwork (addr) {
try {
const network = process.env.NETWORK
// Return false if NETWORK is not defined.
if (!network || network === "") {
console.log(`Warning: NETWORK environment variable is not defined!`)
if (!network || network === '') {
console.log('Warning: NETWORK environment variable is not defined!')
return false
}
@@ -59,21 +59,21 @@ function validateNetwork(addr) {
// Return true if the network and address both match testnet
const addrIsTest = bchjs.Address.isTestnetAddress(cashAddr)
if (network === "testnet" && addrIsTest) return true
if (network === 'testnet' && addrIsTest) return true
// Return true if the network and address both match mainnet
const addrIsMain = bchjs.Address.isMainnetAddress(cashAddr)
if (network === "mainnet" && addrIsMain) return true
if (network === 'mainnet' && addrIsMain) return true
return false
} catch (err) {
logger.error(`Error in validateNetwork()`)
wlogger.error('Error in validateNetwork()')
return false
}
}
// Dynamically set these based on env vars. Allows unit testing.
function setEnvVars() {
function setEnvVars () {
const BitboxHTTP = axios.create({
baseURL: process.env.RPC_BASEURL,
timeout: 15000
@@ -82,13 +82,13 @@ function setEnvVars() {
const password = process.env.RPC_PASSWORD
const requestConfig = {
method: "post",
method: 'post',
auth: {
username: username,
password: password
},
data: {
jsonrpc: "1.0"
jsonrpc: '1.0'
}
}
@@ -96,9 +96,9 @@ function setEnvVars() {
}
// Axios options used when calling axios.post() to talk with a full node.
function getAxiosOptions() {
function getAxiosOptions () {
return {
method: "post",
method: 'post',
baseURL: process.env.RPC_BASEURL,
timeout: 15000,
auth: {
@@ -106,7 +106,7 @@ function getAxiosOptions() {
password: process.env.RPC_PASSWORD
},
data: {
jsonrpc: "1.0"
jsonrpc: '1.0'
}
}
}
@@ -116,7 +116,7 @@ function getAxiosOptions() {
// error messages.
// Returns an object. If successful, obj.msg is a string.
// If there is a failure, obj.msg is false.
function decodeError(err) {
function decodeError (err) {
try {
// Attempt to extract the full node error message.
if (
@@ -124,39 +124,41 @@ function decodeError(err) {
err.response.data &&
err.response.data.error &&
err.response.data.error.message
)
) {
return { msg: err.response.data.error.message, status: 400 }
}
// Attempt to extract the Insight error message
if (err.response && err.response.data)
if (err.response && err.response.data) {
return { msg: err.response.data, status: err.response.status }
}
// console.log(`err.message: ${err.message}`)
// console.log(`err: `, err)
// Attempt to detect a network connection error.
if (err.message && err.message.indexOf("ENOTFOUND") > -1) {
if (err.message && err.message.indexOf('ENOTFOUND') > -1) {
return {
msg:
"Network error: Could not communicate with full node or other external service.",
'Network error: Could not communicate with full node or other external service.',
status: 503
}
}
// Different kind of network error
if (err.message && err.message.indexOf("ENETUNREACH") > -1) {
if (err.message && err.message.indexOf('ENETUNREACH') > -1) {
return {
msg:
"Network error: Could not communicate with full node or other external service.",
'Network error: Could not communicate with full node or other external service.',
status: 503
}
}
// Different kind of network error
if (err.message && err.message.indexOf("EAI_AGAIN") > -1) {
if (err.message && err.message.indexOf('EAI_AGAIN') > -1) {
return {
msg:
"Network error: Could not communicate with full node or other external service.",
'Network error: Could not communicate with full node or other external service.',
status: 503
}
}
@@ -164,18 +166,18 @@ function decodeError(err) {
// Axios timeout (aborted) error, or service is down (connection refused).
if (
err.code &&
(err.code === "ECONNABORTED" || err.code === "ECONNREFUSED")
(err.code === 'ECONNABORTED' || err.code === 'ECONNREFUSED')
) {
return {
msg:
"Network error: Could not communicate with full node or other external service.",
'Network error: Could not communicate with full node or other external service.',
status: 503
}
}
return { msg: false, status: 500 }
} catch (err) {
wlogger.error(`unhandled error in route-utils.js/decodeError(): `, err)
wlogger.error('unhandled error in route-utils.js/decodeError(): ', err)
return { msg: false, status: 500 }
}
}
+32 -30
View File
@@ -2,22 +2,22 @@
A private library of utility functions used by several different routes.
*/
"use strict"
'use strict'
const axios = require("axios")
const wlogger = require("../../util/winston-logging")
const axios = require('axios')
const wlogger = require('../../util/winston-logging')
const util = require("util")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const BCHJS = require("@chris.troutner/bch-js")
const BCHJS = require('@chris.troutner/bch-js')
const bchjs = new BCHJS()
let _this
// let _this
class RouteUtils {
constructor() {
_this = this
constructor () {
// _this = this
this.bchjs = bchjs
this.axios = axios
@@ -27,7 +27,7 @@ class RouteUtils {
// The array is then validated against freemium and pro-tier rate limiting
// requirements. A boolean is returned to indicate if the array size if valid
// or not.
validateArraySize(req, array) {
validateArraySize (req, array) {
const FREEMIUM_INPUT_SIZE = 20
const PRO_INPUT_SIZE = 20
@@ -41,9 +41,9 @@ class RouteUtils {
}
// Axios options used when calling axios.post() to talk with a full node.
getAxiosOptions() {
getAxiosOptions () {
return {
method: "post",
method: 'post',
baseURL: process.env.RPC_BASEURL,
timeout: 15000,
auth: {
@@ -51,7 +51,7 @@ class RouteUtils {
password: process.env.RPC_PASSWORD
},
data: {
jsonrpc: "1.0"
jsonrpc: '1.0'
}
}
}
@@ -61,13 +61,13 @@ class RouteUtils {
// This prevent a common user-error issue that is easy to make: passing a
// testnet address into rest.bitcoin.com or passing a mainnet address into
// trest.bitcoin.com.
validateNetwork(addr) {
validateNetwork (addr) {
try {
const network = process.env.NETWORK
// Return false if NETWORK is not defined.
if (!network || network === "") {
console.log(`Warning: NETWORK environment variable is not defined!`)
if (!network || network === '') {
console.log('Warning: NETWORK environment variable is not defined!')
return false
}
@@ -77,15 +77,15 @@ class RouteUtils {
// Return true if the network and address both match testnet
const addrIsTest = this.bchjs.Address.isTestnetAddress(cashAddr)
if (network === "testnet" && addrIsTest) return true
if (network === 'testnet' && addrIsTest) return true
// Return true if the network and address both match mainnet
const addrIsMain = this.bchjs.Address.isMainnetAddress(cashAddr)
if (network === "mainnet" && addrIsMain) return true
if (network === 'mainnet' && addrIsMain) return true
return false
} catch (err) {
logger.error(`Error in validateNetwork()`)
wlogger.error('Error in validateNetwork()')
return false
}
}
@@ -95,7 +95,7 @@ class RouteUtils {
// error messages.
// Returns an object. If successful, obj.msg is a string.
// If there is a failure, obj.msg is false.
decodeError(err) {
decodeError (err) {
try {
// Attempt to extract the full node error message.
if (
@@ -103,39 +103,41 @@ class RouteUtils {
err.response.data &&
err.response.data.error &&
err.response.data.error.message
)
) {
return { msg: err.response.data.error.message, status: 400 }
}
// Attempt to extract the Insight error message
if (err.response && err.response.data)
if (err.response && err.response.data) {
return { msg: err.response.data, status: err.response.status }
}
// console.log(`err.message: ${err.message}`)
// console.log(`err: `, err)
// Attempt to detect a network connection error.
if (err.message && err.message.indexOf("ENOTFOUND") > -1) {
if (err.message && err.message.indexOf('ENOTFOUND') > -1) {
return {
msg:
"Network error: Could not communicate with full node or other external service.",
'Network error: Could not communicate with full node or other external service.',
status: 503
}
}
// Different kind of network error
if (err.message && err.message.indexOf("ENETUNREACH") > -1) {
if (err.message && err.message.indexOf('ENETUNREACH') > -1) {
return {
msg:
"Network error: Could not communicate with full node or other external service.",
'Network error: Could not communicate with full node or other external service.',
status: 503
}
}
// Different kind of network error
if (err.message && err.message.indexOf("EAI_AGAIN") > -1) {
if (err.message && err.message.indexOf('EAI_AGAIN') > -1) {
return {
msg:
"Network error: Could not communicate with full node or other external service.",
'Network error: Could not communicate with full node or other external service.',
status: 503
}
}
@@ -143,18 +145,18 @@ class RouteUtils {
// Axios timeout (aborted) error, or service is down (connection refused).
if (
err.code &&
(err.code === "ECONNABORTED" || err.code === "ECONNREFUSED")
(err.code === 'ECONNABORTED' || err.code === 'ECONNREFUSED')
) {
return {
msg:
"Network error: Could not communicate with full node or other external service.",
'Network error: Could not communicate with full node or other external service.',
status: 503
}
}
return { msg: false, status: 500 }
} catch (err) {
wlogger.error(`unhandled error in route-utils.js/decodeError(): `, err)
wlogger.error('unhandled error in route-utils.js/decodeError(): ', err)
return { msg: false, status: 500 }
}
}
+292 -297
View File
File diff suppressed because it is too large Load Diff
+94 -87
View File
@@ -1,50 +1,50 @@
"use strict"
'use strict'
const express = require("express")
const express = require('express')
const router = express.Router()
const axios = require("axios")
const axios = require('axios')
const routeUtils = require("./route-utils")
const wlogger = require("../../util/winston-logging")
const blockbook = require("./blockbook")
const routeUtils = require('./route-utils')
const wlogger = require('../../util/winston-logging')
const blockbook = require('./blockbook')
const util = require("util")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const BCHJS = require("@chris.troutner/bch-js")
const BCHJS = require('@chris.troutner/bch-js')
const bchjs = new BCHJS()
const BCHJS_TESTNET = `https://testnet.bchjs.cash/v3/`
// const BCHJS_TESTNET = 'https://testnet.bchjs.cash/v3/'
const bchjsHTTP = axios.create({
baseURL: process.env.RPC_BASEURL
})
// const bchjsHTTP = axios.create({
// baseURL: process.env.RPC_BASEURL
// })
const username = process.env.RPC_USERNAME
const password = process.env.RPC_PASSWORD
// const username = process.env.RPC_USERNAME
// const password = process.env.RPC_PASSWORD
const requestConfig = {
method: "post",
auth: {
username: username,
password: password
},
data: {
jsonrpc: "1.0"
}
}
// const requestConfig = {
// method: 'post',
// auth: {
// username: username,
// password: password
// },
// data: {
// jsonrpc: '1.0'
// }
// }
let _this
class UtilRoute {
constructor() {
constructor () {
this.bchjs = bchjs
this.blockbook = blockbook
_this = this
}
root(req, res, next) {
return res.json({ status: "util" })
root (req, res, next) {
return res.json({ status: 'util' })
}
/**
@@ -59,23 +59,23 @@ class UtilRoute {
*
*
*/
async validateAddressSingle(req, res, next) {
async validateAddressSingle (req, res, next) {
try {
const address = req.params.address
if (!address || address === "") {
if (!address || address === '') {
res.status(400)
return res.json({ error: "address can not be empty" })
return res.json({ error: 'address can not be empty' })
}
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
requestConfig.data.id = "validateaddress"
requestConfig.data.method = "validateaddress"
requestConfig.data.id = 'validateaddress'
requestConfig.data.method = 'validateaddress'
requestConfig.data.params = [address]
const response = await BitboxHTTP(requestConfig)
@@ -89,7 +89,7 @@ class UtilRoute {
return res.json({ error: msg })
}
wlogger.error(`Error in util.ts/validateAddressSingle().`, err)
wlogger.error('Error in util.ts/validateAddressSingle().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -109,7 +109,7 @@ class UtilRoute {
*
*
*/
async validateAddressBulk(req, res, next) {
async validateAddressBulk (req, res, next) {
try {
const addresses = req.body.addresses
@@ -117,7 +117,7 @@ class UtilRoute {
if (!Array.isArray(addresses)) {
res.status(400)
return res.json({
error: "addresses needs to be an array. Use GET for single address."
error: 'addresses needs to be an array. Use GET for single address.'
})
}
@@ -125,7 +125,7 @@ class UtilRoute {
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.`
error: 'Array too large.'
})
}
@@ -135,7 +135,7 @@ class UtilRoute {
// Ensure the input is a valid BCH address.
try {
var legacyAddr = bchjs.Address.toLegacyAddress(address)
bchjs.Address.toLegacyAddress(address)
} catch (err) {
res.status(400)
return res.json({
@@ -148,27 +148,28 @@ class UtilRoute {
if (!networkIsValid) {
res.status(400)
return res.json({
error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.`
error:
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
})
}
}
wlogger.debug(`Executing util/validate with these addresses: `, addresses)
wlogger.debug('Executing util/validate with these addresses: ', addresses)
const {
BitboxHTTP,
username,
password,
// username,
// password,
requestConfig
} = routeUtils.setEnvVars()
// Loop through each address and creates an array of requests to call in parallel
const promises = addresses.map(async address => {
requestConfig.data.id = "validateaddress"
requestConfig.data.method = "validateaddress"
requestConfig.data.id = 'validateaddress'
requestConfig.data.method = 'validateaddress'
requestConfig.data.params = [address]
return await BitboxHTTP(requestConfig)
return BitboxHTTP(requestConfig)
})
// Wait for all parallel Insight requests to return.
@@ -187,7 +188,7 @@ class UtilRoute {
return res.json({ error: msg })
}
wlogger.error(`Error in util.ts/validateAddressSingle().`, err)
wlogger.error('Error in util.ts/validateAddressSingle().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
@@ -212,29 +213,29 @@ class UtilRoute {
*
*/
async sweepWif(req, res, next) {
async sweepWif (req, res, next) {
try {
// Validate input
const wif = req.body.wif
const toAddr = req.body.toAddr
const balanceOnly = req.body.balanceOnly
if (typeof wif !== "string" || wif.length !== 52) {
if (typeof wif !== 'string' || wif.length !== 52) {
res.status(400)
return res.json({
error: "WIF needs to a proper compressed WIF starting with K or L"
error: 'WIF needs to a proper compressed WIF starting with K or L'
})
}
if (!balanceOnly) {
// Only throw error if balanceOnly is false or undefined.
if (!toAddr || toAddr === "") {
if (!toAddr || toAddr === '') {
res.status(400)
return res.json({ error: "address can not be empty" })
return res.json({ error: 'address can not be empty' })
}
}
wlogger.debug(`Executing util/sweepWif with this address: `, toAddr)
wlogger.debug('Executing util/sweepWif with this address: ', toAddr)
// Generate a private and public key pair from the WIF.
const ecPair = bchjs.ECPair.fromWIF(wif)
@@ -253,7 +254,7 @@ class UtilRoute {
// Exit if balance is zero.
if (isNaN(totalBalance) || totalBalance === 0) {
res.status(422)
return res.json({ error: "No balance found at BCH address." })
return res.json({ error: 'No balance found at BCH address.' })
}
// Exit if this is a balance-only call.
@@ -274,7 +275,7 @@ class UtilRoute {
// Exit if there are no UTXOs.
if (utxos.length === 0) {
res.status(422)
return res.json({ error: "No utxos found." })
return res.json({ error: 'No utxos found.' })
}
// Figure out which UTXOs are associated with SLP tokens.
@@ -295,7 +296,8 @@ class UtilRoute {
if (bchUtxos.length === 0 && tokenUtxos.length > 0) {
res.status(422)
return res.json({
error: `Tokens found, but no BCH UTXOs found. Add BCH to wallet to move tokens.`
error:
'Tokens found, but no BCH UTXOs found. Add BCH to wallet to move tokens.'
})
}
@@ -337,14 +339,14 @@ class UtilRoute {
// Catch the specific case of multiple tokens.
if (
err.message &&
err.message.indexOf("Multiple token classes detected") > -1
err.message.indexOf('Multiple token classes detected') > -1
) {
res.status(422)
return res.json({ error: err.message })
}
wlogger.error(`Error in util.js/sweepWif().`, err)
console.error(`Error in util.js/sweepWif().`, err)
wlogger.error('Error in util.js/sweepWif().', err)
console.error('Error in util.js/sweepWif().', err)
res.status(500)
return res.json({ error: err.message })
@@ -352,7 +354,7 @@ class UtilRoute {
}
// Sweep BCH only from a private WIF.
async _sweepBCH(options) {
async _sweepBCH (options) {
try {
// const wif = flags.wif
// const toAddr = flags.address
@@ -377,9 +379,9 @@ class UtilRoute {
// instance of transaction builder
let transactionBuilder
if (options.testnet)
transactionBuilder = new _this.bchjs.TransactionBuilder("testnet")
else transactionBuilder = new _this.bchjs.TransactionBuilder()
if (options.testnet) {
transactionBuilder = new _this.bchjs.TransactionBuilder('testnet')
} else transactionBuilder = new _this.bchjs.TransactionBuilder()
let originalAmount = 0
@@ -394,7 +396,7 @@ class UtilRoute {
if (originalAmount < 546) {
throw new Error(
`Original amount less than the dust limit. Not enough BCH to send.`
'Original amount less than the dust limit. Not enough BCH to send.'
)
}
@@ -435,21 +437,24 @@ class UtilRoute {
const hex = tx.toHex()
return hex
} catch (err) {
wlogger.error(`Error in util.js/sweepBCH().`)
wlogger.error('Error in util.js/sweepBCH().')
throw err
}
}
// Sweep BCH and tokens from a WIF.
async _sweepTokens(options) {
async _sweepTokens (options) {
try {
const { ecPair, utxos, fromAddr, toAddr, bchUtxos, tokenUtxos } = options
// const { ecPair, utxos, fromAddr, toAddr, bchUtxos, tokenUtxos } = options
const { ecPair, utxos, toAddr, bchUtxos, tokenUtxos } = options
// Input validation
if (!Array.isArray(bchUtxos) || bchUtxos.length === 0)
throw new Error(`bchUtxos need to be an array with one UTXO.`)
if (!Array.isArray(tokenUtxos) || tokenUtxos.length === 0)
throw new Error(`tokenUtxos need to be an array with one UTXO.`)
if (!Array.isArray(bchUtxos) || bchUtxos.length === 0) {
throw new Error('bchUtxos need to be an array with one UTXO.')
}
if (!Array.isArray(tokenUtxos) || tokenUtxos.length === 0) {
throw new Error('tokenUtxos need to be an array with one UTXO.')
}
// if (flags.testnet)
// this.BITBOX = new config.BCHLIB({ restURL: config.TESTNET_REST })
@@ -462,15 +467,15 @@ class UtilRoute {
const otherTokens = tokenUtxos.filter(x => x.tokenId !== tokenId)
if (otherTokens.length > 0) {
throw new Error(
`Multiple token classes detected. This function only supports a single class of token.`
'Multiple token classes detected. This function only supports a single class of token.'
)
}
// instance of transaction builder
let transactionBuilder
if (options.testnet)
transactionBuilder = new _this.bchjs.TransactionBuilder("testnet")
else transactionBuilder = new _this.bchjs.TransactionBuilder()
if (options.testnet) {
transactionBuilder = new _this.bchjs.TransactionBuilder('testnet')
} else transactionBuilder = new _this.bchjs.TransactionBuilder()
// Combine all the UTXOs into a single array.
const allUtxos = utxos
@@ -488,7 +493,7 @@ class UtilRoute {
if (originalAmount < 300) {
throw new Error(
`Not enough BCH to send. Send more BCH to the wallet to pay miner fees.`
'Not enough BCH to send. Send more BCH to the wallet to pay miner fees.'
)
}
@@ -506,18 +511,20 @@ class UtilRoute {
// amount to send back to the sending address. It's the original amount - 1 sat/byte for tx size
const remainder = originalAmount - txFee - 546
if (remainder < 1)
throw new Error(`Selected UTXO does not have enough satoshis`)
//console.log(`remainder: ${remainder}`)
if (remainder < 1) {
throw new Error('Selected UTXO does not have enough satoshis')
}
// console.log(`remainder: ${remainder}`)
// Tally up the quantity of tokens
let tokenQty = 0
for (let i = 0; i < tokenUtxos.length; i++)
for (let i = 0; i < tokenUtxos.length; i++) {
tokenQty += tokenUtxos[i].tokenQty
}
// console.log(`tokenQty: ${tokenQty}`)
// Generate the OP_RETURN entry for an SLP SEND transaction.
//console.log(`Generating op-return.`)
// console.log(`Generating op-return.`)
const {
script,
outputs
@@ -529,7 +536,7 @@ class UtilRoute {
// is something unexpected happening.
if (outputs > 1) {
throw new Error(
`More than one class of token detected. Sweep feature not supported.`
'More than one class of token detected. Sweep feature not supported.'
)
}
@@ -575,7 +582,7 @@ class UtilRoute {
return hex
} catch (err) {
wlogger.error(`Error in util.js/sweepBCH().`)
wlogger.error('Error in util.js/sweepBCH().')
throw err
}
}
@@ -583,10 +590,10 @@ class UtilRoute {
const utilRoute = new UtilRoute()
router.get("/", utilRoute.root)
router.get("/validateAddress/:address", utilRoute.validateAddressSingle)
router.post("/validateAddress", utilRoute.validateAddressBulk)
router.post("/sweep", utilRoute.sweepWif)
router.get('/', utilRoute.root)
router.get('/validateAddress/:address', utilRoute.validateAddressSingle)
router.post('/validateAddress', utilRoute.validateAddressBulk)
router.post('/sweep', utilRoute.sweepWif)
module.exports = {
router,
+19 -19
View File
@@ -2,51 +2,51 @@
xpub route
*/
"use strict"
'use strict'
const express = require("express")
const axios = require("axios")
const routeUtils = require("./route-utils")
const wlogger = require("../../util/winston-logging")
const express = require('express')
// const axios = require('axios')
const routeUtils = require('./route-utils')
const wlogger = require('../../util/winston-logging')
//const router = express.Router()
// const router = express.Router()
const router = express.Router()
// Used for processing error messages before sending them to the user.
const util = require("util")
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const BCHJS = require("@chris.troutner/bch-js")
const BCHJS = require('@chris.troutner/bch-js')
const bchjs = new BCHJS()
// Connect the route endpoints to their handler functions.
router.get("/", root)
router.get("/fromXPub/:xpub", fromXPubSingle)
router.get('/', root)
router.get('/fromXPub/:xpub', fromXPubSingle)
// Root API endpoint. Simply acknowledges that it exists.
function root(req, res, next) {
return res.json({ status: "address" })
function root (req, res, next) {
return res.json({ status: 'address' })
}
async function fromXPubSingle(req, res, next) {
async function fromXPubSingle (req, res, next) {
try {
const xpub = req.params.xpub
const hdPath = req.query.hdPath ? req.query.hdPath : "0"
const hdPath = req.query.hdPath ? req.query.hdPath : '0'
if (!xpub || xpub === "") {
if (!xpub || xpub === '') {
res.status(400)
return res.json({ error: "xpub can not be empty" })
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."
error: 'xpub can not be an array. Use POST for bulk upload.'
})
}
wlogger.debug(`Executing address/fromXPub with this xpub: `, xpub)
wlogger.debug('Executing address/fromXPub with this xpub: ', xpub)
const cashAddr = bchjs.Address.fromXPub(xpub, hdPath)
const legacyAddr = bchjs.Address.toLegacyAddress(cashAddr)
@@ -64,7 +64,7 @@ async function fromXPubSingle(req, res, next) {
}
// Write out error to error log.
wlogger.error(`Error in address.ts/fromXPubSingle().`, err)
wlogger.error('Error in address.ts/fromXPubSingle().', err)
res.status(500)
return res.json({ error: util.inspect(err) })
+11 -11
View File
@@ -8,29 +8,29 @@
Blockbook.
*/
"use strict"
'use strict'
class BlockbookPath {
constructor() {
constructor () {
// defaults
this.addrPath = `${process.env.BLOCKBOOK_URL}api/v2/address/`
this.utxoPath = `${process.env.BLOCKBOOK_URL}api/v2/utxo/`
this.txPath = `${process.env.BLOCKBOOK_URL}api/v2/tx/`
}
toOpenBazaar() {
if (process.env.NETWORK === "testnet") {
this.addrPath = `https://tbch.blockbook.api.openbazaar.org/api/address/`
this.utxoPath = `https://tbch.blockbook.api.openbazaar.org/api/utxo/`
this.txPath = `https://tbch.blockbook.api.openbazaar.org/api/tx/`
toOpenBazaar () {
if (process.env.NETWORK === 'testnet') {
this.addrPath = 'https://tbch.blockbook.api.openbazaar.org/api/address/'
this.utxoPath = 'https://tbch.blockbook.api.openbazaar.org/api/utxo/'
this.txPath = 'https://tbch.blockbook.api.openbazaar.org/api/tx/'
} else {
this.addrPath = `https://bch.blockbook.api.openbazaar.org/api/address/`
this.utxoPath = `https://bch.blockbook.api.openbazaar.org/api/utxo/`
this.txPath = `https://bch.blockbook.api.openbazaar.org/api/tx/`
this.addrPath = 'https://bch.blockbook.api.openbazaar.org/api/address/'
this.utxoPath = 'https://bch.blockbook.api.openbazaar.org/api/utxo/'
this.txPath = 'https://bch.blockbook.api.openbazaar.org/api/tx/'
}
}
toDefault() {
toDefault () {
this.addrPath = `${process.env.BLOCKBOOK_URL}api/v2/address/`
this.utxoPath = `${process.env.BLOCKBOOK_URL}api/v2/utxo/`
this.txPath = `${process.env.BLOCKBOOK_URL}api/v2/tx/`
+9 -9
View File
@@ -4,33 +4,33 @@
logging library.
*/
"use strict"
'use strict'
var winston = require("winston")
require("winston-daily-rotate-file")
var winston = require('winston')
require('winston-daily-rotate-file')
var NETWORK = process.env.NETWORK
// Configure daily-rotation transport.
var transport = new winston.transports.DailyRotateFile({
filename: `${__dirname}/../../logs/rest-${NETWORK}-%DATE%.log`,
datePattern: "YYYY-MM-DD",
datePattern: 'YYYY-MM-DD',
zippedArchive: false,
maxSize: "1m",
maxFiles: "5d",
maxSize: '1m',
maxFiles: '5d',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
)
})
transport.on("rotate", function(oldFilename, newFilename) {
wlogger.info("Rotating log files")
transport.on('rotate', function (oldFilename, newFilename) {
wlogger.info('Rotating log files')
})
// This controls what goes into the log FILES
var wlogger = winston.createLogger({
level: "verbose",
level: 'verbose',
format: winston.format.json(),
transports: [
//