mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-22 17:22:04 -07:00
fix(linting): Switching to standard linter. Fixed linting errors
This commit is contained in:
+62
-61
@@ -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
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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) })
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) })
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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,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
@@ -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' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+94
-87
@@ -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
@@ -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
@@ -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/`
|
||||
|
||||
@@ -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: [
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user