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

This commit is contained in:
Chris Troutner
2020-02-16 06:38:25 -08:00
parent 7a6a033eb9
commit bb1bac5ada
48 changed files with 4547 additions and 3702 deletions
+6 -50
View File
@@ -1,54 +1,10 @@
{ {
"root": true, "extends": "standard",
"parserOptions": { "env": {
"ecmaVersion": 2018 "node": true,
"mocha": true
}, },
"plugins": ["prettier"], "parserOptions": {
"rules": { "ecmaVersion": 10
"no-debugger": ["warn"],
"no-regex-spaces": ["error"],
"no-unsafe-negation": ["error"],
"curly": ["error", "multi-or-nest", "consistent"],
"dot-location": ["error", "property"],
"dot-notation": ["error"],
"eqeqeq": ["error", "smart"],
"no-else-return": ["error"],
"no-extra-bind": ["error"],
"no-extra-label": ["error"],
"no-floating-decimal": ["error"],
"no-implicit-coercion": ["error", { "allow": ["!!"] }],
"no-lonely-if": ["off"],
"wrap-iife": ["error", "inside"],
"strict": ["error", "global"],
"func-call-spacing": ["error", "never"],
"comma-style": ["error", "last"],
"keyword-spacing": ["error"],
"linebreak-style": ["error", "unix"],
"new-parens": ["error"],
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1 }],
"no-whitespace-before-property": ["error"],
"semi": ["error", "never"],
"arrow-body-style": ["error", "as-needed"],
"arrow-parens": ["error", "as-needed"],
"arrow-spacing": ["error"],
"no-useless-computed-key": ["error"],
"no-useless-rename": ["error"],
"no-var": ["off"],
"prefer-spread": ["error"],
"prefer-template": ["error"],
"rest-spread-spacing": ["error", "never"],
"prefer-const": ["warn", { "destructuring": "all" }],
"no-unreachable": ["warn"],
"no-unused-vars": ["off"],
"prettier/prettier": [
"warn",
{
"printWidth": 80,
"trailingComma": "none",
"singleQuote": false,
"semi": false
}
]
} }
} }
+1164 -284
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -11,6 +11,7 @@
"start": "node ./src/app.js", "start": "node ./src/app.js",
"dev": "nodemon ./dist/app.js", "dev": "nodemon ./dist/app.js",
"test": "npm run test-v3", "test": "npm run test-v3",
"lint": "standard --env mocha --fix",
"test-v3": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 25000 test/v3/", "test-v3": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 25000 test/v3/",
"test:temp": "export NETWORK=mainnet && mocha --timeout 25000 test/v3/blockchain.js", "test:temp": "export NETWORK=mainnet && mocha --timeout 25000 test/v3/blockchain.js",
"test:integration": "mocha test/v3/integration", "test:integration": "mocha test/v3/integration",
@@ -48,7 +49,6 @@
"pg": "^7.11.0", "pg": "^7.11.0",
"pg-hstore": "^2.3.2", "pg-hstore": "^2.3.2",
"rate-limiter-flexible": "^2.0.0", "rate-limiter-flexible": "^2.0.0",
"strftime": "^0.10.0",
"winston": "^3.2.1", "winston": "^3.2.1",
"winston-daily-rotate-file": "^4.0.0" "winston-daily-rotate-file": "^4.0.0"
}, },
@@ -69,8 +69,9 @@
"proxyquire": "^2.1.0", "proxyquire": "^2.1.0",
"request": "^2.88.0", "request": "^2.88.0",
"request-promise": "^4.2.2", "request-promise": "^4.2.2",
"sinon": "^8.0.0",
"semantic-release": "^17.0.0", "semantic-release": "^17.0.0",
"sinon": "^8.0.0",
"standard": "^14.3.1",
"typescript": "^3.1.4" "typescript": "^3.1.4"
}, },
"release": { "release": {
+62 -61
View File
@@ -1,41 +1,41 @@
"use strict" 'use strict'
const express = require("express") const express = require('express')
// Middleware // Middleware
// const { routeRateLimit } = require("./middleware/route-ratelimit") // const { routeRateLimit } = require("./middleware/route-ratelimit")
const RateLimits = require("./middleware/route-ratelimit") const RateLimits = require('./middleware/route-ratelimit')
const rateLimits = new RateLimits() const rateLimits = new RateLimits()
const path = require("path") const path = require('path')
const logger = require("morgan") const logger = require('morgan')
const wlogger = require("./util/winston-logging") const wlogger = require('./util/winston-logging')
const cookieParser = require("cookie-parser") const cookieParser = require('cookie-parser')
const bodyParser = require("body-parser") const bodyParser = require('body-parser')
const basicAuth = require("express-basic-auth") // const basicAuth = require("express-basic-auth")
const helmet = require("helmet") const helmet = require('helmet')
const debug = require("debug")("rest-cloud:server") const debug = require('debug')('rest-cloud:server')
const http = require("http") const http = require('http')
const cors = require("cors") const cors = require('cors')
// Auth and rate limiting middleware libraries. // Auth and rate limiting middleware libraries.
const AuthMW = require("./middleware/auth") const AuthMW = require('./middleware/auth')
const jwtAuth = require("./middleware/jwt-auth") const jwtAuth = require('./middleware/jwt-auth')
// v3 // v3
const healthCheckV3 = require("./routes/v3/health-check") const healthCheckV3 = require('./routes/v3/health-check')
const BlockchainV3 = require("./routes/v3/full-node/blockchain") const BlockchainV3 = require('./routes/v3/full-node/blockchain')
const controlV3 = require("./routes/v3/full-node/control") const controlV3 = require('./routes/v3/full-node/control')
const miningV3 = require("./routes/v3/full-node/mining") const miningV3 = require('./routes/v3/full-node/mining')
const networkV3 = require("./routes/v3/full-node/network") const networkV3 = require('./routes/v3/full-node/network')
const rawtransactionsV3 = require("./routes/v3/full-node/rawtransactions") const rawtransactionsV3 = require('./routes/v3/full-node/rawtransactions')
const utilV3 = require("./routes/v3/util") const utilV3 = require('./routes/v3/util')
const slpV3 = require("./routes/v3/slp") const slpV3 = require('./routes/v3/slp')
const xpubV3 = require("./routes/v3/xpub") const xpubV3 = require('./routes/v3/xpub')
const blockbookV3 = require("./routes/v3/blockbook") const blockbookV3 = require('./routes/v3/blockbook')
const Ninsight = require("./routes/v3/ninsight") const Ninsight = require('./routes/v3/ninsight')
require("dotenv").config() require('dotenv').config()
// Instantiate route libraries. // Instantiate route libraries.
const blockchainV3 = new BlockchainV3() const blockchainV3 = new BlockchainV3()
@@ -44,28 +44,29 @@ const app = express()
app.locals.env = process.env app.locals.env = process.env
//app.use(swStats.getMiddleware({ swaggerSpec: apiSpec })) // app.use(swStats.getMiddleware({ swaggerSpec: apiSpec }))
app.use(helmet()) app.use(helmet())
app.use(cors()) app.use(cors())
app.enable("trust proxy") app.enable('trust proxy')
// view engine setup // view engine setup
app.set("views", path.join(__dirname, "views")) app.set('views', path.join(__dirname, 'views'))
app.set("view engine", "jade") app.set('view engine', 'jade')
// Mount the docs // 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. // Log each request to the console with IP addresses.
// app.use(logger("dev")) // 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)) app.use(logger(morganFormat))
// Log the same data to the winston logs. // Log the same data to the winston logs.
const logStream = { const logStream = {
write: function(message, encoding) { write: function (message, encoding) {
wlogger.info(`request: ${message}`) wlogger.info(`request: ${message}`)
} }
} }
@@ -74,10 +75,10 @@ app.use(logger(morganFormat, { stream: logStream }))
app.use(bodyParser.json()) app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false })) app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser()) app.use(cookieParser())
app.use(express.static(path.join(__dirname, "public"))) app.use(express.static(path.join(__dirname, 'public')))
// const v2prefix = "v2" // const v2prefix = "v2"
const v3prefix = "v3" const v3prefix = 'v3'
// Inspect the header for a JWT token. // Inspect the header for a JWT token.
app.use(`/${v3prefix}/`, jwtAuth.getTokenFromHeaders) app.use(`/${v3prefix}/`, jwtAuth.getTokenFromHeaders)
@@ -92,24 +93,24 @@ app.use(`/${v3prefix}/`, auth.mw())
// app.use(`/${v3prefix}/`, rateLimits.routeRateLimit) // app.use(`/${v3prefix}/`, rateLimits.routeRateLimit)
app.use(`/${v3prefix}/`, rateLimits.rateLimitByResource) app.use(`/${v3prefix}/`, rateLimits.rateLimitByResource)
app.use(`/${v3prefix}/` + `health-check`, healthCheckV3) app.use(`/${v3prefix}/` + 'health-check', healthCheckV3)
app.use(`/${v3prefix}/` + `blockchain`, blockchainV3.router) app.use(`/${v3prefix}/` + 'blockchain', blockchainV3.router)
app.use(`/${v3prefix}/` + `control`, controlV3.router) app.use(`/${v3prefix}/` + 'control', controlV3.router)
app.use(`/${v3prefix}/` + `mining`, miningV3.router) app.use(`/${v3prefix}/` + 'mining', miningV3.router)
app.use(`/${v3prefix}/` + `network`, networkV3) app.use(`/${v3prefix}/` + 'network', networkV3)
app.use(`/${v3prefix}/` + `rawtransactions`, rawtransactionsV3.router) app.use(`/${v3prefix}/` + 'rawtransactions', rawtransactionsV3.router)
app.use(`/${v3prefix}/` + `util`, utilV3.router) app.use(`/${v3prefix}/` + 'util', utilV3.router)
app.use(`/${v3prefix}/` + `slp`, slpV3.router) app.use(`/${v3prefix}/` + 'slp', slpV3.router)
app.use(`/${v3prefix}/` + `xpub`, xpubV3.router) app.use(`/${v3prefix}/` + 'xpub', xpubV3.router)
app.use(`/${v3prefix}/` + `blockbook`, blockbookV3.router) app.use(`/${v3prefix}/` + 'blockbook', blockbookV3.router)
const ninsight = new Ninsight() const ninsight = new Ninsight()
app.use(`/${v3prefix}/` + `ninsight`, ninsight.router) app.use(`/${v3prefix}/` + 'ninsight', ninsight.router)
// catch 404 and forward to error handler // catch 404 and forward to error handler
app.use((req, res, next) => { app.use((req, res, next) => {
const err = { const err = {
message: "Not Found", message: 'Not Found',
status: 404 status: 404
} }
@@ -122,7 +123,7 @@ app.use((err, req, res, next) => {
// set locals, only providing error in development // set locals, only providing error in development
res.locals.message = err.message 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 // render the error page
res.status(status) res.status(status)
@@ -135,8 +136,8 @@ app.use((err, req, res, next) => {
/** /**
* Get port from environment and store in Express. * Get port from environment and store in Express.
*/ */
const port = normalizePort(process.env.PORT || "3000") const port = normalizePort(process.env.PORT || '3000')
app.set("port", port) app.set('port', port)
console.log(`bch-api started on port ${port}`) console.log(`bch-api started on port ${port}`)
/** /**
@@ -149,8 +150,8 @@ const server = http.createServer(app)
*/ */
server.listen(port) server.listen(port)
server.on("error", onError) server.on('error', onError)
server.on("listening", onListening) server.on('listening', onListening)
// Set the time before a timeout error is generated. This impacts testing and // Set the time before a timeout error is generated. This impacts testing and
// the handling of timeout errors. Is 10 seconds too agressive? // 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. * Normalize a port into a number, string, or false.
*/ */
function normalizePort(val) { function normalizePort (val) {
const port = parseInt(val, 10) const port = parseInt(val, 10)
if (isNaN(port)) { if (isNaN(port)) {
@@ -179,18 +180,18 @@ function normalizePort(val) {
/** /**
* Event listener for HTTP server "error" event. * Event listener for HTTP server "error" event.
*/ */
function onError(error) { function onError (error) {
if (error.syscall !== "listen") throw 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 // handle specific listen errors with friendly messages
switch (error.code) { switch (error.code) {
case "EACCES": case 'EACCES':
console.error(`${bind} requires elevated privileges`) console.error(`${bind} requires elevated privileges`)
process.exit(1) process.exit(1)
break break
case "EADDRINUSE": case 'EADDRINUSE':
console.error(`${bind} is already in use`) console.error(`${bind} is already in use`)
process.exit(1) process.exit(1)
break break
@@ -203,9 +204,9 @@ function onError(error) {
* Event listener for HTTP server "listening" event. * Event listener for HTTP server "listening" event.
*/ */
function onListening() { function onListening () {
const addr = server.address() 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}`) debug(`Listening on ${bind}`)
} }
// //
+21 -21
View File
@@ -19,45 +19,45 @@
is set and passed to the route-ratelimits.ts middleware. is set and passed to the route-ratelimits.ts middleware.
*/ */
"use strict" 'use strict'
const passport = require("passport") const passport = require('passport')
const BasicStrategy = require("passport-http").BasicStrategy const BasicStrategy = require('passport-http').BasicStrategy
const AnonymousStrategy = require("passport-anonymous") const AnonymousStrategy = require('passport-anonymous')
const wlogger = require("../util/winston-logging") const wlogger = require('../util/winston-logging')
// Used for debugging and iterrogating JS objects. // Used for debugging and iterrogating JS objects.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
let _this // let _this
// Set default rate limit value for testing // 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 ':'. // 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 // Auth Middleware
class AuthMW { class AuthMW {
constructor() { constructor () {
_this = this // _this = this
// Initialize passport for 'anonymous' authentication. // Initialize passport for 'anonymous' authentication.
passport.use(new AnonymousStrategy()) passport.use(new AnonymousStrategy())
// Initialize passport for 'basic' authentication. // Initialize passport for 'basic' authentication.
passport.use( passport.use(
new BasicStrategy({ passReqToCallback: true }, function( new BasicStrategy({ passReqToCallback: true }, function (
req, req,
username, username,
password, password,
done done
) { ) {
//console.log(`req: ${util.inspect(req)}`) // console.log(`req: ${util.inspect(req)}`)
//console.log(`username: ${username}`) // console.log(`username: ${username}`)
//console.log(`password: ${password}`) // console.log(`password: ${password}`)
// Create the req.locals property if it does not yet exist. // Create the req.locals property if it does not yet exist.
if (!req.locals) { if (!req.locals) {
@@ -72,8 +72,8 @@ class AuthMW {
req.locals.proLimit = false req.locals.proLimit = false
// Evaluate the username and password and set the rate limit accordingly. // Evaluate the username and password and set the rate limit accordingly.
//if (username === "BITBOX" && password === PRO_PASS) { // if (username === "BITBOX" && password === PRO_PASS) {
if (username === "BITBOX") { if (username === 'BITBOX') {
for (let i = 0; i < PRO_PASS.length; i++) { for (let i = 0; i < PRO_PASS.length; i++) {
const thisPass = PRO_PASS[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) return done(null, true)
}) })
@@ -95,8 +95,8 @@ class AuthMW {
} }
// Middleware called by the route. // Middleware called by the route.
mw() { mw () {
return passport.authenticate(["basic", "anonymous"], { return passport.authenticate(['basic', 'anonymous'], {
session: false session: false
}) })
} }
+4 -4
View File
@@ -5,7 +5,7 @@
If found, will populate req.locals.jwtToken with the JWT token. 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. // 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 // 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 const authStr = req.headers.authorization
// If the header is proceeded by the word 'Token' // If the header is proceeded by the word 'Token'
if (authStr.split(" ")[0] === "Token") { if (authStr.split(' ')[0] === 'Token') {
const token = authStr.split(" ")[1] const token = authStr.split(' ')[1]
// console.log(`JWT found: ${token}`) // console.log(`JWT found: ${token}`)
@@ -36,7 +36,7 @@ const getTokenFromHeaders = (req, res, next) => {
} }
} }
} catch (err) { } catch (err) {
console.log(`Error in getTokenFromHeaders: `, err) console.log('Error in getTokenFromHeaders: ', err)
} }
next() next()
+59 -62
View File
@@ -1,28 +1,28 @@
"use strict" 'use strict'
const express = require("express") // const express = require('express')
const RateLimit = require("express-rate-limit") const RateLimit = require('express-rate-limit')
const axios = require("axios") const axios = require('axios')
const wlogger = require("../util/winston-logging") const wlogger = require('../util/winston-logging')
const jwt = require("jsonwebtoken") const jwt = require('jsonwebtoken')
const KeyEncoder = require("key-encoder").default const KeyEncoder = require('key-encoder').default
const keyEncoder = new KeyEncoder("secp256k1") const keyEncoder = new KeyEncoder('secp256k1')
// Redis // Redis
const redisOptions = { const redisOptions = {
enableOfflineQueue: false, enableOfflineQueue: false,
port: process.env.REDIS_PORT ? process.env.REDIS_PORT : 6379, 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)}`) console.log(`redisOptions: ${JSON.stringify(redisOptions, null, 2)}`)
const Redis = require("ioredis") const Redis = require('ioredis')
const redisClient = new Redis(redisOptions) const redisClient = new Redis(redisOptions)
// Rate limiter middleware lib. // Rate limiter middleware lib.
const { RateLimiterRedis } = require("rate-limiter-flexible") const { RateLimiterRedis } = require('rate-limiter-flexible')
const rateLimitOptions = { const rateLimitOptions = {
storeClient: redisClient, storeClient: redisClient,
points: 100, // Number of points 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 // This hard-coded value is temporary. It will be swapped out with an environment
// variable when moved to production. // variable when moved to production.
const publicKey = const publicKey =
"03e6c358092a459f7da9420de770eef3e16cf3c9c54a3d3d14ac2d7f0b82af4d7d" '03e6c358092a459f7da9420de770eef3e16cf3c9c54a3d3d14ac2d7f0b82af4d7d'
// Set max requests per minute // Set max requests per minute
const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS
@@ -48,7 +48,7 @@ const uniqueRateLimits = {}
let _this let _this
class RateLimits { class RateLimits {
constructor() { constructor () {
_this = this _this = this
this.jwt = jwt this.jwt = jwt
@@ -57,7 +57,7 @@ class RateLimits {
// Used to disconnect from the Redis DB. // Used to disconnect from the Redis DB.
// Called by unit tests so that node.js thread doesn't live forever. // Called by unit tests so that node.js thread doesn't live forever.
closeRedis() { closeRedis () {
redisClient.disconnect() redisClient.disconnect()
} }
@@ -79,7 +79,7 @@ class RateLimits {
will be downgraded to 0 on-the-fly. Indexer endpoints will effectively be will be downgraded to 0 on-the-fly. Indexer endpoints will effectively be
downgraded to the anonymous access tier. 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 // Disable rate limiting if 0 passed from RATE_LIMIT_MAX_REQUESTS
if (maxRequests === 0) return next() if (maxRequests === 0) return next()
@@ -87,7 +87,7 @@ class RateLimits {
if (!req.locals) { if (!req.locals) {
req.locals = { req.locals = {
// default values // default values
jwtToken: "", jwtToken: '',
proLimit: false, proLimit: false,
apiLevel: 0 apiLevel: 0
} }
@@ -95,9 +95,9 @@ class RateLimits {
// Warn if JWT_AUTH_SERVER env var is not set. // Warn if JWT_AUTH_SERVER env var is not set.
const authServer = process.env.JWT_AUTH_SERVER const authServer = process.env.JWT_AUTH_SERVER
if (!authServer || authServer === "") { if (!authServer || authServer === '') {
console.warn( 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 { } else {
// If a JWT token is passed in, validate it and enable pro-tier rate limits // If a JWT token is passed in, validate it and enable pro-tier rate limits
@@ -129,7 +129,7 @@ class RateLimits {
} }
// Current route // Current route
const rateLimitTier = req.locals.proLimit ? "PRO" : "BASIC" const rateLimitTier = req.locals.proLimit ? 'PRO' : 'BASIC'
const path = req.baseUrl + req.path const path = req.baseUrl + req.path
// Create a unique string as a route identifier. // Create a unique string as a route identifier.
@@ -138,10 +138,10 @@ class RateLimits {
req.method + req.method +
req.locals.apiLevel + // Generates new rate limit when user upgrades JWT token. req.locals.apiLevel + // Generates new rate limit when user upgrades JWT token.
path path
.split("/") .split('/')
.slice(0, 4) .slice(0, 4)
.join("/") .join('/')
//console.log(`route identifier: ${JSON.stringify(route, null, 2)}`) // console.log(`route identifier: ${JSON.stringify(route, null, 2)}`)
// console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) // console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`)
@@ -166,8 +166,8 @@ class RateLimits {
windowMs: 60 * 1000, // 1 minute window windowMs: 60 * 1000, // 1 minute window
delayMs: 0, // disable delaying - full speed until the max limit is reached delayMs: 0, // disable delaying - full speed until the max limit is reached
max: PRO_RPM, // start blocking after this many requests per minute max: PRO_RPM, // start blocking after this many requests per minute
handler: function(req, res) { handler: function (req, res) {
//console.log(`pro-tier rate-handler triggered.`) // console.log(`pro-tier rate-handler triggered.`)
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ return res.json({
@@ -188,8 +188,8 @@ class RateLimits {
windowMs: 60 * 1000, // 1 minute window windowMs: 60 * 1000, // 1 minute window
delayMs: 0, // disable delaying - full speed until the max limit is reached delayMs: 0, // disable delaying - full speed until the max limit is reached
max: maxRequests, // start blocking after maxRequests max: maxRequests, // start blocking after maxRequests
handler: function(req, res) { handler: function (req, res) {
//console.log(`freemium rate-handler triggered.`) // console.log(`freemium rate-handler triggered.`)
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ 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 // Call rate limit for this route
uniqueRateLimits[route](req, res, next) 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 // 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 // uses its output to adjust rate limits on-the-fly based on the users
// permission level. // permission level.
evalUserPermissioins(req, authData) { evalUserPermissioins (req, authData) {
// console.log(`authData: ${JSON.stringify(authData, null, 2)}`) // console.log(`authData: ${JSON.stringify(authData, null, 2)}`)
// Return object with default values // Return object with default values
@@ -224,9 +224,9 @@ class RateLimits {
// if apiLevel = 0 (free tier), then return the default values. // if apiLevel = 0 (free tier), then return the default values.
if (retObj.apiLevel === 0) return retObj 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)}`) // console.log(`locals: ${JSON.stringify(locals, null, 2)}`)
const url = req.url const url = req.url
// console.log(`url: ${JSON.stringify(url, null, 2)}`) // 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 // 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 // library. It uses fine-grain rate limiting based on the resources being
// consumed. // consumed.
async rateLimitByResource(req, res, next) { async rateLimitByResource (req, res, next) {
try { try {
let userId let userId
let decoded = {} let decoded = {}
@@ -258,7 +258,7 @@ class RateLimits {
if (!req.locals) { if (!req.locals) {
req.locals = { req.locals = {
// default values // default values
jwtToken: "", jwtToken: '',
proLimit: false, proLimit: false,
apiLevel: 0 apiLevel: 0
} }
@@ -267,10 +267,10 @@ class RateLimits {
// Decode the JWT token if one exists. // Decode the JWT token if one exists.
if (req.locals.jwtToken) { if (req.locals.jwtToken) {
const jwtOptions = { const jwtOptions = {
algorithms: ["ES256"] algorithms: ['ES256']
} }
const pemPublicKey = keyEncoder.encodePublic(publicKey, "raw", "pem") const pemPublicKey = keyEncoder.encodePublic(publicKey, 'raw', 'pem')
// Validate the JWT token. // Validate the JWT token.
decoded = _this.jwt.verify( decoded = _this.jwt.verify(
@@ -282,7 +282,7 @@ class RateLimits {
userId = decoded.id userId = decoded.id
} else { } else {
wlogger.debug(`No JWT token found!`) wlogger.debug('No JWT token found!')
} }
// Code here for the rate limiter is adapted from this example: // Code here for the rate limiter is adapted from this example:
@@ -292,7 +292,7 @@ class RateLimits {
const resource = _this.getResource(req.url) const resource = _this.getResource(req.url)
wlogger.debug(`resource: ${resource}`) wlogger.debug(`resource: ${resource}`)
let key = userId ? userId : req.ip let key = userId || req.ip
// const pointsToConsume = userId ? 1 : 30 // const pointsToConsume = userId ? 1 : 30
decoded.resource = resource decoded.resource = resource
@@ -316,7 +316,7 @@ class RateLimits {
}) })
} }
} catch (err) { } catch (err) {
wlogger.error(`Error in route-ratelimit.js/newRateLimit(): `, err) wlogger.error('Error in route-ratelimit.js/newRateLimit(): ', err)
// throw err // throw err
} }
@@ -325,7 +325,7 @@ class RateLimits {
// Calculates the points consumed, based on the jwt information and the route // Calculates the points consumed, based on the jwt information and the route
// requested. // requested.
calcPoints(jwtInfo) { calcPoints (jwtInfo) {
let retVal = 30 // By default, use anonymous tier. let retVal = 30 // By default, use anonymous tier.
try { try {
@@ -334,8 +334,8 @@ class RateLimits {
const apiLevel = jwtInfo.apiLevel const apiLevel = jwtInfo.apiLevel
const resource = jwtInfo.resource const resource = jwtInfo.resource
const level30Routes = ["insight", "bitcore", "blockbook"] const level30Routes = ['insight', 'bitcore', 'blockbook']
const level40Routes = ["slp"] const level40Routes = ['slp']
wlogger.debug(`apiLevel: ${apiLevel}`) wlogger.debug(`apiLevel: ${apiLevel}`)
@@ -346,28 +346,25 @@ class RateLimits {
if (apiLevel >= 40) retVal = 1 if (apiLevel >= 40) retVal = 1
// else if (apiLevel >= 10) retVal = 10 // else if (apiLevel >= 10) retVal = 10
else retVal = 10 else retVal = 10
}
// Normal indexer routes // Normal indexer routes
else if (level30Routes.includes(resource)) { } else if (level30Routes.includes(resource)) {
if (apiLevel >= 30) retVal = 1 if (apiLevel >= 30) retVal = 1
else retVal = 10 else retVal = 10
}
// Full node tier // Full node tier
else if (apiLevel >= 20) { } else if (apiLevel >= 20) {
retVal = 1 retVal = 1
}
// Free tier, full node only. // Free tier, full node only.
else { } else {
retVal = 10 retVal = 10
} }
} }
return retVal return retVal
} catch (err) { } catch (err) {
wlogger.error(`Error in route-ratelimit.js/calcPoints()`) wlogger.error('Error in route-ratelimit.js/calcPoints()')
// throw err // throw err
retVal = 30 retVal = 30
} }
@@ -379,16 +376,16 @@ class RateLimits {
// the user is requesting. // the user is requesting.
// This was created as a function so that it can be unit tested. Not sure // This was created as a function so that it can be unit tested. Not sure
// what kind of variations will be seen in production. // what kind of variations will be seen in production.
getResource(url) { getResource (url) {
try { try {
wlogger.debug(`url: ${JSON.stringify(url, null, 2)}`) wlogger.debug(`url: ${JSON.stringify(url, null, 2)}`)
const splitUrl = url.split("/") const splitUrl = url.split('/')
const resource = splitUrl[1] const resource = splitUrl[1]
return resource return resource
} catch (err) { } catch (err) {
wlogger.error(`Error in getResource().`) wlogger.error('Error in getResource().')
throw err throw err
} }
} }
@@ -397,7 +394,7 @@ class RateLimits {
// potentially be used by Bitcoin.com. // potentially be used by Bitcoin.com.
// Rather than using apiLevel, the rateLimit is explicitly recorded in the // Rather than using apiLevel, the rateLimit is explicitly recorded in the
// JWT token. // JWT token.
async rateLimitSimple(req, res, next) { async rateLimitSimple (req, res, next) {
try { try {
let userId let userId
let decoded = {} let decoded = {}
@@ -406,7 +403,7 @@ class RateLimits {
if (!req.locals) { if (!req.locals) {
req.locals = { req.locals = {
// default values // default values
jwtToken: "", jwtToken: '',
proLimit: false, proLimit: false,
rateLimit: 3 rateLimit: 3
} }
@@ -415,10 +412,10 @@ class RateLimits {
// Decode the JWT token if one exists. // Decode the JWT token if one exists.
if (req.locals.jwtToken) { if (req.locals.jwtToken) {
const jwtOptions = { const jwtOptions = {
algorithms: ["ES256"] algorithms: ['ES256']
} }
const pemPublicKey = keyEncoder.encodePublic(publicKey, "raw", "pem") const pemPublicKey = keyEncoder.encodePublic(publicKey, 'raw', 'pem')
// Validate the JWT token. // Validate the JWT token.
decoded = _this.jwt.verify( decoded = _this.jwt.verify(
@@ -430,14 +427,14 @@ class RateLimits {
userId = decoded.id userId = decoded.id
} else { } else {
wlogger.debug(`No JWT token found!`) wlogger.debug('No JWT token found!')
} }
// Code here for the rate limiter is adapted from this example: // 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 // https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#authorized-and-not-authorized-users
try { try {
// Key for Redis key/value pair. // Key for Redis key/value pair.
const key = userId ? userId : req.ip const key = userId || req.ip
const pointsToConsume = _this.calcPoints2(decoded) const pointsToConsume = _this.calcPoints2(decoded)
@@ -454,7 +451,7 @@ class RateLimits {
}) })
} }
} catch (err) { } catch (err) {
wlogger.error(`Error in route-ratelimit.js/rateLimitSimple(): `, err) wlogger.error('Error in route-ratelimit.js/rateLimitSimple(): ', err)
// throw err // throw err
} }
@@ -463,7 +460,7 @@ class RateLimits {
// Calculates the points consumed, based on the explicit rateLimit defined // Calculates the points consumed, based on the explicit rateLimit defined
// in the JWT token. // in the JWT token.
calcPoints2(jwtInfo) { calcPoints2 (jwtInfo) {
let retVal = 30 // By default, use anonymous tier. let retVal = 30 // By default, use anonymous tier.
try { try {
@@ -480,7 +477,7 @@ class RateLimits {
retVal = points retVal = points
} }
} catch (err) { } catch (err) {
wlogger.error(`Error in route-ratelimit.js/calcPoints2()`) wlogger.error('Error in route-ratelimit.js/calcPoints2()')
// throw err // throw err
retVal = 30 retVal = 30
} }
+77 -73
View File
@@ -2,49 +2,49 @@
Blockbook API route Blockbook API route
*/ */
"use strict" 'use strict'
const express = require("express") const express = require('express')
const axios = require("axios") const axios = require('axios')
const routeUtils = require("./route-utils") const routeUtils = require('./route-utils')
const wlogger = require("../../util/winston-logging") const wlogger = require('../../util/winston-logging')
// Library for easily switching the API paths to use different instances of // Library for easily switching the API paths to use different instances of
// Blockbook. // Blockbook.
const BlockbookPath = require("../../util/blockbook-path") const BlockbookPath = require('../../util/blockbook-path')
const BLOCKBOOKPATH = new BlockbookPath() const BLOCKBOOKPATH = new BlockbookPath()
// BLOCKBOOKPATH.toOpenBazaar() // BLOCKBOOKPATH.toOpenBazaar()
const router = express.Router() const router = express.Router()
// Used for processing error messages before sending them to the user. // Used for processing error messages before sending them to the user.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } 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 = new BCHJS()
//const BLOCKBOOK_URL = process.env.BLOCKBOOK_URL // const BLOCKBOOK_URL = process.env.BLOCKBOOK_URL
// Connect the route endpoints to their handler functions. // Connect the route endpoints to their handler functions.
router.get("/", root) router.get('/', root)
router.get("/balance/:address", balanceSingle) router.get('/balance/:address', balanceSingle)
router.post("/balance", balanceBulk) router.post('/balance', balanceBulk)
router.get("/utxos/:address", utxosSingle) router.get('/utxos/:address', utxosSingle)
router.post("/utxos", utxosBulk) router.post('/utxos', utxosBulk)
router.get("/tx/:txid", txSingle) router.get('/tx/:txid', txSingle)
router.post("/tx", txBulk) router.post('/tx', txBulk)
// Root API endpoint. Simply acknowledges that it exists. // Root API endpoint. Simply acknowledges that it exists.
function root(req, res, next) { function root (req, res, next) {
return res.json({ status: "address" }) return res.json({ status: 'address' })
} }
// Query the Blockbook Node API for a balance on a single BCH address. // Query the Blockbook Node API for a balance on a single BCH address.
// Returns a Promise. // Returns a Promise.
async function balanceFromBlockbook(thisAddress) { async function balanceFromBlockbook (thisAddress) {
try { try {
//console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`) // console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
// Convert the address to a cashaddr without a prefix. // Convert the address to a cashaddr without a prefix.
const addr = bchjs.Address.toCashAddress(thisAddress) const addr = bchjs.Address.toCashAddress(thisAddress)
@@ -55,12 +55,13 @@ async function balanceFromBlockbook(thisAddress) {
// Query the Blockbook Node API. // Query the Blockbook Node API.
const axiosResponse = await axios.get(path) const axiosResponse = await axios.get(path)
const retData = axiosResponse.data const retData = axiosResponse.data
//console.log(`retData: ${util.inspect(retData)}`) // console.log(`retData: ${util.inspect(retData)}`)
return retData return retData
} catch (err) { } catch (err) {
// Dev Note: Do not log error messages here. Throw them instead and let the // Dev Note: Do not log error messages here. Throw them instead and let the
// parent function handle it. // parent function handle it.
wlogger.debug('Error in blockbook.js/balanceFromBlockbook()')
throw err throw err
} }
} }
@@ -77,31 +78,32 @@ async function balanceFromBlockbook(thisAddress) {
* *
*/ */
// GET handler for single balance // GET handler for single balance
async function balanceSingle(req, res, next) { async function balanceSingle (req, res, next) {
try { try {
const address = req.params.address const address = req.params.address
if (!address || address === "") { if (!address || address === '') {
res.status(400) 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. // Reject if address is an array.
if (Array.isArray(address)) { if (Array.isArray(address)) {
res.status(400) res.status(400)
return res.json({ 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( wlogger.debug(
`Executing blockbook/balanceSingle with this address: `, 'Executing blockbook/balanceSingle with this address: ',
address address
) )
// Ensure the input is a valid BCH address. // Ensure the input is a valid BCH address.
try { try {
const legacyAddr = bchjs.Address.toLegacyAddress(address) // const legacyAddr = bchjs.Address.toLegacyAddress(address)
bchjs.Address.toLegacyAddress(address)
} catch (err) { } catch (err) {
res.status(400) res.status(400)
return res.json({ return res.json({
@@ -114,7 +116,7 @@ async function balanceSingle(req, res, next) {
if (!networkIsValid) { if (!networkIsValid) {
res.status(400) res.status(400)
return res.json({ 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. // 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) res.status(500)
return res.json({ error: util.inspect(err) }) 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 // POST handler for bulk queries on address details
async function balanceBulk(req, res, next) { async function balanceBulk (req, res, next) {
try { try {
let addresses = req.body.addresses 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. // Reject if addresses is not an array.
if (!Array.isArray(addresses)) { if (!Array.isArray(addresses)) {
res.status(400) res.status(400)
return res.json({ 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)) { if (!routeUtils.validateArraySize(req, addresses)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ return res.json({
error: `Array too large.` error: 'Array too large.'
}) })
} }
wlogger.debug( wlogger.debug(
`Executing blockbook.js/balanceBulk with these addresses: `, 'Executing blockbook.js/balanceBulk with these addresses: ',
addresses addresses
) )
@@ -206,7 +208,7 @@ async function balanceBulk(req, res, next) {
// Loops through each address and creates an array of Promises, querying // Loops through each address and creates an array of Promises, querying
// Insight API in parallel. // Insight API in parallel.
addresses = addresses.map(async (address, index) => addresses = addresses.map(async (address, index) =>
//console.log(`address: ${address}`) // console.log(`address: ${address}`)
balanceFromBlockbook(address) balanceFromBlockbook(address)
) )
@@ -224,7 +226,7 @@ async function balanceBulk(req, res, next) {
return res.json({ error: msg }) return res.json({ error: msg })
} }
wlogger.error(`Error in blockbook.js/balanceBulk().`, err) wlogger.error('Error in blockbook.js/balanceBulk().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) 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. // Query the Blockbook API for utxos associated with a BCH address.
// Returns a Promise. // Returns a Promise.
async function utxosFromBlockbook(thisAddress) { async function utxosFromBlockbook (thisAddress) {
try { try {
//console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`) // console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
// Convert the address to a cashaddr without a prefix. // Convert the address to a cashaddr without a prefix.
const addr = bchjs.Address.toCashAddress(thisAddress) const addr = bchjs.Address.toCashAddress(thisAddress)
@@ -249,13 +251,13 @@ async function utxosFromBlockbook(thisAddress) {
// console.log(`retData: ${util.inspect(retData)}`) // console.log(`retData: ${util.inspect(retData)}`)
// Add the satoshis property to each UTXO. // Add the satoshis property to each UTXO.
for (let i = 0; i < retData.length; i++) for (let i = 0; i < retData.length; i++) { retData[i].satoshis = Number(retData[i].value) }
retData[i].satoshis = Number(retData[i].value)
return retData return retData
} catch (err) { } catch (err) {
// Dev Note: Do not log error messages here. Throw them instead and let the // Dev Note: Do not log error messages here. Throw them instead and let the
// parent function handle it. // parent function handle it.
wlogger.debug('Error in blockbook.js/utxosFromBlockbook()')
throw err throw err
} }
} }
@@ -272,31 +274,32 @@ async function utxosFromBlockbook(thisAddress) {
* *
*/ */
// GET handler for single balance // GET handler for single balance
async function utxosSingle(req, res, next) { async function utxosSingle (req, res, next) {
try { try {
const address = req.params.address const address = req.params.address
if (!address || address === "") { if (!address || address === '') {
res.status(400) 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. // Reject if address is an array.
if (Array.isArray(address)) { if (Array.isArray(address)) {
res.status(400) res.status(400)
return res.json({ 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( wlogger.debug(
`Executing blockbook/utxosSingle with this address: `, 'Executing blockbook/utxosSingle with this address: ',
address address
) )
// Ensure the input is a valid BCH address. // Ensure the input is a valid BCH address.
try { try {
const legacyAddr = bchjs.Address.toLegacyAddress(address) // const legacyAddr = bchjs.Address.toLegacyAddress(address)
bchjs.Address.toLegacyAddress(address)
} catch (err) { } catch (err) {
res.status(400) res.status(400)
return res.json({ return res.json({
@@ -309,7 +312,7 @@ async function utxosSingle(req, res, next) {
if (!networkIsValid) { if (!networkIsValid) {
res.status(400) res.status(400)
return res.json({ 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. // 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) res.status(500)
return res.json({ error: util.inspect(err) }) 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 // POST handler for bulk queries on address utxos
async function utxosBulk(req, res, next) { async function utxosBulk (req, res, next) {
try { try {
let addresses = req.body.addresses 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. // Reject if addresses is not an array.
if (!Array.isArray(addresses)) { if (!Array.isArray(addresses)) {
res.status(400) res.status(400)
return res.json({ 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)) { if (!routeUtils.validateArraySize(req, addresses)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ return res.json({
error: `Array too large.` error: 'Array too large.'
}) })
} }
wlogger.debug( wlogger.debug(
`Executing blockbook.js/utxosBulk with these addresses: `, 'Executing blockbook.js/utxosBulk with these addresses: ',
addresses addresses
) )
@@ -401,7 +404,7 @@ async function utxosBulk(req, res, next) {
// Loops through each address and creates an array of Promises, querying // Loops through each address and creates an array of Promises, querying
// Insight API in parallel. // Insight API in parallel.
addresses = addresses.map(async (address, index) => addresses = addresses.map(async (address, index) =>
//console.log(`address: ${address}`) // console.log(`address: ${address}`)
utxosFromBlockbook(address) utxosFromBlockbook(address)
) )
@@ -419,7 +422,7 @@ async function utxosBulk(req, res, next) {
return res.json({ error: msg }) return res.json({ error: msg })
} }
wlogger.error(`Error in blockbook.js/utxosBulk().`, err) wlogger.error('Error in blockbook.js/utxosBulk().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) 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. // Query the Blockbook Node API for transactions on a single TXID.
// Returns a Promise. // Returns a Promise.
async function transactionsFromBlockbook(txid) { async function transactionsFromBlockbook (txid) {
try { try {
//console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`) // console.log(`BLOCKBOOK_URL: ${BLOCKBOOK_URL}`)
const path = `${BLOCKBOOKPATH.txPath}${txid}` const path = `${BLOCKBOOKPATH.txPath}${txid}`
// console.log(`path: ${path}`) // console.log(`path: ${path}`)
@@ -438,12 +441,13 @@ async function transactionsFromBlockbook(txid) {
// Query the Blockbook Node API. // Query the Blockbook Node API.
const axiosResponse = await axios.get(path) const axiosResponse = await axios.get(path)
const retPromise = axiosResponse.data const retPromise = axiosResponse.data
//console.log(`retData: ${util.inspect(retData)}`) // console.log(`retData: ${util.inspect(retData)}`)
return retPromise return retPromise
} catch (err) { } catch (err) {
// Dev Note: Do not log error messages here. Throw them instead and let the // Dev Note: Do not log error messages here. Throw them instead and let the
// parent function handle it. // parent function handle it.
wlogger.debug('Error in blockbook.js/transactionsFromBlockbook()')
throw err throw err
} }
} }
@@ -460,20 +464,20 @@ async function transactionsFromBlockbook(txid) {
* *
*/ */
// GET handler for single transaction details. // GET handler for single transaction details.
async function txSingle(req, res, next) { async function txSingle (req, res, next) {
try { try {
const txid = req.params.txid const txid = req.params.txid
if (!txid || txid === "") { if (!txid || txid === '') {
res.status(400) 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. // Reject if address is an array.
if (Array.isArray(txid)) { if (Array.isArray(txid)) {
res.status(400) res.status(400)
return res.json({ 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. // Query the Blockbook Node API.
const retData = await transactionsFromBlockbook(txid) const retData = await transactionsFromBlockbook(txid)
@@ -502,7 +506,7 @@ async function txSingle(req, res, next) {
} }
// Write out error to error log. // 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) res.status(500)
return res.json({ error: util.inspect(err) }) 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 // POST handler for bulk queries on tx details
async function txBulk(req, res, next) { async function txBulk (req, res, next) {
try { try {
let txids = req.body.txids 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. // Reject if txids is not an array.
if (!Array.isArray(txids)) { if (!Array.isArray(txids)) {
res.status(400) res.status(400)
return res.json({ 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)) { if (!routeUtils.validateArraySize(req, txids)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ 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. // Validate each element in the txids array.
for (let i = 0; i < txids.length; i++) { for (let i = 0; i < txids.length; i++) {
const thisTxid = txids[i] const thisTxid = txids[i]
if (!thisTxid || thisTxid === "") { if (!thisTxid || thisTxid === '') {
res.status(400) 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. // 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 // Loops through each address and creates an array of Promises, querying
// Insight API in parallel. // Insight API in parallel.
txids = txids.map(async (txid, index) => txids = txids.map(async (txid, index) =>
//console.log(`address: ${address}`) // console.log(`address: ${address}`)
transactionsFromBlockbook(txid) transactionsFromBlockbook(txid)
) )
@@ -584,7 +588,7 @@ async function txBulk(req, res, next) {
return res.json({ error: msg }) return res.json({ error: msg })
} }
wlogger.error(`Error in blockbook.js/txBulk().`, err) wlogger.error('Error in blockbook.js/txBulk().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) return res.json({ error: util.inspect(err) })
+137 -135
View File
@@ -2,28 +2,28 @@
A library for interacting with the Full Node 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 router = express.Router()
const axios = require("axios") const axios = require('axios')
const wlogger = require("../../../util/winston-logging") const wlogger = require('../../../util/winston-logging')
const RouteUtils = require("../route-utils2") const RouteUtils = require('../route-utils2')
const routeUtils = new RouteUtils() const routeUtils = new RouteUtils()
// Used to convert error messages to strings, to safely pass to users. // 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 } 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 = new BCHJS()
let _this let _this
class Blockchain { class Blockchain {
constructor() { constructor () {
_this = this _this = this
this.bchjs = bchjs this.bchjs = bchjs
@@ -31,35 +31,35 @@ class Blockchain {
this.routeUtils = routeUtils this.routeUtils = routeUtils
this.router = router this.router = router
this.router.get("/", this.root) this.router.get('/', this.root)
this.router.get("/getBestBlockHash", this.getBestBlockHash) this.router.get('/getBestBlockHash', this.getBestBlockHash)
this.router.get("/getBlockchainInfo", this.getBlockchainInfo) this.router.get('/getBlockchainInfo', this.getBlockchainInfo)
this.router.get("/getBlockCount", this.getBlockCount) this.router.get('/getBlockCount', this.getBlockCount)
this.router.get("/getBlockHeader/:hash", this.getBlockHeaderSingle) this.router.get('/getBlockHeader/:hash', this.getBlockHeaderSingle)
this.router.post("/getBlockHeader", this.getBlockHeaderBulk) this.router.post('/getBlockHeader', this.getBlockHeaderBulk)
this.router.get("/getChainTips", this.getChainTips) this.router.get('/getChainTips', this.getChainTips)
this.router.get("/getDifficulty", this.getDifficulty) this.router.get('/getDifficulty', this.getDifficulty)
this.router.get("/getMempoolEntry/:txid", this.getMempoolEntrySingle) this.router.get('/getMempoolEntry/:txid', this.getMempoolEntrySingle)
this.router.post("/getMempoolEntry", this.getMempoolEntryBulk) this.router.post('/getMempoolEntry', this.getMempoolEntryBulk)
this.router.get( this.router.get(
"/getMempoolAncestors/:txid", '/getMempoolAncestors/:txid',
this.getMempoolAncestorsSingle this.getMempoolAncestorsSingle
) )
this.router.get("/getMempoolInfo", this.getMempoolInfo) this.router.get('/getMempoolInfo', this.getMempoolInfo)
this.router.get("/getRawMempool", this.getRawMempool) this.router.get('/getRawMempool', this.getRawMempool)
this.router.get("/getTxOut/:txid/:n", this.getTxOut) this.router.get('/getTxOut/:txid/:n', this.getTxOut)
this.router.get("/getTxOutProof/:txid", this.getTxOutProofSingle) this.router.get('/getTxOutProof/:txid', this.getTxOutProofSingle)
this.router.post("/getTxOutProof", this.getTxOutProofBulk) this.router.post('/getTxOutProof', this.getTxOutProofBulk)
this.router.get("/verifyTxOutProof/:proof", this.verifyTxOutProofSingle) this.router.get('/verifyTxOutProof/:proof', this.verifyTxOutProofSingle)
this.router.post("/verifyTxOutProof", this.verifyTxOutProofBulk) this.router.post('/verifyTxOutProof', this.verifyTxOutProofBulk)
} }
root(req, res, next) { root (req, res, next) {
return res.json({ status: "blockchain" }) return res.json({ status: 'blockchain' })
} }
// DRY error handler. // DRY error handler.
errorHandler(err, res) { errorHandler (err, res) {
// Attempt to decode the error message. // Attempt to decode the error message.
const { msg, status } = _this.routeUtils.decodeError(err) const { msg, status } = _this.routeUtils.decodeError(err)
if (msg) { if (msg) {
@@ -83,12 +83,12 @@ class Blockchain {
* *
* @apiSuccess {String} bestBlockHash 000000000000000002bc884334336d99c9a9c616670a9244c6a8c1fc35aa91a1 * @apiSuccess {String} bestBlockHash 000000000000000002bc884334336d99c9a9c616670a9244c6a8c1fc35aa91a1
*/ */
async getBestBlockHash(req, res, next) { async getBestBlockHash (req, res, next) {
try { try {
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getbestblockhash" options.data.id = 'getbestblockhash'
options.data.method = "getbestblockhash" options.data.method = 'getbestblockhash'
options.data.params = [] options.data.params = []
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -97,7 +97,7 @@ class Blockchain {
return res.json(response.data.result) return res.json(response.data.result)
} catch (err) { } catch (err) {
// Write out error to error log. // 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) return _this.errorHandler(err, res)
} }
@@ -128,12 +128,12 @@ class Blockchain {
* @apiSuccess {Object} object.softforks.reject * @apiSuccess {Object} object.softforks.reject
* @apiSuccess {String} object.softforks.reject.status true * @apiSuccess {String} object.softforks.reject.status true
*/ */
async getBlockchainInfo(req, res, next) { async getBlockchainInfo (req, res, next) {
try { try {
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getblockchaininfo" options.data.id = 'getblockchaininfo'
options.data.method = "getblockchaininfo" options.data.method = 'getblockchaininfo'
options.data.params = [] options.data.params = []
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -141,7 +141,7 @@ class Blockchain {
return res.json(response.data.result) return res.json(response.data.result)
} catch (err) { } catch (err) {
// Write out error to error log. // 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) return _this.errorHandler(err, res)
} }
@@ -158,12 +158,12 @@ class Blockchain {
* *
* @apiSuccess {Number} bestBlockCount 587665 * @apiSuccess {Number} bestBlockCount 587665
*/ */
async getBlockCount(req, res, next) { async getBlockCount (req, res, next) {
try { try {
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getblockcount" options.data.id = 'getblockcount'
options.data.method = "getblockcount" options.data.method = 'getblockcount'
options.data.params = [] options.data.params = []
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -172,7 +172,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
@@ -208,22 +208,23 @@ class Blockchain {
* @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523" * @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523"
* @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3" * @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3"
*/ */
async getBlockHeaderSingle(req, res, next) { async getBlockHeaderSingle (req, res, next) {
try { try {
let verbose = false let verbose = false
if (req.query.verbose && req.query.verbose.toString() === "true") if (req.query.verbose && req.query.verbose.toString() === 'true') {
verbose = true verbose = true
}
const hash = req.params.hash const hash = req.params.hash
if (!hash || hash === "") { if (!hash || hash === '') {
res.status(400) res.status(400)
return res.json({ error: "hash can not be empty" }) return res.json({ error: 'hash can not be empty' })
} }
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getblockheader" options.data.id = 'getblockheader'
options.data.method = "getblockheader" options.data.method = 'getblockheader'
options.data.params = [hash, verbose] options.data.params = [hash, verbose]
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -232,7 +233,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
@@ -268,7 +269,7 @@ class Blockchain {
* @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523" * @apiSuccess {String} object.previousblockhash "0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523"
* @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3" * @apiSuccess {String} object.nextblockhash "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3"
*/ */
async getBlockHeaderBulk(req, res, next) { async getBlockHeaderBulk (req, res, next) {
try { try {
const hashes = req.body.hashes const hashes = req.body.hashes
const verbose = req.body.verbose ? req.body.verbose : false const verbose = req.body.verbose ? req.body.verbose : false
@@ -276,7 +277,7 @@ class Blockchain {
if (!Array.isArray(hashes)) { if (!Array.isArray(hashes)) {
res.status(400) res.status(400)
return res.json({ 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)) { if (!routeUtils.validateArraySize(req, hashes)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ return res.json({
error: "Array too large." error: 'Array too large.'
}) })
} }
wlogger.debug( wlogger.debug(
"Executing blockchain/getBlockHeaderBulk with these hashes: ", 'Executing blockchain/getBlockHeaderBulk with these hashes: ',
hashes hashes
) )
@@ -308,11 +309,11 @@ class Blockchain {
// Loop through each hash and creates an array of requests to call in parallel // Loop through each hash and creates an array of requests to call in parallel
const promises = hashes.map(async hash => { const promises = hashes.map(async hash => {
options.data.id = "getblockheader" options.data.id = 'getblockheader'
options.data.method = "getblockheader" options.data.method = 'getblockheader'
options.data.params = [hash, verbose] options.data.params = [hash, verbose]
return await _this.axios.request(options) return _this.axios.request(options)
}) })
const axiosResult = await _this.axios.all(promises) const axiosResult = await _this.axios.all(promises)
@@ -325,7 +326,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) 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" * 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 { try {
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getchaintips" options.data.id = 'getchaintips'
options.data.method = "getchaintips" options.data.method = 'getchaintips'
options.data.params = [] options.data.params = []
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -356,7 +357,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) 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" * 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 { try {
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getdifficulty" options.data.id = 'getdifficulty'
options.data.method = "getdifficulty" options.data.method = 'getdifficulty'
options.data.params = [] options.data.params = []
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -388,7 +389,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) 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" * 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 { try {
// Validate input parameter // Validate input parameter
const txid = req.params.txid const txid = req.params.txid
if (!txid || txid === "") { if (!txid || txid === '') {
res.status(400) res.status(400)
return res.json({ error: "txid can not be empty" }) return res.json({ error: 'txid can not be empty' })
} }
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getmempoolentry" options.data.id = 'getmempoolentry'
options.data.method = "getmempoolentry" options.data.method = 'getmempoolentry'
options.data.params = [txid] options.data.params = [txid]
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -427,7 +428,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
@@ -442,14 +443,14 @@ class Blockchain {
* @apiExample Example usage: * @apiExample Example usage:
* curl -X POST https://mainnet.bchjs.cash/v3/blockchain/getMempoolEntry -H "Content-Type: application/json" -d "{\"txids\":[\"a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1\",\"5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e\"]}" * 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 { try {
const txids = req.body.txids const txids = req.body.txids
if (!Array.isArray(txids)) { if (!Array.isArray(txids)) {
res.status(400) res.status(400)
return res.json({ 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)) { if (!routeUtils.validateArraySize(req, txids)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ return res.json({
error: "Array too large." error: 'Array too large.'
}) })
} }
wlogger.debug( wlogger.debug(
"Executing blockchain/getMempoolEntry with these txids: ", 'Executing blockchain/getMempoolEntry with these txids: ',
txids txids
) )
@@ -472,7 +473,7 @@ class Blockchain {
if (txid.length !== 64) { if (txid.length !== 64) {
res.status(400) 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 // Loop through each txid and creates an array of requests to call in parallel
const promises = txids.map(async txid => { const promises = txids.map(async txid => {
options.data.id = "getmempoolentry" options.data.id = 'getmempoolentry'
options.data.method = "getmempoolentry" options.data.method = 'getmempoolentry'
options.data.params = [txid] options.data.params = [txid]
return await _this.axios.request(options) return _this.axios.request(options)
}) })
const axiosResult = await _this.axios.all(promises) const axiosResult = await _this.axios.all(promises)
@@ -498,7 +499,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) 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" * 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 { try {
// Validate input parameter // Validate input parameter
const txid = req.params.txid const txid = req.params.txid
if (!txid || txid === "") { if (!txid || txid === '') {
res.status(400) 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 let verbose = req.params.verbose
@@ -531,8 +532,8 @@ class Blockchain {
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getmempoolancestors" options.data.id = 'getmempoolancestors'
options.data.method = "getmempoolancestors" options.data.method = 'getmempoolancestors'
options.data.params = [txid, verbose] options.data.params = [txid, verbose]
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -542,7 +543,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
@@ -558,13 +559,13 @@ class Blockchain {
* curl -X GET https://mainnet.bchjs.cash/v3/getMempoolInfo -H "accept: application/json" * curl -X GET https://mainnet.bchjs.cash/v3/getMempoolInfo -H "accept: application/json"
* *
*/ */
async getMempoolInfo(req, res, next) { async getMempoolInfo (req, res, next) {
try { try {
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "getmempoolinfo" options.data.id = 'getmempoolinfo'
options.data.method = "getmempoolinfo" options.data.method = 'getmempoolinfo'
options.data.params = [] options.data.params = []
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -572,7 +573,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
@@ -602,16 +603,16 @@ class Blockchain {
* @apiParam {Boolean} verbose Return verbose data * @apiParam {Boolean} verbose Return verbose data
* *
*/ */
async getRawMempool(req, res, next) { async getRawMempool (req, res, next) {
try { try {
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
let verbose = false 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.id = 'getrawmempool'
options.data.method = "getrawmempool" options.data.method = 'getrawmempool'
options.data.params = [verbose] options.data.params = [verbose]
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -620,7 +621,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
@@ -641,32 +642,33 @@ class Blockchain {
* *
*/ */
// Returns details about an unspent transaction output. // Returns details about an unspent transaction output.
async getTxOut(req, res, next) { async getTxOut (req, res, next) {
try { try {
// Validate input parameter // Validate input parameter
const txid = req.params.txid const txid = req.params.txid
if (!txid || txid === "") { if (!txid || txid === '') {
res.status(400) 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 let n = req.params.n
if (n === undefined || n === "") { if (n === undefined || n === '') {
res.status(400) res.status(400)
return res.json({ error: "n can not be empty" }) return res.json({ error: 'n can not be empty' })
} }
n = parseInt(n) n = parseInt(n)
let include_mempool = false let includeMempool = false
if (req.query.include_mempool && req.query.include_mempool === "true") if (req.query.includeMempool && req.query.includeMempool === 'true') {
include_mempool = true includeMempool = true
}
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "gettxout" options.data.id = 'gettxout'
options.data.method = "gettxout" options.data.method = 'gettxout'
options.data.params = [txid, n, include_mempool] options.data.params = [txid, n, includeMempool]
// console.log(`requestConfig: ${JSON.stringify(requestConfig, null, 2)}`) // console.log(`requestConfig: ${JSON.stringify(requestConfig, null, 2)}`)
@@ -676,7 +678,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
@@ -694,20 +696,20 @@ class Blockchain {
* @apiParam {String} txid Transaction id (required) * @apiParam {String} txid Transaction id (required)
* *
*/ */
async getTxOutProofSingle(req, res, next) { async getTxOutProofSingle (req, res, next) {
try { try {
// Validate input parameter // Validate input parameter
const txid = req.params.txid const txid = req.params.txid
if (!txid || txid === "") { if (!txid || txid === '') {
res.status(400) res.status(400)
return res.json({ error: "txid can not be empty" }) return res.json({ error: 'txid can not be empty' })
} }
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "gettxoutproof" options.data.id = 'gettxoutproof'
options.data.method = "gettxoutproof" options.data.method = 'gettxoutproof'
options.data.params = [[txid]] options.data.params = [[txid]]
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -716,14 +718,14 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
} }
// Returns a hex-encoded proof that 'txid' was included in a block. // Returns a hex-encoded proof that 'txid' was included in a block.
async getTxOutProofBulk(req, res, next) { async getTxOutProofBulk (req, res, next) {
try { try {
const txids = req.body.txids const txids = req.body.txids
@@ -731,7 +733,7 @@ class Blockchain {
if (!Array.isArray(txids)) { if (!Array.isArray(txids)) {
res.status(400) res.status(400)
return res.json({ 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)) { if (!routeUtils.validateArraySize(req, txids)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ return res.json({
error: "Array too large." error: 'Array too large.'
}) })
} }
@@ -759,17 +761,17 @@ class Blockchain {
} }
wlogger.debug( wlogger.debug(
"Executing blockchain/getTxOutProof with these txids: ", 'Executing blockchain/getTxOutProof with these txids: ',
txids txids
) )
// Loop through each txid and creates an array of requests to call in parallel // Loop through each txid and creates an array of requests to call in parallel
const promises = txids.map(async txid => { const promises = txids.map(async txid => {
options.data.id = "gettxoutproof" options.data.id = 'gettxoutproof'
options.data.method = "gettxoutproof" options.data.method = 'gettxoutproof'
options.data.params = [[txid]] options.data.params = [[txid]]
return await _this.axios.request(options) return _this.axios.request(options)
}) })
// Wait for all parallel promisses to resolve. // Wait for all parallel promisses to resolve.
@@ -783,26 +785,26 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
} }
async verifyTxOutProofSingle(req, res, next) { async verifyTxOutProofSingle (req, res, next) {
try { try {
// Validate input parameter // Validate input parameter
const proof = req.params.proof const proof = req.params.proof
if (!proof || proof === "") { if (!proof || proof === '') {
res.status(400) res.status(400)
return res.json({ error: "proof can not be empty" }) return res.json({ error: 'proof can not be empty' })
} }
// Axios options // Axios options
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
options.data.id = "verifytxoutproof" options.data.id = 'verifytxoutproof'
options.data.method = "verifytxoutproof" options.data.method = 'verifytxoutproof'
options.data.params = [req.params.proof] options.data.params = [req.params.proof]
const response = await _this.axios.request(options) const response = await _this.axios.request(options)
@@ -811,13 +813,13 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
} }
async verifyTxOutProofBulk(req, res, next) { async verifyTxOutProofBulk (req, res, next) {
try { try {
const proofs = req.body.proofs const proofs = req.body.proofs
@@ -825,7 +827,7 @@ class Blockchain {
if (!Array.isArray(proofs)) { if (!Array.isArray(proofs)) {
res.status(400) res.status(400)
return res.json({ 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)) { if (!routeUtils.validateArraySize(req, proofs)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ 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++) { for (let i = 0; i < proofs.length; i++) {
const proof = proofs[i] const proof = proofs[i]
if (!proof || proof === "") { if (!proof || proof === '') {
res.status(400) res.status(400)
return res.json({ error: `proof can not be empty: ${proof}` }) return res.json({ error: `proof can not be empty: ${proof}` })
} }
} }
wlogger.debug( wlogger.debug(
"Executing blockchain/verifyTxOutProof with these proofs: ", 'Executing blockchain/verifyTxOutProof with these proofs: ',
proofs proofs
) )
// Loop through each proof and creates an array of requests to call in parallel // Loop through each proof and creates an array of requests to call in parallel
const promises = proofs.map(async proof => { const promises = proofs.map(async proof => {
options.data.id = "verifytxoutproof" options.data.id = 'verifytxoutproof'
options.data.method = "verifytxoutproof" options.data.method = 'verifytxoutproof'
options.data.params = [proof] options.data.params = [proof]
return await _this.axios.request(options) return _this.axios.request(options)
}) })
// Wait for all parallel promisses to resolve. // Wait for all parallel promisses to resolve.
@@ -875,7 +877,7 @@ class Blockchain {
} catch (err) { } catch (err) {
// Write out error to error log. // Write out error to error log.
// logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // 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) return _this.errorHandler(err, res)
} }
+18 -19
View File
@@ -1,21 +1,21 @@
"use strict" 'use strict'
const express = require("express") const express = require('express')
const router = express.Router() const router = express.Router()
const axios = require("axios") // const axios = require('axios')
const routeUtils = require("../route-utils") const routeUtils = require('../route-utils')
const wlogger = require("../../../util/winston-logging") const wlogger = require('../../../util/winston-logging')
// Used for processing error messages before sending them to the user. // Used for processing error messages before sending them to the user.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
router.get("/", root) router.get('/', root)
router.get("/getnetworkinfo", getNetworkInfo) router.get('/getnetworkinfo', getNetworkInfo)
function root(req, res, next) { function root (req, res, next) {
return res.json({ status: "control" }) 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" * 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 { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
requestConfig.data.id = "getnetworkinfo" requestConfig.data.id = 'getnetworkinfo'
requestConfig.data.method = "getnetworkinfo" requestConfig.data.method = 'getnetworkinfo'
requestConfig.data.params = [] requestConfig.data.params = []
try { try {
@@ -45,14 +45,13 @@ async function getNetworkInfo(req, res, next) {
return res.json(response.data.result) return res.json(response.data.result)
} catch (error) { } catch (error) {
wlogger.error(`Error in control.ts/getNetworkInfo().`, error) wlogger.error('Error in control.ts/getNetworkInfo().', error)
// Write out error to error log. // Write out error to error log.
//logger.error(`Error in control/getInfo: `, error) // logger.error(`Error in control/getInfo: `, error)
res.status(500) res.status(500)
if (error.response && error.response.data && 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: error.response.data.error })
return res.json({ error: util.inspect(error) }) return res.json({ error: util.inspect(error) })
} }
} }
+38 -38
View File
@@ -1,39 +1,39 @@
"use strict" 'use strict'
const express = require("express") const express = require('express')
const router = express.Router() const router = express.Router()
const axios = require("axios") // const axios = require('axios')
const routeUtils = require("../route-utils") const routeUtils = require('../route-utils')
const wlogger = require("../../../util/winston-logging") const wlogger = require('../../../util/winston-logging')
// Used to convert error messages to strings, to safely pass to users. // 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 } util.inspect.defaultOptions = { depth: 1 }
const BitboxHTTP = axios.create({ // const BitboxHTTP = axios.create({
baseURL: process.env.RPC_BASEURL // baseURL: process.env.RPC_BASEURL
}) // })
const username = process.env.RPC_USERNAME // const username = process.env.RPC_USERNAME
const password = process.env.RPC_PASSWORD // const password = process.env.RPC_PASSWORD
const requestConfig = { // const requestConfig = {
method: "post", // method: 'post',
auth: { // auth: {
username: username, // username: username,
password: password // password: password
}, // },
data: { // data: {
jsonrpc: "1.0" // jsonrpc: '1.0'
} // }
} // }
router.get("/", root) router.get('/', root)
router.get("/getMiningInfo", getMiningInfo) router.get('/getMiningInfo', getMiningInfo)
router.get("/getNetworkHashps", getNetworkHashPS) router.get('/getNetworkHashps', getNetworkHashPS)
function root(req, res, next) { function root (req, res, next) {
return res.json({ status: "mining" }) 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 { try {
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
requestConfig.data.id = "getmininginfo" requestConfig.data.id = 'getmininginfo'
requestConfig.data.method = "getmininginfo" requestConfig.data.method = 'getmininginfo'
requestConfig.data.params = [] requestConfig.data.params = []
const response = await BitboxHTTP(requestConfig) const response = await BitboxHTTP(requestConfig)
@@ -96,7 +96,7 @@ async function getMiningInfo(req, res, next) {
return res.json({ error: msg }) return res.json({ error: msg })
} }
wlogger.error(`Error in mining.ts/getMiningInfo().`, err) wlogger.error('Error in mining.ts/getMiningInfo().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) 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 { try {
let nblocks = 120 // Default let nblocks = 120 // Default
let height = -1 // Default let height = -1 // Default
@@ -123,13 +123,13 @@ async function getNetworkHashPS(req, res, next) {
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
requestConfig.data.id = "getnetworkhashps" requestConfig.data.id = 'getnetworkhashps'
requestConfig.data.method = "getnetworkhashps" requestConfig.data.method = 'getnetworkhashps'
requestConfig.data.params = [nblocks, height] requestConfig.data.params = [nblocks, height]
const response = await BitboxHTTP(requestConfig) const response = await BitboxHTTP(requestConfig)
@@ -143,7 +143,7 @@ async function getNetworkHashPS(req, res, next) {
return res.json({ error: msg }) return res.json({ error: msg })
} }
wlogger.error(`Error in mining.ts/getNetworkHashPS().`, err) wlogger.error('Error in mining.ts/getNetworkHashPS().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) return res.json({ error: util.inspect(err) })
+4 -4
View File
@@ -1,10 +1,10 @@
"use strict" 'use strict'
const express = require("express") const express = require('express')
const router = express.Router() const router = express.Router()
router.get("/", async (req, res, next) => { router.get('/', async (req, res, next) => {
res.json({ status: "network" }) res.json({ status: 'network' })
}) })
// router.post('/addNode/:node/:command', (req, res, next) => { // router.post('/addNode/:node/:command', (req, res, next) => {
+119 -119
View File
@@ -1,45 +1,45 @@
"use strict" 'use strict'
const express = require("express") const express = require('express')
const router = express.Router() const router = express.Router()
const axios = require("axios") const axios = require('axios')
const routeUtils = require("../route-utils") const routeUtils = require('../route-utils')
const wlogger = require("../../../util/winston-logging") const wlogger = require('../../../util/winston-logging')
// Used to convert error messages to strings, to safely pass to users. // 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 } util.inspect.defaultOptions = { depth: 1 }
const BitboxHTTP = axios.create({ // const BitboxHTTP = axios.create({
baseURL: process.env.RPC_BASEURL // baseURL: process.env.RPC_BASEURL
}) // })
const username = process.env.RPC_USERNAME // const username = process.env.RPC_USERNAME
const password = process.env.RPC_PASSWORD // const password = process.env.RPC_PASSWORD
const requestConfig = { // const requestConfig = {
method: "post", // method: 'post',
auth: { // auth: {
username: username, // username: username,
password: password // password: password
}, // },
data: { // data: {
jsonrpc: "1.0" // jsonrpc: '1.0'
} // }
} // }
router.get("/", root) router.get('/', root)
router.get("/decodeRawTransaction/:hex", decodeRawTransactionSingle) router.get('/decodeRawTransaction/:hex', decodeRawTransactionSingle)
router.post("/decodeRawTransaction", decodeRawTransactionBulk) router.post('/decodeRawTransaction', decodeRawTransactionBulk)
router.get("/decodeScript/:hex", decodeScriptSingle) router.get('/decodeScript/:hex', decodeScriptSingle)
router.post("/decodeScript", decodeScriptBulk) router.post('/decodeScript', decodeScriptBulk)
router.post("/getRawTransaction", getRawTransactionBulk) router.post('/getRawTransaction', getRawTransactionBulk)
router.get("/getRawTransaction/:txid", getRawTransactionSingle) router.get('/getRawTransaction/:txid', getRawTransactionSingle)
router.post("/sendRawTransaction", sendRawTransactionBulk) router.post('/sendRawTransaction', sendRawTransactionBulk)
router.get("/sendRawTransaction/:hex", sendRawTransactionSingle) router.get('/sendRawTransaction/:hex', sendRawTransactionSingle)
function root(req, res, next) { function root (req, res, next) {
return res.json({ status: "rawtransactions" }) return res.json({ status: 'rawtransactions' })
} }
// Decode transaction hex into a JSON object. // 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 { try {
const hex = req.params.hex const hex = req.params.hex
// Throw an error if hex is empty. // Throw an error if hex is empty.
if (!hex || hex === "") { if (!hex || hex === '') {
res.status(400) res.status(400)
return res.json({ error: "hex can not be empty" }) return res.json({ error: 'hex can not be empty' })
} }
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
requestConfig.data.id = "decoderawtransaction" requestConfig.data.id = 'decoderawtransaction'
requestConfig.data.method = "decoderawtransaction" requestConfig.data.method = 'decoderawtransaction'
requestConfig.data.params = [hex] requestConfig.data.params = [hex]
const response = await BitboxHTTP(requestConfig) const response = await BitboxHTTP(requestConfig)
@@ -88,9 +88,9 @@ async function decodeRawTransactionSingle(req, res, next) {
} }
// Write out error to error log. // Write out error to error log.
//logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) // logger.error(`Error in rawtransactions/decodeRawTransaction: `, err)
wlogger.error( wlogger.error(
`Error in rawtransactions.ts/decodeRawTransactionSingle().`, 'Error in rawtransactions.ts/decodeRawTransactionSingle().',
err err
) )
@@ -111,50 +111,50 @@ async function decodeRawTransactionSingle(req, res, next) {
* *
*/ */
async function decodeRawTransactionBulk(req, res, next) { async function decodeRawTransactionBulk (req, res, next) {
try { try {
const hexes = req.body.hexes const hexes = req.body.hexes
if (!Array.isArray(hexes)) { if (!Array.isArray(hexes)) {
res.status(400) 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 // Enforce array size rate limits
if (!routeUtils.validateArraySize(req, hexes)) { if (!routeUtils.validateArraySize(req, hexes)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ return res.json({
error: `Array too large.` error: 'Array too large.'
}) })
} }
const results = [] // const results = []
// Validate each element in the address array. // Validate each element in the address array.
for (let i = 0; i < hexes.length; i++) { for (let i = 0; i < hexes.length; i++) {
const thisHex = hexes[i] const thisHex = hexes[i]
// Reject if id is empty // Reject if id is empty
if (!thisHex || thisHex === "") { if (!thisHex || thisHex === '') {
res.status(400) res.status(400)
return res.json({ error: "Encountered empty hex" }) return res.json({ error: 'Encountered empty hex' })
} }
} }
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
// Loop through each height and creates an array of requests to call in parallel // Loop through each height and creates an array of requests to call in parallel
const promises = hexes.map(async hex => { const promises = hexes.map(async hex => {
requestConfig.data.id = "decoderawtransaction" requestConfig.data.id = 'decoderawtransaction'
requestConfig.data.method = "decoderawtransaction" requestConfig.data.method = 'decoderawtransaction'
requestConfig.data.params = [hex] requestConfig.data.params = [hex]
return await BitboxHTTP(requestConfig) return BitboxHTTP(requestConfig)
}) })
// Wait for all parallel Insight requests to return. // Wait for all parallel Insight requests to return.
@@ -210,9 +210,9 @@ async function decodeRawTransactionBulk(req, res, next) {
} }
// Write out error to error log. // Write out error to error log.
//logger.error(`Error in rawtransactions/getRawTransaction: `, err) // logger.error(`Error in rawtransactions/getRawTransaction: `, err)
wlogger.error( wlogger.error(
`Error in rawtransactions.ts/decodeRawTransactionBulk().`, 'Error in rawtransactions.ts/decodeRawTransactionBulk().',
err err
) )
@@ -235,25 +235,25 @@ async function decodeRawTransactionBulk(req, res, next) {
* *
* *
*/ */
async function decodeScriptSingle(req, res, next) { async function decodeScriptSingle (req, res, next) {
try { try {
const hex = req.params.hex const hex = req.params.hex
// Throw an error if hex is empty. // Throw an error if hex is empty.
if (!hex || hex === "") { if (!hex || hex === '') {
res.status(400) res.status(400)
return res.json({ error: "hex can not be empty" }) return res.json({ error: 'hex can not be empty' })
} }
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
requestConfig.data.id = "decodescript" requestConfig.data.id = 'decodescript'
requestConfig.data.method = "decodescript" requestConfig.data.method = 'decodescript'
requestConfig.data.params = [hex] requestConfig.data.params = [hex]
const response = await BitboxHTTP(requestConfig) const response = await BitboxHTTP(requestConfig)
@@ -267,8 +267,8 @@ async function decodeScriptSingle(req, res, next) {
} }
// Write out error to error log. // Write out error to error log.
//logger.error(`Error in rawtransactions/decodeScript: `, err) // logger.error(`Error in rawtransactions/decodeScript: `, err)
wlogger.error(`Error in rawtransactions.ts/decodeScriptSingle().`, err) wlogger.error('Error in rawtransactions.ts/decodeScriptSingle().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) 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 { try {
const hexes = req.body.hexes const hexes = req.body.hexes
// Validation // Validation
if (!Array.isArray(hexes)) { if (!Array.isArray(hexes)) {
res.status(400) 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 // Enforce array size rate limits
if (!routeUtils.validateArraySize(req, hexes)) { if (!routeUtils.validateArraySize(req, hexes)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ 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] const hex = hexes[i]
// Throw an error if hex is empty. // Throw an error if hex is empty.
if (!hex || hex === "") { if (!hex || hex === '') {
res.status(400) res.status(400)
return res.json({ error: "Encountered empty hex" }) return res.json({ error: 'Encountered empty hex' })
} }
} }
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
// Loop through each hex and create an array of promises // Loop through each hex and create an array of promises
const promises = hexes.map(async hex => { const promises = hexes.map(async hex => {
requestConfig.data.id = "decodescript" requestConfig.data.id = 'decodescript'
requestConfig.data.method = "decodescript" requestConfig.data.method = 'decodescript'
requestConfig.data.params = [hex] requestConfig.data.params = [hex]
const response = await BitboxHTTP(requestConfig) const response = await BitboxHTTP(requestConfig)
@@ -352,8 +352,8 @@ async function decodeScriptBulk(req, res, next) {
} }
// Write out error to error log. // Write out error to error log.
//logger.error(`Error in rawtransactions/decodeScript: `, err) // logger.error(`Error in rawtransactions/decodeScript: `, err)
wlogger.error(`Error in rawtransactions.ts/decodeScriptBulk().`, err) wlogger.error('Error in rawtransactions.ts/decodeScriptBulk().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) 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. // Retrieve raw transactions details from the full node.
async function getRawTransactionsFromNode(txid, verbose) { async function getRawTransactionsFromNode (txid, verbose) {
try { try {
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
requestConfig.data.id = "getrawtransaction" requestConfig.data.id = 'getrawtransaction'
requestConfig.data.method = "getrawtransaction" requestConfig.data.method = 'getrawtransaction'
requestConfig.data.params = [txid, verbose] requestConfig.data.params = [txid, verbose]
const response = await BitboxHTTP(requestConfig) const response = await BitboxHTTP(requestConfig)
return response.data.result return response.data.result
} catch (err) { } catch (err) {
wlogger.error(`Error in rawtransactions.ts/getRawTransactionsFromNode().`) wlogger.error('Error in rawtransactions.ts/getRawTransactionsFromNode().')
throw err 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}' * 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 { try {
let verbose = 0 let verbose = 0
if (req.body.verbose) verbose = 1 if (req.body.verbose) verbose = 1
@@ -405,32 +405,32 @@ async function getRawTransactionBulk(req, res, next) {
const txids = req.body.txids const txids = req.body.txids
if (!Array.isArray(txids)) { if (!Array.isArray(txids)) {
res.status(400) 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 // Enforce array size rate limits
if (!routeUtils.validateArraySize(req, txids)) { if (!routeUtils.validateArraySize(req, txids)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ return res.json({
error: `Array too large.` error: 'Array too large.'
}) })
} }
// stub response object // stub response object
const returnResponse = { // const returnResponse = {
status: 100, // status: 100,
json: { // json: {
error: "" // error: ''
} // }
} // }
// Validate each txid in the array. // Validate each txid in the array.
for (let i = 0; i < txids.length; i++) { for (let i = 0; i < txids.length; i++) {
const txid = txids[i] const txid = txids[i]
if (!txid || txid === "") { if (!txid || txid === '') {
res.status(400) res.status(400)
return res.json({ error: `Encountered empty TXID` }) return res.json({ error: 'Encountered empty TXID' })
} }
if (txid.length !== 64) { if (txid.length !== 64) {
@@ -460,8 +460,8 @@ async function getRawTransactionBulk(req, res, next) {
} }
// Write out error to error log. // 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/getRawTransactionBulk().`, err) wlogger.error('Error in rawtransactions.ts/getRawTransactionBulk().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) 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 { try {
let verbose = 0 let verbose = 0
if (req.query.verbose === "true") verbose = 1 if (req.query.verbose === 'true') verbose = 1
const txid = req.params.txid const txid = req.params.txid
if (!txid || txid === "") { if (!txid || txid === '') {
res.status(400) 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) const data = await getRawTransactionsFromNode(txid, verbose)
@@ -505,8 +505,8 @@ async function getRawTransactionSingle(req, res, next) {
} }
// Write out error to error log. // 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/getRawTransactionSingle().`, err) wlogger.error('Error in rawtransactions.ts/getRawTransactionSingle().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) 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 { try {
// Validation // Validation
const hexes = req.body.hexes const hexes = req.body.hexes
@@ -534,13 +534,13 @@ async function sendRawTransactionBulk(req, res, next) {
// Reject if input is not an array // Reject if input is not an array
if (!Array.isArray(hexes)) { if (!Array.isArray(hexes)) {
res.status(400) res.status(400)
return res.json({ error: "hex must be an array" }) return res.json({ error: 'hex must be an array' })
} }
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
@@ -548,7 +548,7 @@ async function sendRawTransactionBulk(req, res, next) {
if (!routeUtils.validateArraySize(req, hexes)) { if (!routeUtils.validateArraySize(req, hexes)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ 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++) { for (let i = 0; i < hexes.length; i++) {
const hex = hexes[i] const hex = hexes[i]
if (hex === "") { if (hex === '') {
res.status(400) res.status(400)
return res.json({ 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++) { for (let i = 0; i < hexes.length; i++) {
const hex = hexes[i] const hex = hexes[i]
requestConfig.data.id = "sendrawtransaction" requestConfig.data.id = 'sendrawtransaction'
requestConfig.data.method = "sendrawtransaction" requestConfig.data.method = 'sendrawtransaction'
requestConfig.data.params = [hex] requestConfig.data.params = [hex]
const rpcResult = await BitboxHTTP(requestConfig) const rpcResult = await BitboxHTTP(requestConfig)
@@ -613,7 +613,7 @@ async function sendRawTransactionBulk(req, res, next) {
return res.json({ error: msg }) return res.json({ error: msg })
} }
wlogger.error(`Error in rawtransactions.ts/sendRawTransactionBulk().`, err) wlogger.error('Error in rawtransactions.ts/sendRawTransactionBulk().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) 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 { try {
const hex = req.params.hex // URL parameter const hex = req.params.hex // URL parameter
// Reject if input is not an array or a string // Reject if input is not an array or a string
if (typeof hex !== "string") { if (typeof hex !== 'string') {
res.status(400) res.status(400)
return res.json({ error: "hex must be a string" }) return res.json({ error: 'hex must be a string' })
} }
// Validation // Validation
if (hex === "") { if (hex === '') {
res.status(400) res.status(400)
return res.json({ return res.json({
error: `Encountered empty hex` error: 'Encountered empty hex'
}) })
} }
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
// RPC call // RPC call
requestConfig.data.id = "sendrawtransaction" requestConfig.data.id = 'sendrawtransaction'
requestConfig.data.method = "sendrawtransaction" requestConfig.data.method = 'sendrawtransaction'
requestConfig.data.params = [hex] requestConfig.data.params = [hex]
const rpcResult = await BitboxHTTP(requestConfig) const rpcResult = await BitboxHTTP(requestConfig)
@@ -678,7 +678,7 @@ async function sendRawTransactionSingle(req, res, next) {
} }
wlogger.error( wlogger.error(
`Error in rawtransactions.ts/sendRawTransactionSingle().`, 'Error in rawtransactions.ts/sendRawTransactionSingle().',
err err
) )
+3 -3
View File
@@ -3,13 +3,13 @@
readiness. readiness.
*/ */
"use strict" 'use strict'
const express = require("express") const express = require('express')
const router = express.Router() const router = express.Router()
/* GET home page. */ /* GET home page. */
router.get("/", (req, res, next) => { router.get('/', (req, res, next) => {
res.json({ status: true }) res.json({ status: true })
}) })
+13 -13
View File
@@ -2,30 +2,30 @@
A library for interacting with the Bitcoin.com ninsight (not Insight) indexer. A library for interacting with the Bitcoin.com ninsight (not Insight) indexer.
*/ */
"use strict" 'use strict'
const express = require("express") const express = require('express')
const axios = require("axios") // const axios = require('axios')
const routeUtils = require("./route-utils") // const routeUtils = require('./route-utils')
const wlogger = require("../../util/winston-logging") // const wlogger = require('../../util/winston-logging')
const router = express.Router() const router = express.Router()
const BCHJS = require("@chris.troutner/bch-js") // const BCHJS = require('@chris.troutner/bch-js')
const bchjs = new BCHJS() // const bchjs = new BCHJS()
let _this // let _this
class Ninsight { class Ninsight {
constructor() { constructor () {
_this = this // _this = this
this.router = router this.router = router
this.router.get("/", this.root) this.router.get('/', this.root)
} }
root(req, res, next) { root (req, res, next) {
return res.json({ status: "ninsight" }) return res.json({ status: 'ninsight' })
} }
} }
+32 -30
View File
@@ -2,15 +2,15 @@
A private library of utility functions used by several different routes. A private library of utility functions used by several different routes.
*/ */
"use strict" 'use strict'
const axios = require("axios") const axios = require('axios')
const wlogger = require("../../util/winston-logging") const wlogger = require('../../util/winston-logging')
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } 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 = new BCHJS()
module.exports = { module.exports = {
@@ -25,7 +25,7 @@ module.exports = {
// The array is then validated against freemium and pro-tier rate limiting // 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 // requirements. A boolean is returned to indicate if the array size if valid
// or not. // or not.
function validateArraySize(req, array) { function validateArraySize (req, array) {
const FREEMIUM_INPUT_SIZE = 20 const FREEMIUM_INPUT_SIZE = 20
const PRO_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 // 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 // testnet address into rest.bitcoin.com or passing a mainnet address into
// trest.bitcoin.com. // trest.bitcoin.com.
function validateNetwork(addr) { function validateNetwork (addr) {
try { try {
const network = process.env.NETWORK const network = process.env.NETWORK
// Return false if NETWORK is not defined. // Return false if NETWORK is not defined.
if (!network || network === "") { if (!network || network === '') {
console.log(`Warning: NETWORK environment variable is not defined!`) console.log('Warning: NETWORK environment variable is not defined!')
return false return false
} }
@@ -59,21 +59,21 @@ function validateNetwork(addr) {
// Return true if the network and address both match testnet // Return true if the network and address both match testnet
const addrIsTest = bchjs.Address.isTestnetAddress(cashAddr) 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 // Return true if the network and address both match mainnet
const addrIsMain = bchjs.Address.isMainnetAddress(cashAddr) const addrIsMain = bchjs.Address.isMainnetAddress(cashAddr)
if (network === "mainnet" && addrIsMain) return true if (network === 'mainnet' && addrIsMain) return true
return false return false
} catch (err) { } catch (err) {
logger.error(`Error in validateNetwork()`) wlogger.error('Error in validateNetwork()')
return false return false
} }
} }
// Dynamically set these based on env vars. Allows unit testing. // Dynamically set these based on env vars. Allows unit testing.
function setEnvVars() { function setEnvVars () {
const BitboxHTTP = axios.create({ const BitboxHTTP = axios.create({
baseURL: process.env.RPC_BASEURL, baseURL: process.env.RPC_BASEURL,
timeout: 15000 timeout: 15000
@@ -82,13 +82,13 @@ function setEnvVars() {
const password = process.env.RPC_PASSWORD const password = process.env.RPC_PASSWORD
const requestConfig = { const requestConfig = {
method: "post", method: 'post',
auth: { auth: {
username: username, username: username,
password: password password: password
}, },
data: { 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. // Axios options used when calling axios.post() to talk with a full node.
function getAxiosOptions() { function getAxiosOptions () {
return { return {
method: "post", method: 'post',
baseURL: process.env.RPC_BASEURL, baseURL: process.env.RPC_BASEURL,
timeout: 15000, timeout: 15000,
auth: { auth: {
@@ -106,7 +106,7 @@ function getAxiosOptions() {
password: process.env.RPC_PASSWORD password: process.env.RPC_PASSWORD
}, },
data: { data: {
jsonrpc: "1.0" jsonrpc: '1.0'
} }
} }
} }
@@ -116,7 +116,7 @@ function getAxiosOptions() {
// error messages. // error messages.
// Returns an object. If successful, obj.msg is a string. // Returns an object. If successful, obj.msg is a string.
// If there is a failure, obj.msg is false. // If there is a failure, obj.msg is false.
function decodeError(err) { function decodeError (err) {
try { try {
// Attempt to extract the full node error message. // Attempt to extract the full node error message.
if ( if (
@@ -124,39 +124,41 @@ function decodeError(err) {
err.response.data && err.response.data &&
err.response.data.error && err.response.data.error &&
err.response.data.error.message err.response.data.error.message
) ) {
return { msg: err.response.data.error.message, status: 400 } return { msg: err.response.data.error.message, status: 400 }
}
// Attempt to extract the Insight error message // 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 } return { msg: err.response.data, status: err.response.status }
}
// console.log(`err.message: ${err.message}`) // console.log(`err.message: ${err.message}`)
// console.log(`err: `, err) // console.log(`err: `, err)
// Attempt to detect a network connection error. // Attempt to detect a network connection error.
if (err.message && err.message.indexOf("ENOTFOUND") > -1) { if (err.message && err.message.indexOf('ENOTFOUND') > -1) {
return { return {
msg: 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 status: 503
} }
} }
// Different kind of network error // Different kind of network error
if (err.message && err.message.indexOf("ENETUNREACH") > -1) { if (err.message && err.message.indexOf('ENETUNREACH') > -1) {
return { return {
msg: 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 status: 503
} }
} }
// Different kind of network error // Different kind of network error
if (err.message && err.message.indexOf("EAI_AGAIN") > -1) { if (err.message && err.message.indexOf('EAI_AGAIN') > -1) {
return { return {
msg: 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 status: 503
} }
} }
@@ -164,18 +166,18 @@ function decodeError(err) {
// Axios timeout (aborted) error, or service is down (connection refused). // Axios timeout (aborted) error, or service is down (connection refused).
if ( if (
err.code && err.code &&
(err.code === "ECONNABORTED" || err.code === "ECONNREFUSED") (err.code === 'ECONNABORTED' || err.code === 'ECONNREFUSED')
) { ) {
return { return {
msg: 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 status: 503
} }
} }
return { msg: false, status: 500 } return { msg: false, status: 500 }
} catch (err) { } 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 } return { msg: false, status: 500 }
} }
} }
+32 -30
View File
@@ -2,22 +2,22 @@
A private library of utility functions used by several different routes. A private library of utility functions used by several different routes.
*/ */
"use strict" 'use strict'
const axios = require("axios") const axios = require('axios')
const wlogger = require("../../util/winston-logging") const wlogger = require('../../util/winston-logging')
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } 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 = new BCHJS()
let _this // let _this
class RouteUtils { class RouteUtils {
constructor() { constructor () {
_this = this // _this = this
this.bchjs = bchjs this.bchjs = bchjs
this.axios = axios this.axios = axios
@@ -27,7 +27,7 @@ class RouteUtils {
// The array is then validated against freemium and pro-tier rate limiting // 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 // requirements. A boolean is returned to indicate if the array size if valid
// or not. // or not.
validateArraySize(req, array) { validateArraySize (req, array) {
const FREEMIUM_INPUT_SIZE = 20 const FREEMIUM_INPUT_SIZE = 20
const PRO_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. // Axios options used when calling axios.post() to talk with a full node.
getAxiosOptions() { getAxiosOptions () {
return { return {
method: "post", method: 'post',
baseURL: process.env.RPC_BASEURL, baseURL: process.env.RPC_BASEURL,
timeout: 15000, timeout: 15000,
auth: { auth: {
@@ -51,7 +51,7 @@ class RouteUtils {
password: process.env.RPC_PASSWORD password: process.env.RPC_PASSWORD
}, },
data: { 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 // 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 // testnet address into rest.bitcoin.com or passing a mainnet address into
// trest.bitcoin.com. // trest.bitcoin.com.
validateNetwork(addr) { validateNetwork (addr) {
try { try {
const network = process.env.NETWORK const network = process.env.NETWORK
// Return false if NETWORK is not defined. // Return false if NETWORK is not defined.
if (!network || network === "") { if (!network || network === '') {
console.log(`Warning: NETWORK environment variable is not defined!`) console.log('Warning: NETWORK environment variable is not defined!')
return false return false
} }
@@ -77,15 +77,15 @@ class RouteUtils {
// Return true if the network and address both match testnet // Return true if the network and address both match testnet
const addrIsTest = this.bchjs.Address.isTestnetAddress(cashAddr) 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 // Return true if the network and address both match mainnet
const addrIsMain = this.bchjs.Address.isMainnetAddress(cashAddr) const addrIsMain = this.bchjs.Address.isMainnetAddress(cashAddr)
if (network === "mainnet" && addrIsMain) return true if (network === 'mainnet' && addrIsMain) return true
return false return false
} catch (err) { } catch (err) {
logger.error(`Error in validateNetwork()`) wlogger.error('Error in validateNetwork()')
return false return false
} }
} }
@@ -95,7 +95,7 @@ class RouteUtils {
// error messages. // error messages.
// Returns an object. If successful, obj.msg is a string. // Returns an object. If successful, obj.msg is a string.
// If there is a failure, obj.msg is false. // If there is a failure, obj.msg is false.
decodeError(err) { decodeError (err) {
try { try {
// Attempt to extract the full node error message. // Attempt to extract the full node error message.
if ( if (
@@ -103,39 +103,41 @@ class RouteUtils {
err.response.data && err.response.data &&
err.response.data.error && err.response.data.error &&
err.response.data.error.message err.response.data.error.message
) ) {
return { msg: err.response.data.error.message, status: 400 } return { msg: err.response.data.error.message, status: 400 }
}
// Attempt to extract the Insight error message // 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 } return { msg: err.response.data, status: err.response.status }
}
// console.log(`err.message: ${err.message}`) // console.log(`err.message: ${err.message}`)
// console.log(`err: `, err) // console.log(`err: `, err)
// Attempt to detect a network connection error. // Attempt to detect a network connection error.
if (err.message && err.message.indexOf("ENOTFOUND") > -1) { if (err.message && err.message.indexOf('ENOTFOUND') > -1) {
return { return {
msg: 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 status: 503
} }
} }
// Different kind of network error // Different kind of network error
if (err.message && err.message.indexOf("ENETUNREACH") > -1) { if (err.message && err.message.indexOf('ENETUNREACH') > -1) {
return { return {
msg: 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 status: 503
} }
} }
// Different kind of network error // Different kind of network error
if (err.message && err.message.indexOf("EAI_AGAIN") > -1) { if (err.message && err.message.indexOf('EAI_AGAIN') > -1) {
return { return {
msg: 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 status: 503
} }
} }
@@ -143,18 +145,18 @@ class RouteUtils {
// Axios timeout (aborted) error, or service is down (connection refused). // Axios timeout (aborted) error, or service is down (connection refused).
if ( if (
err.code && err.code &&
(err.code === "ECONNABORTED" || err.code === "ECONNREFUSED") (err.code === 'ECONNABORTED' || err.code === 'ECONNREFUSED')
) { ) {
return { return {
msg: 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 status: 503
} }
} }
return { msg: false, status: 500 } return { msg: false, status: 500 }
} catch (err) { } 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 } return { msg: false, status: 500 }
} }
} }
+292 -297
View File
File diff suppressed because it is too large Load Diff
+94 -87
View File
@@ -1,50 +1,50 @@
"use strict" 'use strict'
const express = require("express") const express = require('express')
const router = express.Router() const router = express.Router()
const axios = require("axios") const axios = require('axios')
const routeUtils = require("./route-utils") const routeUtils = require('./route-utils')
const wlogger = require("../../util/winston-logging") const wlogger = require('../../util/winston-logging')
const blockbook = require("./blockbook") const blockbook = require('./blockbook')
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } 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 = new BCHJS()
const BCHJS_TESTNET = `https://testnet.bchjs.cash/v3/` // const BCHJS_TESTNET = 'https://testnet.bchjs.cash/v3/'
const bchjsHTTP = axios.create({ // const bchjsHTTP = axios.create({
baseURL: process.env.RPC_BASEURL // baseURL: process.env.RPC_BASEURL
}) // })
const username = process.env.RPC_USERNAME // const username = process.env.RPC_USERNAME
const password = process.env.RPC_PASSWORD // const password = process.env.RPC_PASSWORD
const requestConfig = { // const requestConfig = {
method: "post", // method: 'post',
auth: { // auth: {
username: username, // username: username,
password: password // password: password
}, // },
data: { // data: {
jsonrpc: "1.0" // jsonrpc: '1.0'
} // }
} // }
let _this let _this
class UtilRoute { class UtilRoute {
constructor() { constructor () {
this.bchjs = bchjs this.bchjs = bchjs
this.blockbook = blockbook this.blockbook = blockbook
_this = this _this = this
} }
root(req, res, next) { root (req, res, next) {
return res.json({ status: "util" }) return res.json({ status: 'util' })
} }
/** /**
@@ -59,23 +59,23 @@ class UtilRoute {
* *
* *
*/ */
async validateAddressSingle(req, res, next) { async validateAddressSingle (req, res, next) {
try { try {
const address = req.params.address const address = req.params.address
if (!address || address === "") { if (!address || address === '') {
res.status(400) res.status(400)
return res.json({ error: "address can not be empty" }) return res.json({ error: 'address can not be empty' })
} }
const { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
requestConfig.data.id = "validateaddress" requestConfig.data.id = 'validateaddress'
requestConfig.data.method = "validateaddress" requestConfig.data.method = 'validateaddress'
requestConfig.data.params = [address] requestConfig.data.params = [address]
const response = await BitboxHTTP(requestConfig) const response = await BitboxHTTP(requestConfig)
@@ -89,7 +89,7 @@ class UtilRoute {
return res.json({ error: msg }) return res.json({ error: msg })
} }
wlogger.error(`Error in util.ts/validateAddressSingle().`, err) wlogger.error('Error in util.ts/validateAddressSingle().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) return res.json({ error: util.inspect(err) })
@@ -109,7 +109,7 @@ class UtilRoute {
* *
* *
*/ */
async validateAddressBulk(req, res, next) { async validateAddressBulk (req, res, next) {
try { try {
const addresses = req.body.addresses const addresses = req.body.addresses
@@ -117,7 +117,7 @@ class UtilRoute {
if (!Array.isArray(addresses)) { if (!Array.isArray(addresses)) {
res.status(400) res.status(400)
return res.json({ 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)) { if (!routeUtils.validateArraySize(req, addresses)) {
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({ 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. // Ensure the input is a valid BCH address.
try { try {
var legacyAddr = bchjs.Address.toLegacyAddress(address) bchjs.Address.toLegacyAddress(address)
} catch (err) { } catch (err) {
res.status(400) res.status(400)
return res.json({ return res.json({
@@ -148,27 +148,28 @@ class UtilRoute {
if (!networkIsValid) { if (!networkIsValid) {
res.status(400) res.status(400)
return res.json({ 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 { const {
BitboxHTTP, BitboxHTTP,
username, // username,
password, // password,
requestConfig requestConfig
} = routeUtils.setEnvVars() } = routeUtils.setEnvVars()
// Loop through each address and creates an array of requests to call in parallel // Loop through each address and creates an array of requests to call in parallel
const promises = addresses.map(async address => { const promises = addresses.map(async address => {
requestConfig.data.id = "validateaddress" requestConfig.data.id = 'validateaddress'
requestConfig.data.method = "validateaddress" requestConfig.data.method = 'validateaddress'
requestConfig.data.params = [address] requestConfig.data.params = [address]
return await BitboxHTTP(requestConfig) return BitboxHTTP(requestConfig)
}) })
// Wait for all parallel Insight requests to return. // Wait for all parallel Insight requests to return.
@@ -187,7 +188,7 @@ class UtilRoute {
return res.json({ error: msg }) return res.json({ error: msg })
} }
wlogger.error(`Error in util.ts/validateAddressSingle().`, err) wlogger.error('Error in util.ts/validateAddressSingle().', err)
res.status(500) res.status(500)
return res.json({ error: util.inspect(err) }) return res.json({ error: util.inspect(err) })
@@ -212,29 +213,29 @@ class UtilRoute {
* *
*/ */
async sweepWif(req, res, next) { async sweepWif (req, res, next) {
try { try {
// Validate input // Validate input
const wif = req.body.wif const wif = req.body.wif
const toAddr = req.body.toAddr const toAddr = req.body.toAddr
const balanceOnly = req.body.balanceOnly const balanceOnly = req.body.balanceOnly
if (typeof wif !== "string" || wif.length !== 52) { if (typeof wif !== 'string' || wif.length !== 52) {
res.status(400) res.status(400)
return res.json({ 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) { if (!balanceOnly) {
// Only throw error if balanceOnly is false or undefined. // Only throw error if balanceOnly is false or undefined.
if (!toAddr || toAddr === "") { if (!toAddr || toAddr === '') {
res.status(400) 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. // Generate a private and public key pair from the WIF.
const ecPair = bchjs.ECPair.fromWIF(wif) const ecPair = bchjs.ECPair.fromWIF(wif)
@@ -253,7 +254,7 @@ class UtilRoute {
// Exit if balance is zero. // Exit if balance is zero.
if (isNaN(totalBalance) || totalBalance === 0) { if (isNaN(totalBalance) || totalBalance === 0) {
res.status(422) 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. // Exit if this is a balance-only call.
@@ -274,7 +275,7 @@ class UtilRoute {
// Exit if there are no UTXOs. // Exit if there are no UTXOs.
if (utxos.length === 0) { if (utxos.length === 0) {
res.status(422) 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. // Figure out which UTXOs are associated with SLP tokens.
@@ -295,7 +296,8 @@ class UtilRoute {
if (bchUtxos.length === 0 && tokenUtxos.length > 0) { if (bchUtxos.length === 0 && tokenUtxos.length > 0) {
res.status(422) res.status(422)
return res.json({ 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. // Catch the specific case of multiple tokens.
if ( if (
err.message && err.message &&
err.message.indexOf("Multiple token classes detected") > -1 err.message.indexOf('Multiple token classes detected') > -1
) { ) {
res.status(422) res.status(422)
return res.json({ error: err.message }) return res.json({ error: err.message })
} }
wlogger.error(`Error in util.js/sweepWif().`, err) wlogger.error('Error in util.js/sweepWif().', err)
console.error(`Error in util.js/sweepWif().`, err) console.error('Error in util.js/sweepWif().', err)
res.status(500) res.status(500)
return res.json({ error: err.message }) return res.json({ error: err.message })
@@ -352,7 +354,7 @@ class UtilRoute {
} }
// Sweep BCH only from a private WIF. // Sweep BCH only from a private WIF.
async _sweepBCH(options) { async _sweepBCH (options) {
try { try {
// const wif = flags.wif // const wif = flags.wif
// const toAddr = flags.address // const toAddr = flags.address
@@ -377,9 +379,9 @@ class UtilRoute {
// instance of transaction builder // instance of transaction builder
let transactionBuilder let transactionBuilder
if (options.testnet) if (options.testnet) {
transactionBuilder = new _this.bchjs.TransactionBuilder("testnet") transactionBuilder = new _this.bchjs.TransactionBuilder('testnet')
else transactionBuilder = new _this.bchjs.TransactionBuilder() } else transactionBuilder = new _this.bchjs.TransactionBuilder()
let originalAmount = 0 let originalAmount = 0
@@ -394,7 +396,7 @@ class UtilRoute {
if (originalAmount < 546) { if (originalAmount < 546) {
throw new Error( 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() const hex = tx.toHex()
return hex return hex
} catch (err) { } catch (err) {
wlogger.error(`Error in util.js/sweepBCH().`) wlogger.error('Error in util.js/sweepBCH().')
throw err throw err
} }
} }
// Sweep BCH and tokens from a WIF. // Sweep BCH and tokens from a WIF.
async _sweepTokens(options) { async _sweepTokens (options) {
try { 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 // Input validation
if (!Array.isArray(bchUtxos) || bchUtxos.length === 0) if (!Array.isArray(bchUtxos) || bchUtxos.length === 0) {
throw new Error(`bchUtxos need to be an array with one UTXO.`) 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(tokenUtxos) || tokenUtxos.length === 0) {
throw new Error('tokenUtxos need to be an array with one UTXO.')
}
// if (flags.testnet) // if (flags.testnet)
// this.BITBOX = new config.BCHLIB({ restURL: config.TESTNET_REST }) // this.BITBOX = new config.BCHLIB({ restURL: config.TESTNET_REST })
@@ -462,15 +467,15 @@ class UtilRoute {
const otherTokens = tokenUtxos.filter(x => x.tokenId !== tokenId) const otherTokens = tokenUtxos.filter(x => x.tokenId !== tokenId)
if (otherTokens.length > 0) { if (otherTokens.length > 0) {
throw new Error( 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 // instance of transaction builder
let transactionBuilder let transactionBuilder
if (options.testnet) if (options.testnet) {
transactionBuilder = new _this.bchjs.TransactionBuilder("testnet") transactionBuilder = new _this.bchjs.TransactionBuilder('testnet')
else transactionBuilder = new _this.bchjs.TransactionBuilder() } else transactionBuilder = new _this.bchjs.TransactionBuilder()
// Combine all the UTXOs into a single array. // Combine all the UTXOs into a single array.
const allUtxos = utxos const allUtxos = utxos
@@ -488,7 +493,7 @@ class UtilRoute {
if (originalAmount < 300) { if (originalAmount < 300) {
throw new Error( 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 // amount to send back to the sending address. It's the original amount - 1 sat/byte for tx size
const remainder = originalAmount - txFee - 546 const remainder = originalAmount - txFee - 546
if (remainder < 1) if (remainder < 1) {
throw new Error(`Selected UTXO does not have enough satoshis`) throw new Error('Selected UTXO does not have enough satoshis')
//console.log(`remainder: ${remainder}`) }
// console.log(`remainder: ${remainder}`)
// Tally up the quantity of tokens // Tally up the quantity of tokens
let tokenQty = 0 let tokenQty = 0
for (let i = 0; i < tokenUtxos.length; i++) for (let i = 0; i < tokenUtxos.length; i++) {
tokenQty += tokenUtxos[i].tokenQty tokenQty += tokenUtxos[i].tokenQty
}
// console.log(`tokenQty: ${tokenQty}`) // console.log(`tokenQty: ${tokenQty}`)
// Generate the OP_RETURN entry for an SLP SEND transaction. // Generate the OP_RETURN entry for an SLP SEND transaction.
//console.log(`Generating op-return.`) // console.log(`Generating op-return.`)
const { const {
script, script,
outputs outputs
@@ -529,7 +536,7 @@ class UtilRoute {
// is something unexpected happening. // is something unexpected happening.
if (outputs > 1) { if (outputs > 1) {
throw new Error( 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 return hex
} catch (err) { } catch (err) {
wlogger.error(`Error in util.js/sweepBCH().`) wlogger.error('Error in util.js/sweepBCH().')
throw err throw err
} }
} }
@@ -583,10 +590,10 @@ class UtilRoute {
const utilRoute = new UtilRoute() const utilRoute = new UtilRoute()
router.get("/", utilRoute.root) router.get('/', utilRoute.root)
router.get("/validateAddress/:address", utilRoute.validateAddressSingle) router.get('/validateAddress/:address', utilRoute.validateAddressSingle)
router.post("/validateAddress", utilRoute.validateAddressBulk) router.post('/validateAddress', utilRoute.validateAddressBulk)
router.post("/sweep", utilRoute.sweepWif) router.post('/sweep', utilRoute.sweepWif)
module.exports = { module.exports = {
router, router,
+19 -19
View File
@@ -2,51 +2,51 @@
xpub route xpub route
*/ */
"use strict" 'use strict'
const express = require("express") const express = require('express')
const axios = require("axios") // const axios = require('axios')
const routeUtils = require("./route-utils") const routeUtils = require('./route-utils')
const wlogger = require("../../util/winston-logging") const wlogger = require('../../util/winston-logging')
//const router = express.Router() // const router = express.Router()
const router = express.Router() const router = express.Router()
// Used for processing error messages before sending them to the user. // Used for processing error messages before sending them to the user.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } 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 = new BCHJS()
// Connect the route endpoints to their handler functions. // Connect the route endpoints to their handler functions.
router.get("/", root) router.get('/', root)
router.get("/fromXPub/:xpub", fromXPubSingle) router.get('/fromXPub/:xpub', fromXPubSingle)
// Root API endpoint. Simply acknowledges that it exists. // Root API endpoint. Simply acknowledges that it exists.
function root(req, res, next) { function root (req, res, next) {
return res.json({ status: "address" }) return res.json({ status: 'address' })
} }
async function fromXPubSingle(req, res, next) { async function fromXPubSingle (req, res, next) {
try { try {
const xpub = req.params.xpub 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) 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. // Reject if xpub is an array.
if (Array.isArray(xpub)) { if (Array.isArray(xpub)) {
res.status(400) res.status(400)
return res.json({ 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 cashAddr = bchjs.Address.fromXPub(xpub, hdPath)
const legacyAddr = bchjs.Address.toLegacyAddress(cashAddr) const legacyAddr = bchjs.Address.toLegacyAddress(cashAddr)
@@ -64,7 +64,7 @@ async function fromXPubSingle(req, res, next) {
} }
// Write out error to error log. // 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) res.status(500)
return res.json({ error: util.inspect(err) }) return res.json({ error: util.inspect(err) })
+11 -11
View File
@@ -8,29 +8,29 @@
Blockbook. Blockbook.
*/ */
"use strict" 'use strict'
class BlockbookPath { class BlockbookPath {
constructor() { constructor () {
// defaults // defaults
this.addrPath = `${process.env.BLOCKBOOK_URL}api/v2/address/` this.addrPath = `${process.env.BLOCKBOOK_URL}api/v2/address/`
this.utxoPath = `${process.env.BLOCKBOOK_URL}api/v2/utxo/` this.utxoPath = `${process.env.BLOCKBOOK_URL}api/v2/utxo/`
this.txPath = `${process.env.BLOCKBOOK_URL}api/v2/tx/` this.txPath = `${process.env.BLOCKBOOK_URL}api/v2/tx/`
} }
toOpenBazaar() { toOpenBazaar () {
if (process.env.NETWORK === "testnet") { if (process.env.NETWORK === 'testnet') {
this.addrPath = `https://tbch.blockbook.api.openbazaar.org/api/address/` this.addrPath = 'https://tbch.blockbook.api.openbazaar.org/api/address/'
this.utxoPath = `https://tbch.blockbook.api.openbazaar.org/api/utxo/` this.utxoPath = 'https://tbch.blockbook.api.openbazaar.org/api/utxo/'
this.txPath = `https://tbch.blockbook.api.openbazaar.org/api/tx/` this.txPath = 'https://tbch.blockbook.api.openbazaar.org/api/tx/'
} else { } else {
this.addrPath = `https://bch.blockbook.api.openbazaar.org/api/address/` this.addrPath = 'https://bch.blockbook.api.openbazaar.org/api/address/'
this.utxoPath = `https://bch.blockbook.api.openbazaar.org/api/utxo/` this.utxoPath = 'https://bch.blockbook.api.openbazaar.org/api/utxo/'
this.txPath = `https://bch.blockbook.api.openbazaar.org/api/tx/` this.txPath = 'https://bch.blockbook.api.openbazaar.org/api/tx/'
} }
} }
toDefault() { toDefault () {
this.addrPath = `${process.env.BLOCKBOOK_URL}api/v2/address/` this.addrPath = `${process.env.BLOCKBOOK_URL}api/v2/address/`
this.utxoPath = `${process.env.BLOCKBOOK_URL}api/v2/utxo/` this.utxoPath = `${process.env.BLOCKBOOK_URL}api/v2/utxo/`
this.txPath = `${process.env.BLOCKBOOK_URL}api/v2/tx/` this.txPath = `${process.env.BLOCKBOOK_URL}api/v2/tx/`
+9 -9
View File
@@ -4,33 +4,33 @@
logging library. logging library.
*/ */
"use strict" 'use strict'
var winston = require("winston") var winston = require('winston')
require("winston-daily-rotate-file") require('winston-daily-rotate-file')
var NETWORK = process.env.NETWORK var NETWORK = process.env.NETWORK
// Configure daily-rotation transport. // Configure daily-rotation transport.
var transport = new winston.transports.DailyRotateFile({ var transport = new winston.transports.DailyRotateFile({
filename: `${__dirname}/../../logs/rest-${NETWORK}-%DATE%.log`, filename: `${__dirname}/../../logs/rest-${NETWORK}-%DATE%.log`,
datePattern: "YYYY-MM-DD", datePattern: 'YYYY-MM-DD',
zippedArchive: false, zippedArchive: false,
maxSize: "1m", maxSize: '1m',
maxFiles: "5d", maxFiles: '5d',
format: winston.format.combine( format: winston.format.combine(
winston.format.timestamp(), winston.format.timestamp(),
winston.format.json() winston.format.json()
) )
}) })
transport.on("rotate", function(oldFilename, newFilename) { transport.on('rotate', function (oldFilename, newFilename) {
wlogger.info("Rotating log files") wlogger.info('Rotating log files')
}) })
// This controls what goes into the log FILES // This controls what goes into the log FILES
var wlogger = winston.createLogger({ var wlogger = winston.createLogger({
level: "verbose", level: 'verbose',
format: winston.format.json(), format: winston.format.json(),
transports: [ transports: [
// //
+308 -308
View File
File diff suppressed because it is too large Load Diff
+536 -536
View File
File diff suppressed because it is too large Load Diff
+42 -42
View File
@@ -7,24 +7,24 @@
*/ */
"use strict" 'use strict'
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const controlRoute = require("../../src/routes/v3/full-node/control") const controlRoute = require('../../src/routes/v3/full-node/control')
const nock = require("nock") // HTTP mocking const nock = require('nock') // HTTP mocking
let originalEnvVars // Used during transition from integration to unit tests. let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data. // Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks") const { mockReq, mockRes } = require('./mocks/express-mocks')
const mockData = require("./mocks/control-mock") const mockData = require('./mocks/control-mock')
// Used for debugging. // Used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
describe("#ControlRouter", () => { describe('#ControlRouter', () => {
let req, res let req, res
before(() => { before(() => {
@@ -37,12 +37,12 @@ describe("#ControlRouter", () => {
} }
// Set default environment variables for unit tests. // Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit" if (!process.env.TEST) process.env.TEST = 'unit'
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" process.env.BITCOINCOM_BASEURL = 'http://fakeurl/api/'
process.env.RPC_BASEURL = "http://fakeurl/api" process.env.RPC_BASEURL = 'http://fakeurl/api'
process.env.RPC_USERNAME = "fakeusername" process.env.RPC_USERNAME = 'fakeusername'
process.env.RPC_PASSWORD = "fakepassword" process.env.RPC_PASSWORD = 'fakepassword'
} }
}) })
@@ -75,30 +75,30 @@ describe("#ControlRouter", () => {
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
}) })
describe("#root", async () => { describe('#root', async () => {
// root route handler. // root route handler.
const root = controlRoute.testableComponents.root const root = controlRoute.testableComponents.root
it("should respond to GET for base route", async () => { it('should respond to GET for base route', async () => {
const result = root(req, res) const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "control", "Returns static string") assert.equal(result.status, 'control', 'Returns static string')
}) })
}) })
describe("#GetNetworkInfo", () => { describe('#GetNetworkInfo', () => {
const getNetworkInfo = controlRoute.testableComponents.getNetworkInfo const getNetworkInfo = controlRoute.testableComponents.getNetworkInfo
it("should throw 500 when network issues", async () => { it('should throw 500 when network issues', async () => {
// Save the existing RPC URL. // Save the existing RPC URL.
const savedUrl = process.env.RPC_BASEURL const savedUrl = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error. // Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/" process.env.RPC_BASEURL = 'http://fakeurl/api/'
const result = await getNetworkInfo(req, res) await getNetworkInfo(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL. // Restore the saved URL.
process.env.RPC_BASEURL = savedUrl process.env.RPC_BASEURL = savedUrl
@@ -106,36 +106,36 @@ describe("#ControlRouter", () => {
assert.isAbove( assert.isAbove(
res.statusCode, res.statusCode,
499, 499,
"HTTP status code 500 or greater expected." 'HTTP status code 500 or greater expected.'
) )
//assert.include(result.error, "ENOTFOUND", "Error message expected") // assert.include(result.error, "ENOTFOUND", "Error message expected")
}) })
it("should get info on the full node", async () => { it('should get info on the full node', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.RPC_BASEURL}`) nock(`${process.env.RPC_BASEURL}`)
.post(uri => uri.includes("/")) .post(uri => uri.includes('/'))
.reply(200, { result: mockData.mockGetNetworkInfo }) .reply(200, { result: mockData.mockGetNetworkInfo })
} }
const result = await getNetworkInfo(req, res) const result = await getNetworkInfo(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [ assert.hasAnyKeys(result, [
"version", 'version',
"subversion", 'subversion',
"protocolversion", 'protocolversion',
"localservices", 'localservices',
"localrelay", 'localrelay',
"timeoffset", 'timeoffset',
"networkactive", 'networkactive',
"connections", 'connections',
"networks", 'networks',
"relayfee", 'relayfee',
"excessutxocharge", 'excessutxocharge',
"localaddresses", 'localaddresses',
"warnings" 'warnings'
]) ])
}) })
}) })
+12 -12
View File
@@ -3,11 +3,11 @@
These tests assume that the repo is running locally and pointed at TESTNET These tests assume that the repo is running locally and pointed at TESTNET
*/ */
"use strict" 'use strict'
const rp = require("request-promise") const rp = require('request-promise')
const rawtransactions = require("../../../dist/routes/v2/address") // const rawtransactions = require('../../../dist/routes/v2/address')
/* /*
This unconfirmed utxo test needs to be expanded in the future to automatically This unconfirmed utxo test needs to be expanded in the future to automatically
@@ -19,19 +19,19 @@ const rawtransactions = require("../../../dist/routes/v2/address")
3. Run this program with `node address.js` 3. Run this program with `node address.js`
4. If successful, it will return the unconfirmed UTXOs. 4. If successful, it will return the unconfirmed UTXOs.
*/ */
const addr1 = "bchtest:qpdcp5pv7qphu5tsjfgwezld9n9aq9ue6swgqpf45c" const addr1 = 'bchtest:qpdcp5pv7qphu5tsjfgwezld9n9aq9ue6swgqpf45c'
const addr2 = "bchtest:qzpvx999fagau0xqmvu3xvll9evapge7u5hcgeknzv" const addr2 = 'bchtest:qzpvx999fagau0xqmvu3xvll9evapge7u5hcgeknzv'
async function testSingleUnconfirmed() { async function testSingleUnconfirmed () {
try { try {
const options = { const options = {
method: "GET", method: 'GET',
uri: `http://localhost:3000/v2/address/unconfirmed/${addr1}`, uri: `http://localhost:3000/v2/address/unconfirmed/${addr1}`,
resolveWithFullResponse: true, resolveWithFullResponse: true,
json: true json: true
} }
const result = await rp(options) await rp(options)
// console.log(`result.body: ${JSON.stringify(result.body, null, 2)}`) // console.log(`result.body: ${JSON.stringify(result.body, null, 2)}`)
} catch (err) { } catch (err) {
// console.log(`Error: `, err) // console.log(`Error: `, err)
@@ -39,11 +39,11 @@ async function testSingleUnconfirmed() {
} }
testSingleUnconfirmed() testSingleUnconfirmed()
async function testDoubleUnconfirmed() { async function testDoubleUnconfirmed () {
try { try {
const options = { const options = {
method: "POST", method: 'POST',
uri: `http://localhost:3000/v2/address/unconfirmed`, uri: 'http://localhost:3000/v2/address/unconfirmed',
resolveWithFullResponse: true, resolveWithFullResponse: true,
json: true, json: true,
body: { body: {
@@ -51,7 +51,7 @@ async function testDoubleUnconfirmed() {
} }
} }
const result = await rp(options) await rp(options)
// console.log(`result.body: ${JSON.stringify(result.body, null, 2)}`) // console.log(`result.body: ${JSON.stringify(result.body, null, 2)}`)
} catch (err) { } catch (err) {
// console.log(`Error: `, err) // console.log(`Error: `, err)
+3 -3
View File
@@ -1,9 +1,9 @@
"use strict" 'use strict'
/** /**
* Read more about panda here: https://panda-suite.github.io/ * Read more about panda here: https://panda-suite.github.io/
*/ */
const panda = require("pandacash-core") const panda = require('pandacash-core')
const runLocalNode = done => { const runLocalNode = done => {
const server = panda.server({ const server = panda.server({
@@ -16,7 +16,7 @@ const runLocalNode = done => {
server.listen({ server.listen({
port: 48332, port: 48332,
walletPort: 48333, walletPort: 48333
}, (err, pandaCashCore) => { }, (err, pandaCashCore) => {
if (err) return console.error(err) if (err) return console.error(err)
+38 -38
View File
@@ -4,42 +4,42 @@
in the tests below to match the rate limit setting in your own installation. in the tests below to match the rate limit setting in your own installation.
*/ */
"use strict" 'use strict'
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const axios = require("axios") const axios = require('axios')
// Used for debugging. // Used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
// const SERVER = `http://192.168.0.36:12400/v3/` // const SERVER = `http://192.168.0.36:12400/v3/`
const SERVER = `http://localhost:3000/v3/` const SERVER = 'http://localhost:3000/v3/'
const TEST_JWT = const TEST_JWT =
"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlM2EwNDE1ZWIyOWE5NjJkYTI3MDhiNCIsImFwaUxldmVsIjowLCJyYXRlTGltaXQiOjEwLCJpYXQiOjE1ODA4NjA0NjcsImV4cCI6MTU4MzQ1MjQ2N30.fuY5S-YrF0J11h5uyMjPe7wiVkYRnIyXi4dL9-V-C6pLJm33p0dSq_pSheVVWw78n5kAvL_9kFHngbnmQiOJYQ" 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlM2EwNDE1ZWIyOWE5NjJkYTI3MDhiNCIsImFwaUxldmVsIjowLCJyYXRlTGltaXQiOjEwLCJpYXQiOjE1ODA4NjA0NjcsImV4cCI6MTU4MzQ1MjQ2N30.fuY5S-YrF0J11h5uyMjPe7wiVkYRnIyXi4dL9-V-C6pLJm33p0dSq_pSheVVWw78n5kAvL_9kFHngbnmQiOJYQ'
describe("#rate limits", () => { describe('#rate limits', () => {
it("should get control/getNetworkInfo() with no auth", async () => { it('should get control/getNetworkInfo() with no auth', async () => {
const options = { const options = {
method: "GET", method: 'GET',
url: `${SERVER}control/getNetworkInfo` url: `${SERVER}control/getNetworkInfo`
} }
const result = await axios(options) const result = await axios(options)
//console.log(`result.status: ${result.status}`) // console.log(`result.status: ${result.status}`)
// console.log(`result.data: ${util.inspect(result.data)}`) // console.log(`result.data: ${util.inspect(result.data)}`)
assert.equal(result.status, 200) assert.equal(result.status, 200)
assert.hasAnyKeys(result.data, ["version"]) assert.hasAnyKeys(result.data, ['version'])
}) })
it("should trigger rate-limit handler if rate limits exceeds 30 request per minute", async () => { it('should trigger rate-limit handler if rate limits exceeds 30 request per minute', async () => {
try { try {
// Actual rate limit is 60 per minute X 4 nodes = 240 rpm. // Actual rate limit is 60 per minute X 4 nodes = 240 rpm.
const options = { const options = {
method: "GET", method: 'GET',
url: `${SERVER}control/getNetworkInfo` url: `${SERVER}control/getNetworkInfo`
} }
@@ -51,29 +51,29 @@ describe("#rate limits", () => {
await Promise.all(promises) await Promise.all(promises)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
//console.log(`err.response: ${util.inspect(err.response)}`) // console.log(`err.response: ${util.inspect(err.response)}`)
assert.equal(err.response.status, 429) assert.equal(err.response.status, 429)
assert.include(err.response.data.error, "Too many requests") assert.include(err.response.data.error, 'Too many requests')
} }
}) })
it("should not trigger rate-limit handler if correct pro-tier password is used", async () => { it('should not trigger rate-limit handler if correct pro-tier password is used', async () => {
try { try {
const username = "BITBOX" const username = 'BITBOX'
// Pro-tier is accessed by using the right password. // Pro-tier is accessed by using the right password.
const password = "BITBOX" const password = 'BITBOX'
//const password = "something" // const password = "something"
const combined = `${username}:${password}` const combined = `${username}:${password}`
const base64Credential = Buffer.from(combined).toString("base64") const base64Credential = Buffer.from(combined).toString('base64')
const readyCredential = `Basic ${base64Credential}` const readyCredential = `Basic ${base64Credential}`
const options = { const options = {
method: "GET", method: 'GET',
url: `${SERVER}control/getNetworkInfo`, url: `${SERVER}control/getNetworkInfo`,
headers: { Authorization: readyCredential } headers: { Authorization: readyCredential }
} }
@@ -86,33 +86,33 @@ describe("#rate limits", () => {
await Promise.all(promises) await Promise.all(promises)
assert.equal(true, true, "Not throwing an error is a pass!") assert.equal(true, true, 'Not throwing an error is a pass!')
} catch (err) { } catch (err) {
// console.log(`err.response: ${util.inspect(err.response)}`) // console.log(`err.response: ${util.inspect(err.response)}`)
assert.equal( assert.equal(
true, true,
false, false,
"This error handler should not have been triggered. Is the password correct?" 'This error handler should not have been triggered. Is the password correct?'
) )
} }
}) })
it("should trigger rate-limit handler if rate limits exceeds pro-tier limit", async () => { it('should trigger rate-limit handler if rate limits exceeds pro-tier limit', async () => {
try { try {
const username = "BITBOX" const username = 'BITBOX'
// Pro-tier is accessed by using the right password. // Pro-tier is accessed by using the right password.
const password = "BITBOX" const password = 'BITBOX'
//const password = "something" // const password = "something"
const combined = `${username}:${password}` const combined = `${username}:${password}`
const base64Credential = Buffer.from(combined).toString("base64") const base64Credential = Buffer.from(combined).toString('base64')
const readyCredential = `Basic ${base64Credential}` const readyCredential = `Basic ${base64Credential}`
// Actual rate limit is 60 per minute X 4 nodes = 240 rpm. // Actual rate limit is 60 per minute X 4 nodes = 240 rpm.
const options = { const options = {
method: "GET", method: 'GET',
url: `${SERVER}control/getNetworkInfo`, url: `${SERVER}control/getNetworkInfo`,
headers: { Authorization: readyCredential } headers: { Authorization: readyCredential }
} }
@@ -125,20 +125,20 @@ describe("#rate limits", () => {
await Promise.all(promises) await Promise.all(promises)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
//console.log(`err.response: ${util.inspect(err.response)}`) // console.log(`err.response: ${util.inspect(err.response)}`)
assert.equal(err.response.status, 429) assert.equal(err.response.status, 429)
assert.include(err.response.data.error, "Too many requests") assert.include(err.response.data.error, 'Too many requests')
} }
}) })
it("should unlock pro-tier for a valid JWT token", async () => { it('should unlock pro-tier for a valid JWT token', async () => {
try { try {
// Actual rate limit is 60 per minute X 4 nodes = 240 rpm. // Actual rate limit is 60 per minute X 4 nodes = 240 rpm.
const options = { const options = {
method: "GET", method: 'GET',
url: `${SERVER}control/`, url: `${SERVER}control/`,
headers: { headers: {
Authorization: `Token ${TEST_JWT}` Authorization: `Token ${TEST_JWT}`
@@ -153,12 +153,12 @@ describe("#rate limits", () => {
await Promise.all(promises) await Promise.all(promises)
//assert.equal(true, false, "Unexpected result!") // assert.equal(true, false, "Unexpected result!")
assert.equal(true, true, "Not throwing an error is a pass!") assert.equal(true, true, 'Not throwing an error is a pass!')
} catch (err) { } catch (err) {
console.log(`err.response: ${util.inspect(err.response)}`) console.log(`err.response: ${util.inspect(err.response)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} }
// Override default timeout for this test. // Override default timeout for this test.
}).timeout(20000) }).timeout(20000)
+56 -56
View File
@@ -9,129 +9,129 @@
so it is omitted here. so it is omitted here.
*/ */
"use strict" 'use strict'
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const rawtransactions = require("../../../dist/routes/v2/rawtransactions") // const rawtransactions = require('../../../dist/routes/v2/rawtransactions')
const rp = require("request-promise") const rp = require('request-promise')
// Used for debugging. // Used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
const mockData = require("../mocks/raw-transactions-mocks") // const mockData = require('../mocks/raw-transactions-mocks')
describe("#Raw-Transactions", () => { describe('#Raw-Transactions', () => {
describe("#root", () => { describe('#root', () => {
it(`should return root`, async () => { it('should return root', async () => {
const options = { const options = {
method: "GET", method: 'GET',
uri: `http://localhost:3000/v2/rawtransactions/`, uri: 'http://localhost:3000/v2/rawtransactions/',
resolveWithFullResponse: true, resolveWithFullResponse: true,
json: true json: true
} }
const result = await rp(options) const result = await rp(options)
//console.log(`result: ${JSON.stringify(result, null, 0)}`) // console.log(`result: ${JSON.stringify(result, null, 0)}`)
assert.equal(result.body.status, "rawtransactions") assert.equal(result.body.status, 'rawtransactions')
}) })
}) })
describe("#whCreateTx", () => { describe('#whCreateTx', () => {
it(`should return tx hex`, async () => { it('should return tx hex', async () => {
const minIn = { const minIn = {
txid: txid:
"f7ed9cf23dee85910f6269c9a101a75fcfd2f3c6fc81f17fad824ff7aaf99ab2", 'f7ed9cf23dee85910f6269c9a101a75fcfd2f3c6fc81f17fad824ff7aaf99ab2',
vout: 1 vout: 1
} }
const options = { const options = {
method: "PUT", method: 'PUT',
uri: `http://localhost:3000/v2/rawtransactions/create`, uri: 'http://localhost:3000/v2/rawtransactions/create',
resolveWithFullResponse: true, resolveWithFullResponse: true,
json: true, json: true,
body: { body: {
//inputs: [mockData.mockWHCreateInput], // inputs: [mockData.mockWHCreateInput],
inputs: [minIn], inputs: [minIn],
outputs: {} outputs: {}
} }
} }
const result = await rp(options) const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) // console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.equal( assert.equal(
result.body, result.body,
"0200000001b29af9aaf74f82ad7ff181fcc6f3d2cf5fa701a1c969620f9185ee3df29cedf70100000000ffffffff0000000000" '0200000001b29af9aaf74f82ad7ff181fcc6f3d2cf5fa701a1c969620f9185ee3df29cedf70100000000ffffffff0000000000'
) )
}) })
}) })
describe("#whOpReturn", () => { describe('#whOpReturn', () => {
it(`should return tx hex`, async () => { it('should return tx hex', async () => {
const options = { const options = {
method: "PUT", method: 'PUT',
uri: `http://localhost:3000/v2/rawtransactions/opreturn`, uri: 'http://localhost:3000/v2/rawtransactions/opreturn',
resolveWithFullResponse: true, resolveWithFullResponse: true,
json: true, json: true,
body: { body: {
rawtx: "01000000000000000000", rawtx: '01000000000000000000',
payload: "00000000000000020000000006dac2c0" payload: '00000000000000020000000006dac2c0'
} }
} }
const result = await rp(options) const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) // console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.equal( assert.equal(
result.body, result.body,
"0100000000010000000000000000166a140877686300000000000000020000000006dac2c000000000" '0100000000010000000000000000166a140877686300000000000000020000000006dac2c000000000'
) )
}) })
}) })
describe("#whReference", () => { describe('#whReference', () => {
it(`should return tx hex`, async () => { it('should return tx hex', async () => {
const options = { const options = {
method: "PUT", method: 'PUT',
uri: `http://localhost:3000/v2/rawtransactions/reference`, uri: 'http://localhost:3000/v2/rawtransactions/reference',
resolveWithFullResponse: true, resolveWithFullResponse: true,
json: true, json: true,
body: { body: {
rawtx: rawtx:
"0100000001a7a9402ecd77f3c9f745793c9ec805bfa2e14b89877581c734c774864247e6f50400000000ffffffff03aa0a0000000000001976a9146d18edfe073d53f84dd491dae1379f8fb0dfe5d488ac5c0d0000000000004751210252ce4bdd3ce38b4ebbc5a6e1343608230da508ff12d23d85b58c964204c4cef3210294cc195fc096f87d0f813a337ae7e5f961b1c8a18f1f8604a909b3a5121f065b52aeaa0a0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac00000000", '0100000001a7a9402ecd77f3c9f745793c9ec805bfa2e14b89877581c734c774864247e6f50400000000ffffffff03aa0a0000000000001976a9146d18edfe073d53f84dd491dae1379f8fb0dfe5d488ac5c0d0000000000004751210252ce4bdd3ce38b4ebbc5a6e1343608230da508ff12d23d85b58c964204c4cef3210294cc195fc096f87d0f813a337ae7e5f961b1c8a18f1f8604a909b3a5121f065b52aeaa0a0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac00000000',
destination: "bitcoincash:qrn60nerx5zug4u4hal06atep3lzhtecvy4pxk75lf", destination: 'bitcoincash:qrn60nerx5zug4u4hal06atep3lzhtecvy4pxk75lf',
amount: 0.005 amount: 0.005
} }
} }
const result = await rp(options) const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) // console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.isString(result.body) assert.isString(result.body)
}) })
}) })
describe("#whChangeOutput", () => { describe('#whChangeOutput', () => {
it(`should return tx hex`, async () => { it('should return tx hex', async () => {
const options = { const options = {
method: "PUT", method: 'PUT',
uri: `http://localhost:3000/v2/rawtransactions/change`, uri: 'http://localhost:3000/v2/rawtransactions/change',
resolveWithFullResponse: true, resolveWithFullResponse: true,
json: true, json: true,
body: { body: {
rawtx: rawtx:
"0100000001b15ee60431ef57ec682790dec5a3c0d83a0c360633ea8308fbf6d5fc10a779670400000000ffffffff025c0d00000000000047512102f3e471222bb57a7d416c82bf81c627bfcd2bdc47f36e763ae69935bba4601ece21021580b888ff56feb27f17f08802ebed26258c23697d6a462d43fc13b565fda2dd52aeaa0a0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac00000000", '0100000001b15ee60431ef57ec682790dec5a3c0d83a0c360633ea8308fbf6d5fc10a779670400000000ffffffff025c0d00000000000047512102f3e471222bb57a7d416c82bf81c627bfcd2bdc47f36e763ae69935bba4601ece21021580b888ff56feb27f17f08802ebed26258c23697d6a462d43fc13b565fda2dd52aeaa0a0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac00000000',
destination: "bitcoincash:qrn60nerx5zug4u4hal06atep3lzhtecvy4pxk75lf", destination: 'bitcoincash:qrn60nerx5zug4u4hal06atep3lzhtecvy4pxk75lf',
prevtxs: [ prevtxs: [
{ {
txid: txid:
"6779a710fcd5f6fb0883ea3306360c3ad8c0a3c5de902768ec57ef3104e65eb1", '6779a710fcd5f6fb0883ea3306360c3ad8c0a3c5de902768ec57ef3104e65eb1',
vout: 4, vout: 4,
scriptPubKey: scriptPubKey:
"76a9147b25205fd98d462880a3e5b0541235831ae959e588ac", '76a9147b25205fd98d462880a3e5b0541235831ae959e588ac',
value: 0.00068257 value: 0.00068257
} }
], ],
@@ -140,43 +140,43 @@ describe("#Raw-Transactions", () => {
} }
const result = await rp(options) const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) // console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.isString(result.body) assert.isString(result.body)
}) })
}) })
describe("#whInput", () => { describe('#whInput', () => {
it(`should return tx hex`, async () => { it('should return tx hex', async () => {
const options = { const options = {
method: "PUT", method: 'PUT',
uri: `http://localhost:3000/v2/rawtransactions/input`, uri: 'http://localhost:3000/v2/rawtransactions/input',
resolveWithFullResponse: true, resolveWithFullResponse: true,
json: true, json: true,
body: { body: {
txid: txid:
"b006729017df05eda586df9ad3f8ccfee5be340aadf88155b784d1fc0e8342ee", 'b006729017df05eda586df9ad3f8ccfee5be340aadf88155b784d1fc0e8342ee',
n: 0 n: 0
} }
} }
const result = await rp(options) const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) // console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.isString(result.body) assert.isString(result.body)
}) })
}) })
describe("#getRawTransaction", () => { describe('#getRawTransaction', () => {
it(`should return tx hex`, async () => { it('should return tx hex', async () => {
const options = { const options = {
method: "POST", method: 'POST',
uri: `http://localhost:3000/v2/rawtransactions/getRawTransaction`, uri: 'http://localhost:3000/v2/rawtransactions/getRawTransaction',
resolveWithFullResponse: true, resolveWithFullResponse: true,
json: true, json: true,
body: { body: {
txids: [ txids: [
"0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098" '0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098'
], ],
verbose: true verbose: true
} }
+49 -49
View File
@@ -6,24 +6,24 @@
to 'integration' to run the tests against BCH mainnet. to 'integration' to run the tests against BCH mainnet.
*/ */
"use strict" 'use strict'
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const miningRoute = require("../../src/routes/v3/full-node/mining") const miningRoute = require('../../src/routes/v3/full-node/mining')
const nock = require("nock") // HTTP mocking const nock = require('nock') // HTTP mocking
let originalEnvVars // Used during transition from integration to unit tests. let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data. // Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks") const { mockReq, mockRes } = require('./mocks/express-mocks')
const mockData = require("./mocks/mining-mocks") const mockData = require('./mocks/mining-mocks')
// Used for debugging. // Used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
describe("#Mining", () => { describe('#Mining', () => {
let req, res let req, res
before(() => { before(() => {
@@ -36,12 +36,12 @@ describe("#Mining", () => {
} }
// Set default environment variables for unit tests. // Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit" if (!process.env.TEST) process.env.TEST = 'unit'
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" process.env.BITCOINCOM_BASEURL = 'http://fakeurl/api/'
process.env.RPC_BASEURL = "http://fakeurl/api" process.env.RPC_BASEURL = 'http://fakeurl/api'
process.env.RPC_USERNAME = "fakeusername" process.env.RPC_USERNAME = 'fakeusername'
process.env.RPC_PASSWORD = "fakepassword" process.env.RPC_PASSWORD = 'fakepassword'
} }
}) })
@@ -74,30 +74,30 @@ describe("#Mining", () => {
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
}) })
describe("#root", async () => { describe('#root', async () => {
// root route handler. // root route handler.
const root = miningRoute.testableComponents.root const root = miningRoute.testableComponents.root
it("should respond to GET for base route", async () => { it('should respond to GET for base route', async () => {
const result = root(req, res) const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "mining", "Returns static string") assert.equal(result.status, 'mining', 'Returns static string')
}) })
}) })
describe("#getMiningInfo", async () => { describe('#getMiningInfo', async () => {
const getMiningInfo = miningRoute.testableComponents.getMiningInfo const getMiningInfo = miningRoute.testableComponents.getMiningInfo
it("should throw 503 when network issues", async () => { it('should throw 503 when network issues', async () => {
// Save the existing RPC URL. // Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error. // Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/" process.env.RPC_BASEURL = 'http://fakeurl/api/'
const result = await getMiningInfo(req, res) await getMiningInfo(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL. // Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2 process.env.RPC_BASEURL = savedUrl2
@@ -105,48 +105,48 @@ describe("#Mining", () => {
assert.isAbove( assert.isAbove(
res.statusCode, res.statusCode,
499, 499,
"HTTP status code 500 or greater expected." 'HTTP status code 500 or greater expected.'
) )
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") // assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
}) })
it("should GET mining information", async () => { it('should GET mining information', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.RPC_BASEURL}`) nock(`${process.env.RPC_BASEURL}`)
.post(uri => uri.includes("/")) .post(uri => uri.includes('/'))
.reply(200, { result: mockData.mockMiningInfo }) .reply(200, { result: mockData.mockMiningInfo })
} }
const result = await getMiningInfo(req, res) const result = await getMiningInfo(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, [ assert.hasAllKeys(result, [
"blocks", 'blocks',
"currentblocksize", 'currentblocksize',
"currentblocktx", 'currentblocktx',
"difficulty", 'difficulty',
"blockprioritypercentage", 'blockprioritypercentage',
"warnings", 'warnings',
"networkhashps", 'networkhashps',
"pooledtx", 'pooledtx',
"chain" 'chain'
]) ])
}) })
}) })
describe("#getNetworkHashPS", async () => { describe('#getNetworkHashPS', async () => {
const getNetworkHashPS = miningRoute.testableComponents.getNetworkHashPS const getNetworkHashPS = miningRoute.testableComponents.getNetworkHashPS
it("should throw 503 when network issues", async () => { it('should throw 503 when network issues', async () => {
// Save the existing RPC URL. // Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error. // Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/" process.env.RPC_BASEURL = 'http://fakeurl/api/'
const result = await getNetworkHashPS(req, res) await getNetworkHashPS(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL. // Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2 process.env.RPC_BASEURL = savedUrl2
@@ -154,21 +154,21 @@ describe("#Mining", () => {
assert.isAbove( assert.isAbove(
res.statusCode, res.statusCode,
499, 499,
"HTTP status code 500 or greater expected." 'HTTP status code 500 or greater expected.'
) )
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") // assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
}) })
it("should GET Network Hash per second", async () => { it('should GET Network Hash per second', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.RPC_BASEURL}`) nock(`${process.env.RPC_BASEURL}`)
.post(uri => uri.includes("/")) .post(uri => uri.includes('/'))
.reply(200, { result: 517604755.6648782 }) .reply(200, { result: 517604755.6648782 })
} }
const result = await getNetworkHashPS(req, res) const result = await getNetworkHashPS(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.isNumber(result) assert.isNumber(result)
}) })
+33 -33
View File
@@ -2,10 +2,10 @@
This library contains mocking data for running unit tests on the address route. This library contains mocking data for running unit tests on the address route.
*/ */
"use strict" 'use strict'
const mockAddressDetails = { const mockAddressDetails = {
addrStr: "1Fg4r9iDrEkCcDmHTy2T79EusNfhyQpu7W", addrStr: '1Fg4r9iDrEkCcDmHTy2T79EusNfhyQpu7W',
balance: 0.00126419, balance: 0.00126419,
balanceSat: 126419, balanceSat: 126419,
totalReceived: 0.02175868, totalReceived: 0.02175868,
@@ -17,17 +17,17 @@ const mockAddressDetails = {
unconfirmedTxApperances: 0, unconfirmedTxApperances: 0,
txApperances: 3, txApperances: 3,
transactions: [ transactions: [
"2dc053f55a666a3d2a08b1c680b704d62a55506d14ad884add87edcc56b9277d", '2dc053f55a666a3d2a08b1c680b704d62a55506d14ad884add87edcc56b9277d',
"544c15ce35c0f2e808d28f29d6587f1ec9276233e29856b7f2938cf0daef0026", '544c15ce35c0f2e808d28f29d6587f1ec9276233e29856b7f2938cf0daef0026',
"81039b1d7b855b133f359f9dc65f776bd105650153a941675fedc504228ddbd3" '81039b1d7b855b133f359f9dc65f776bd105650153a941675fedc504228ddbd3'
], ],
legacyAddress: "1Fg4r9iDrEkCcDmHTy2T79EusNfhyQpu7W", legacyAddress: '1Fg4r9iDrEkCcDmHTy2T79EusNfhyQpu7W',
cashAddress: "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" cashAddress: 'bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'
} }
const mockUtxoDetails = [ const mockUtxoDetails = [
{ {
txid: "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e", txid: '15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e',
vout: 286, vout: 286,
amount: 0.00001, amount: 0.00001,
satoshis: 1000, satoshis: 1000,
@@ -35,7 +35,7 @@ const mockUtxoDetails = [
confirmations: 3490 confirmations: 3490
}, },
{ {
txid: "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e", txid: '15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e',
vout: 287, vout: 287,
amount: 0.00001, amount: 0.00001,
satoshis: 1000, satoshis: 1000,
@@ -43,7 +43,7 @@ const mockUtxoDetails = [
confirmations: 3490 confirmations: 3490
}, },
{ {
txid: "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e", txid: '15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e',
vout: 288, vout: 288,
amount: 0.00001, amount: 0.00001,
satoshis: 1000, satoshis: 1000,
@@ -54,10 +54,10 @@ const mockUtxoDetails = [
const mockUnconfirmed = [ const mockUnconfirmed = [
{ {
address: "1EzdL6TBbkNhnB2fYiBaKmcs5fxaoqwdAp", address: '1EzdL6TBbkNhnB2fYiBaKmcs5fxaoqwdAp',
txid: "000c00a90fb5031da6e02f7625df2f8b35a4c16e6feb9fc72293e67e5ff75786", txid: '000c00a90fb5031da6e02f7625df2f8b35a4c16e6feb9fc72293e67e5ff75786',
vout: 0, vout: 0,
scriptPubKey: "76a914997fabcd94a1e2aaa13a7664362e5e7b96c169a988ac", scriptPubKey: '76a914997fabcd94a1e2aaa13a7664362e5e7b96c169a988ac',
amount: 0.00999626, amount: 0.00999626,
satoshis: 999626, satoshis: 999626,
confirmations: 0, confirmations: 0,
@@ -69,40 +69,40 @@ const mockTransactions = {
pagesTotal: 1, pagesTotal: 1,
txs: [ txs: [
{ {
txid: "000c00a90fb5031da6e02f7625df2f8b35a4c16e6feb9fc72293e67e5ff75786", txid: '000c00a90fb5031da6e02f7625df2f8b35a4c16e6feb9fc72293e67e5ff75786',
version: 2, version: 2,
locktime: 549565, locktime: 549565,
vin: [ vin: [
{ {
txid: txid:
"45c891c6d44619fc85716ba1b593aa83ebc1e500fe611b1ab98531ea203a0f21", '45c891c6d44619fc85716ba1b593aa83ebc1e500fe611b1ab98531ea203a0f21',
vout: 209, vout: 209,
sequence: 4294967294, sequence: 4294967294,
n: 0, n: 0,
scriptSig: { scriptSig: {
hex: hex:
"47304402205b136f348bedae61a87c0500979d47ab02c76f0e4aba866b94c4931fccb5d7dc0220757a141660475b0cd4cb8b1f54b1ace95105f5bea775e817b115fb589fe4477541210246daec651c506f353daa7468714399c773df42ac4d663022d0e446a3331ac64a", '47304402205b136f348bedae61a87c0500979d47ab02c76f0e4aba866b94c4931fccb5d7dc0220757a141660475b0cd4cb8b1f54b1ace95105f5bea775e817b115fb589fe4477541210246daec651c506f353daa7468714399c773df42ac4d663022d0e446a3331ac64a',
asm: asm:
"304402205b136f348bedae61a87c0500979d47ab02c76f0e4aba866b94c4931fccb5d7dc0220757a141660475b0cd4cb8b1f54b1ace95105f5bea775e817b115fb589fe44775[ALL|FORKID] 0246daec651c506f353daa7468714399c773df42ac4d663022d0e446a3331ac64a" '304402205b136f348bedae61a87c0500979d47ab02c76f0e4aba866b94c4931fccb5d7dc0220757a141660475b0cd4cb8b1f54b1ace95105f5bea775e817b115fb589fe44775[ALL|FORKID] 0246daec651c506f353daa7468714399c773df42ac4d663022d0e446a3331ac64a'
}, },
addr: "14TdovsQUL69xL5g3zSzhLpmDb93d9rU9m", addr: '14TdovsQUL69xL5g3zSzhLpmDb93d9rU9m',
valueSat: 1979534, valueSat: 1979534,
value: 0.01979534, value: 0.01979534,
doubleSpentTxID: null doubleSpentTxID: null
}, },
{ {
txid: txid:
"6e5fa79547112a912adf6082c975dc80e282045193512ea369adb6bac7420674", '6e5fa79547112a912adf6082c975dc80e282045193512ea369adb6bac7420674',
vout: 147, vout: 147,
sequence: 4294967294, sequence: 4294967294,
n: 1, n: 1,
scriptSig: { scriptSig: {
hex: hex:
"473044022056674ac83a37d54ffec2c6d33cfc2ae2256f821b8bd818e1b6783356b49f3d28022046d3dd147c8fc51f2da5d16996a3cea1d7314e327aa1ae1017418737ed20136a4121038f23af565f68d8455e07e5c33c5cfc5114de2f478f035db23eb02f8ff5fe7f6e", '473044022056674ac83a37d54ffec2c6d33cfc2ae2256f821b8bd818e1b6783356b49f3d28022046d3dd147c8fc51f2da5d16996a3cea1d7314e327aa1ae1017418737ed20136a4121038f23af565f68d8455e07e5c33c5cfc5114de2f478f035db23eb02f8ff5fe7f6e',
asm: asm:
"3044022056674ac83a37d54ffec2c6d33cfc2ae2256f821b8bd818e1b6783356b49f3d28022046d3dd147c8fc51f2da5d16996a3cea1d7314e327aa1ae1017418737ed20136a[ALL|FORKID] 038f23af565f68d8455e07e5c33c5cfc5114de2f478f035db23eb02f8ff5fe7f6e" '3044022056674ac83a37d54ffec2c6d33cfc2ae2256f821b8bd818e1b6783356b49f3d28022046d3dd147c8fc51f2da5d16996a3cea1d7314e327aa1ae1017418737ed20136a[ALL|FORKID] 038f23af565f68d8455e07e5c33c5cfc5114de2f478f035db23eb02f8ff5fe7f6e'
}, },
addr: "1CEtC2fjELvmAzTd6MWuHb1gvuSq8x3Xpb", addr: '1CEtC2fjELvmAzTd6MWuHb1gvuSq8x3Xpb',
valueSat: 10466, valueSat: 10466,
value: 0.00010466, value: 0.00010466,
doubleSpentTxID: null doubleSpentTxID: null
@@ -110,28 +110,28 @@ const mockTransactions = {
], ],
vout: [ vout: [
{ {
value: "0.00999626", value: '0.00999626',
n: 0, n: 0,
scriptPubKey: { scriptPubKey: {
hex: "76a914997fabcd94a1e2aaa13a7664362e5e7b96c169a988ac", hex: '76a914997fabcd94a1e2aaa13a7664362e5e7b96c169a988ac',
asm: asm:
"OP_DUP OP_HASH160 997fabcd94a1e2aaa13a7664362e5e7b96c169a9 OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 997fabcd94a1e2aaa13a7664362e5e7b96c169a9 OP_EQUALVERIFY OP_CHECKSIG',
addresses: ["1EzdL6TBbkNhnB2fYiBaKmcs5fxaoqwdAp"], addresses: ['1EzdL6TBbkNhnB2fYiBaKmcs5fxaoqwdAp'],
type: "pubkeyhash" type: 'pubkeyhash'
}, },
spentTxId: null, spentTxId: null,
spentIndex: null, spentIndex: null,
spentHeight: null spentHeight: null
}, },
{ {
value: "0.00990000", value: '0.00990000',
n: 1, n: 1,
scriptPubKey: { scriptPubKey: {
hex: "76a914e3335e4d6babe61ea58311293c1c6bfe802801cb88ac", hex: '76a914e3335e4d6babe61ea58311293c1c6bfe802801cb88ac',
asm: asm:
"OP_DUP OP_HASH160 e3335e4d6babe61ea58311293c1c6bfe802801cb OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 e3335e4d6babe61ea58311293c1c6bfe802801cb OP_EQUALVERIFY OP_CHECKSIG',
addresses: ["1MiKvgaQTFCTEZbSDMZgw9ahaB52Cr4ofb"], addresses: ['1MiKvgaQTFCTEZbSDMZgw9ahaB52Cr4ofb'],
type: "pubkeyhash" type: 'pubkeyhash'
}, },
spentTxId: null, spentTxId: null,
spentIndex: null, spentIndex: null,
@@ -139,7 +139,7 @@ const mockTransactions = {
} }
], ],
blockhash: blockhash:
"00000000000000000158b1a2883688873ec5ea076e3b0f576bcdfe1fd277880f", '00000000000000000158b1a2883688873ec5ea076e3b0f576bcdfe1fd277880f',
blockheight: 549601, blockheight: 549601,
confirmations: 5, confirmations: 5,
time: 1537990026, time: 1537990026,
+8 -8
View File
@@ -2,7 +2,7 @@
This library contains mocking data for running unit tests on the bitcore route. This library contains mocking data for running unit tests on the bitcore route.
*/ */
"use strict" 'use strict'
const mockBalance = { const mockBalance = {
confirmed: 10000000, confirmed: 10000000,
@@ -12,18 +12,18 @@ const mockBalance = {
const mockUtxos = [ const mockUtxos = [
{ {
_id: "5cf2c31a33bd46a95ec7e730", _id: '5cf2c31a33bd46a95ec7e730',
chain: "BCH", chain: 'BCH',
network: "testnet", network: 'testnet',
coinbase: false, coinbase: false,
mintIndex: 1, mintIndex: 1,
spentTxid: "", spentTxid: '',
mintTxid: mintTxid:
"5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392", '5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392',
mintHeight: 1265275, mintHeight: 1265275,
spentHeight: -2, spentHeight: -2,
address: "qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4", address: 'qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4',
script: "76a9140e5b4ad9008bb9a027b7e2d0ef958914e12db20788ac", script: '76a9140e5b4ad9008bb9a027b7e2d0ef958914e12db20788ac',
value: 10000000, value: 10000000,
confirmations: -1 confirmations: -1
} }
+13 -13
View File
@@ -2,39 +2,39 @@
This library contains mocking data for running unit tests on the address route. This library contains mocking data for running unit tests on the address route.
*/ */
"use strict" 'use strict'
const mockBlockDetails = { const mockBlockDetails = {
hash: "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79", hash: '00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79',
size: 1319, size: 1319,
height: 1267544, height: 1267544,
version: 536870912, version: 536870912,
merkleroot: merkleroot:
"c2cd01ec2cf149acc7631385f89ae103d1a2ad212ab810c862d9680a811618ce", 'c2cd01ec2cf149acc7631385f89ae103d1a2ad212ab810c862d9680a811618ce',
tx: [ tx: [
"52bfa89d449fef8070ec33e7a61f5ec8b5417ff62b050cd6a144ebce9557e0fa", '52bfa89d449fef8070ec33e7a61f5ec8b5417ff62b050cd6a144ebce9557e0fa',
"8988e8742d2523f667c2d1374861919e3a82903e059fec75c10795da2303b93b", '8988e8742d2523f667c2d1374861919e3a82903e059fec75c10795da2303b93b',
"41def004f22b196d675566a6983ecf97611e72c48375b8ba779983d5ccd369d7", '41def004f22b196d675566a6983ecf97611e72c48375b8ba779983d5ccd369d7',
"8ca8efb06abb94395f8331f87e101504dcf66aee828f2111922f056971fa7502", '8ca8efb06abb94395f8331f87e101504dcf66aee828f2111922f056971fa7502',
"9ab409cbb203be7cf22bd6598d787fcdd5b417d885b72207ecc7253470770649" '9ab409cbb203be7cf22bd6598d787fcdd5b417d885b72207ecc7253470770649'
], ],
time: 1542048973, time: 1542048973,
nonce: 3936284753, nonce: 3936284753,
bits: "1a03c148", bits: '1a03c148',
difficulty: 4467892.99177529, difficulty: 4467892.99177529,
chainwork: "00000000000000000000000000000000000000000000003ee27b503a064045bd", chainwork: '00000000000000000000000000000000000000000000003ee27b503a064045bd',
confirmations: 3, confirmations: 3,
previousblockhash: previousblockhash:
"00000000000001891202cbe18729a02a8763cf04da0ca7aded6e6c7d9b500785", '00000000000001891202cbe18729a02a8763cf04da0ca7aded6e6c7d9b500785',
nextblockhash: nextblockhash:
"000000000000039b93b3f3403a0eb21150904b15229d998cf3788fd8bf192cc2", '000000000000039b93b3f3403a0eb21150904b15229d998cf3788fd8bf192cc2',
reward: 0.78125, reward: 0.78125,
isMainChain: true, isMainChain: true,
poolInfo: {} poolInfo: {}
} }
const mockBlockHash = const mockBlockHash =
"00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79" '00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79'
module.exports = { module.exports = {
mockBlockDetails, mockBlockDetails,
+25 -25
View File
@@ -2,70 +2,70 @@
This library contains mocking data for running unit tests on the blockbook route. This library contains mocking data for running unit tests on the blockbook route.
*/ */
"use strict" 'use strict'
const mockBalance = { const mockBalance = {
page: 1, page: 1,
totalPages: 1, totalPages: 1,
itemsOnPage: 1000, itemsOnPage: 1000,
address: "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", address: 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
balance: "10000000", balance: '10000000',
totalReceived: "10000000", totalReceived: '10000000',
totalSent: "0", totalSent: '0',
unconfirmedBalance: "0", unconfirmedBalance: '0',
unconfirmedTxs: 0, unconfirmedTxs: 0,
txs: 1, txs: 1,
txids: ["5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392"] txids: ['5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392']
} }
const mockUtxos = [ const mockUtxos = [
{ {
txid: "5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392", txid: '5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392',
vout: 1, vout: 1,
value: "10000000", value: '10000000',
height: 1265275, height: 1265275,
confirmations: 42704 confirmations: 42704
} }
] ]
const mockTx = { const mockTx = {
txid: "5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392", txid: '5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392',
version: 2, version: 2,
vin: [ vin: [
{ {
txid: "85ddb8215fc3701a493cf1c450644c5ef32c55aaa2f48ae2d008944394f3e4d3", txid: '85ddb8215fc3701a493cf1c450644c5ef32c55aaa2f48ae2d008944394f3e4d3',
sequence: 4294967295, sequence: 4294967295,
n: 0, n: 0,
addresses: ["bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35"], addresses: ['bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35'],
value: "16983000648", value: '16983000648',
hex: hex:
"47304402202378e55f4d02bb932498deef22dfc1f7a4984858c3b55017e225dd567172252e0220373d27710b5d42a72ac9725959f1605a912fee0ae86a7ad36e7d6d796f14ca29412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792" '47304402202378e55f4d02bb932498deef22dfc1f7a4984858c3b55017e225dd567172252e0220373d27710b5d42a72ac9725959f1605a912fee0ae86a7ad36e7d6d796f14ca29412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792'
} }
], ],
vout: [ vout: [
{ {
value: "16973000422", value: '16973000422',
n: 0, n: 0,
spent: true, spent: true,
hex: "76a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac", hex: '76a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac',
addresses: ["bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35"] addresses: ['bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35']
}, },
{ {
value: "10000000", value: '10000000',
n: 1, n: 1,
hex: "76a9140e5b4ad9008bb9a027b7e2d0ef958914e12db20788ac", hex: '76a9140e5b4ad9008bb9a027b7e2d0ef958914e12db20788ac',
addresses: ["bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4"] addresses: ['bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4']
} }
], ],
blockHash: "00000000005242edac4635ac2375a454e801cc1be8b131b622328089731e5e30", blockHash: '00000000005242edac4635ac2375a454e801cc1be8b131b622328089731e5e30',
blockHeight: 1265275, blockHeight: 1265275,
confirmations: 65402, confirmations: 65402,
blockTime: 1540912733, blockTime: 1540912733,
value: "16983000422", value: '16983000422',
valueIn: "16983000648", valueIn: '16983000648',
fees: "226", fees: '226',
hex: hex:
"0200000001d3e4f394439408d0e28af4a2aa552cf35e4c6450c4f13c491a70c35f21b8dd85000000006a47304402202378e55f4d02bb932498deef22dfc1f7a4984858c3b55017e225dd567172252e0220373d27710b5d42a72ac9725959f1605a912fee0ae86a7ad36e7d6d796f14ca29412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02e66eabf3030000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a9140e5b4ad9008bb9a027b7e2d0ef958914e12db20788ac00000000" '0200000001d3e4f394439408d0e28af4a2aa552cf35e4c6450c4f13c491a70c35f21b8dd85000000006a47304402202378e55f4d02bb932498deef22dfc1f7a4984858c3b55017e225dd567172252e0220373d27710b5d42a72ac9725959f1605a912fee0ae86a7ad36e7d6d796f14ca29412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02e66eabf3030000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a9140e5b4ad9008bb9a027b7e2d0ef958914e12db20788ac00000000'
} }
module.exports = { module.exports = {
+105 -105
View File
@@ -2,30 +2,30 @@
This library contains mocking data for running unit tests on the address route. This library contains mocking data for running unit tests on the address route.
*/ */
"use strict" 'use strict'
const mockBlockHash = const mockBlockHash =
"00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79" '00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79'
const mockBlockchainInfo = { const mockBlockchainInfo = {
chain: "test", chain: 'test',
blocks: 1267694, blocks: 1267694,
headers: 1267694, headers: 1267694,
bestblockhash: bestblockhash:
"000000000000013eaf80d1e157e32804c36a58ad0bb26ca59833880c80298780", '000000000000013eaf80d1e157e32804c36a58ad0bb26ca59833880c80298780',
difficulty: 4105763.969035785, difficulty: 4105763.969035785,
mediantime: 1542131305, mediantime: 1542131305,
verificationprogress: 0.9999968911571566, verificationprogress: 0.9999968911571566,
chainwork: "00000000000000000000000000000000000000000000003f0443b0b5f02ce255", chainwork: '00000000000000000000000000000000000000000000003f0443b0b5f02ce255',
pruned: false, pruned: false,
softforks: [ softforks: [
{ id: "bip34", version: 2, reject: { status: true } }, { id: 'bip34', version: 2, reject: { status: true } },
{ id: "bip66", version: 3, reject: { status: true } }, { id: 'bip66', version: 3, reject: { status: true } },
{ id: "bip65", version: 4, reject: { status: true } } { id: 'bip65', version: 4, reject: { status: true } }
], ],
bip9_softforks: { bip9_softforks: {
csv: { csv: {
status: "active", status: 'active',
startTime: 1456790400, startTime: 1456790400,
timeout: 1493596800, timeout: 1493596800,
since: 770112 since: 770112
@@ -36,171 +36,171 @@ const mockBlockchainInfo = {
const mockChainTips = [ const mockChainTips = [
{ {
height: 1267696, height: 1267696,
hash: "000000000000035bfc43a642ebbff8cfc1e88b1d564de8b0a6c7e2797eeafb21", hash: '000000000000035bfc43a642ebbff8cfc1e88b1d564de8b0a6c7e2797eeafb21',
branchlen: 0, branchlen: 0,
status: "active" status: 'active'
}, },
{ {
height: 1267581, height: 1267581,
hash: "000000000001bd12e4124207682563135b21353ca3087bc6b0c409f7f9e1da91", hash: '000000000001bd12e4124207682563135b21353ca3087bc6b0c409f7f9e1da91',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1267375, height: 1267375,
hash: "00000000702036979df70236bcc45dfc72d43d5d0e6834007afa1fa627e49587", hash: '00000000702036979df70236bcc45dfc72d43d5d0e6834007afa1fa627e49587',
branchlen: 1036, branchlen: 1036,
status: "headers-only" status: 'headers-only'
}, },
{ {
height: 1266979, height: 1266979,
hash: "00000000e851abbde2174ccdc5c2508b909f81f23c4b7abeac864eee1a4891c7", hash: '00000000e851abbde2174ccdc5c2508b909f81f23c4b7abeac864eee1a4891c7',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266973, height: 1266973,
hash: "000000007e1324d2900ed70947f89ab8fd4a267e87c9244c333215659a7370d5", hash: '000000007e1324d2900ed70947f89ab8fd4a267e87c9244c333215659a7370d5',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266967, height: 1266967,
hash: "000000000f37f12843d4800f50ec4de44dce0432e4d448242d43ff04a7a2948d", hash: '000000000f37f12843d4800f50ec4de44dce0432e4d448242d43ff04a7a2948d',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266963, height: 1266963,
hash: "00000000ae11ec99e88898cbd72567cb4cb79e7fc13243357bef79632769deb4", hash: '00000000ae11ec99e88898cbd72567cb4cb79e7fc13243357bef79632769deb4',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266962, height: 1266962,
hash: "000000004f97851259b4460a049e44fa5bd4beadee04a85bf81bd2e90c4f24f9", hash: '000000004f97851259b4460a049e44fa5bd4beadee04a85bf81bd2e90c4f24f9',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266950, height: 1266950,
hash: "00000000d12a0e4a827f73ecaadb563df7e99817294939f2be8e943079dc60b2", hash: '00000000d12a0e4a827f73ecaadb563df7e99817294939f2be8e943079dc60b2',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266944, height: 1266944,
hash: "00000000612d16af273217818e8e7ac5014a19f68ed04e1bc897b1be0a9c744f", hash: '00000000612d16af273217818e8e7ac5014a19f68ed04e1bc897b1be0a9c744f',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266941, height: 1266941,
hash: "0000000000002b91cdb15cacf3368da2c9ffce511d64092193b507dad75f4a27", hash: '0000000000002b91cdb15cacf3368da2c9ffce511d64092193b507dad75f4a27',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266896, height: 1266896,
hash: "000000000002745f67a66617b1f9f65c2dfb4c4b8a9ed9b1320b98d3ca46cbe5", hash: '000000000002745f67a66617b1f9f65c2dfb4c4b8a9ed9b1320b98d3ca46cbe5',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266481, height: 1266481,
hash: "0000000000036babee3ba7654cddb29a9fe5e85c9fbeb44bb53d5ffb694b9670", hash: '0000000000036babee3ba7654cddb29a9fe5e85c9fbeb44bb53d5ffb694b9670',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266452, height: 1266452,
hash: "00000000502c3ab1490e0839780a4b441bbe21507c454545941e6adcde73c2ff", hash: '00000000502c3ab1490e0839780a4b441bbe21507c454545941e6adcde73c2ff',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1266336, height: 1266336,
hash: "000000001c9a0b7cd9eb2210f69d5a9a3f4546ba3707deb40993d729190582d7", hash: '000000001c9a0b7cd9eb2210f69d5a9a3f4546ba3707deb40993d729190582d7',
branchlen: 10, branchlen: 10,
status: "headers-only" status: 'headers-only'
}, },
{ {
height: 1266097, height: 1266097,
hash: "000000000028ac0fbd18afe6f001f70cb7549bd2b3082d754dde88ee236368a2", hash: '000000000028ac0fbd18afe6f001f70cb7549bd2b3082d754dde88ee236368a2',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1265522, height: 1265522,
hash: "00000000000000c6df7a2063030e6ce51399e470cc46e4bf6d67aa3f0feca98d", hash: '00000000000000c6df7a2063030e6ce51399e470cc46e4bf6d67aa3f0feca98d',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1265470, height: 1265470,
hash: "0000000000168e6442a595a02340fccf8dc2b0e8912c632d39e1f870685f6d7c", hash: '0000000000168e6442a595a02340fccf8dc2b0e8912c632d39e1f870685f6d7c',
branchlen: 2, branchlen: 2,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1265275, height: 1265275,
hash: "000000008bc2b37ae6af1b886b52e7e1c1122c12badc4ae6c13df64f789267cf", hash: '000000008bc2b37ae6af1b886b52e7e1c1122c12badc4ae6c13df64f789267cf',
branchlen: 11027, branchlen: 11027,
status: "headers-only" status: 'headers-only'
}, },
{ {
height: 1265257, height: 1265257,
hash: "00000000005bc3e9973d7273211eab02ae02549c7ebae48764c0bd648d4d7bbe", hash: '00000000005bc3e9973d7273211eab02ae02549c7ebae48764c0bd648d4d7bbe',
branchlen: 114, branchlen: 114,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1265254, height: 1265254,
hash: "0000000000e5b909d3541857315e9ee103b042d9ba661f25cc3e8fb25a96c8ce", hash: '0000000000e5b909d3541857315e9ee103b042d9ba661f25cc3e8fb25a96c8ce',
branchlen: 111, branchlen: 111,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1265245, height: 1265245,
hash: "000000000066eb8832f6f990baceb379536f437216e22475a6c1b9133d250cb8", hash: '000000000066eb8832f6f990baceb379536f437216e22475a6c1b9133d250cb8',
branchlen: 102, branchlen: 102,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1264455, height: 1264455,
hash: "00000000000001befc1bb17a23208d0b77b2449e37093ce8fbb5e346b19cafcc", hash: '00000000000001befc1bb17a23208d0b77b2449e37093ce8fbb5e346b19cafcc',
branchlen: 1, branchlen: 1,
status: "valid-headers" status: 'valid-headers'
}, },
{ {
height: 1264373, height: 1264373,
hash: "00000000000001ed3747e04fb95054cac0358fb4d8009d1999fc3ea34413e84a", hash: '00000000000001ed3747e04fb95054cac0358fb4d8009d1999fc3ea34413e84a',
branchlen: 175, branchlen: 175,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1263912, height: 1263912,
hash: "00000000000004635aecbcb97edcd6f5396483338838cb40da804eb354c68bde", hash: '00000000000004635aecbcb97edcd6f5396483338838cb40da804eb354c68bde',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1262906, height: 1262906,
hash: "00000000b31643d92a3e7c38e9755844fc8607e3f1055580a8ff28854e8a8ece", hash: '00000000b31643d92a3e7c38e9755844fc8607e3f1055580a8ff28854e8a8ece',
branchlen: 1, branchlen: 1,
status: "valid-fork" status: 'valid-fork'
}, },
{ {
height: 1255749, height: 1255749,
hash: "000000001dde5b015a99137f4cba87871370f4b6fcfbcc8b126547e6c33177da", hash: '000000001dde5b015a99137f4cba87871370f4b6fcfbcc8b126547e6c33177da',
branchlen: 129, branchlen: 129,
status: "headers-only" status: 'headers-only'
}, },
{ {
height: 1188789, height: 1188789,
hash: "00000000af942ce4eb60b3213cbcb7c98a7330f1f8f1adb4b6376f5e822e15b2", hash: '00000000af942ce4eb60b3213cbcb7c98a7330f1f8f1adb4b6376f5e822e15b2',
branchlen: 92, branchlen: 92,
status: "headers-only" status: 'headers-only'
} }
] ]
@@ -213,76 +213,76 @@ const mockMempoolInfo = {
} }
const mockRawMempool = [ const mockRawMempool = [
"db045bc3bd1088fa91f5ebb05c35cb9e2a91a22377f79b465cc6920b9893123c", 'db045bc3bd1088fa91f5ebb05c35cb9e2a91a22377f79b465cc6920b9893123c',
"6b3df7febf2b9834f1409155f88b866dd516b36376eae00e2b455df82e290405" '6b3df7febf2b9834f1409155f88b866dd516b36376eae00e2b455df82e290405'
] ]
const mockBlockHeaderConcise = const mockBlockHeaderConcise =
"000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c" '000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c'
const mockBlockHeader = { const mockBlockHeader = {
hash: "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900", hash: '00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900',
confirmations: 7, confirmations: 7,
height: 1272859, height: 1272859,
version: 2147418112, version: 2147418112,
versionHex: "7fff0000", versionHex: '7fff0000',
merkleroot: merkleroot:
"846f45604ec6cde2528933305dba80971e9ff0874f74d2811c7c690b2c32706d", '846f45604ec6cde2528933305dba80971e9ff0874f74d2811c7c690b2c32706d',
time: 1544207139, time: 1544207139,
mediantime: 1544202884, mediantime: 1544202884,
nonce: 641041470, nonce: 641041470,
bits: "1a09faea", bits: '1a09faea',
difficulty: 1681035.704111868, difficulty: 1681035.704111868,
chainwork: "00000000000000000000000000000000000000000000003fc4752e608403be04", chainwork: '00000000000000000000000000000000000000000000003fc4752e608403be04',
previousblockhash: previousblockhash:
"0000000000003e467ebcb7e906645676c2f71b9ac520d6508bea45789b7c217d", '0000000000003e467ebcb7e906645676c2f71b9ac520d6508bea45789b7c217d',
nextblockhash: nextblockhash:
"00000000000006899041cdfd6c0b73a97730c362346dde479b77414ad7f25ace" '00000000000006899041cdfd6c0b73a97730c362346dde479b77414ad7f25ace'
} }
const mockTxOut = { const mockTxOut = {
bestblock: "00000000003a7f19730dd9f172d7466658a5c8833dd03ed8f4ba36e3d857b5e7", bestblock: '00000000003a7f19730dd9f172d7466658a5c8833dd03ed8f4ba36e3d857b5e7',
confirmations: 10, confirmations: 10,
value: 0.0001, value: 0.0001,
scriptPubKey: { scriptPubKey: {
asm: asm:
"OP_DUP OP_HASH160 0ee020c07f39526ac5505c54fa1ab98490979b83 OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 0ee020c07f39526ac5505c54fa1ab98490979b83 OP_EQUALVERIFY OP_CHECKSIG',
hex: "76a9140ee020c07f39526ac5505c54fa1ab98490979b8388ac", hex: '76a9140ee020c07f39526ac5505c54fa1ab98490979b8388ac',
reqSigs: 1, reqSigs: 1,
type: "pubkeyhash", type: 'pubkeyhash',
addresses: ["bchtest:qq8wqgxq0uu4y6k92pw9f7s6hxzfp9umsvtg39pzqf"] addresses: ['bchtest:qq8wqgxq0uu4y6k92pw9f7s6hxzfp9umsvtg39pzqf']
}, },
coinbase: false coinbase: false
} }
const mockTxOutProof = const mockTxOutProof =
"000000200798039affceb7a381e15dc62443c286efe7cae852393b656807000000000000cf9a7d6c654fd1c4558229a619a550ff749b373fa0f0027eed68b1abf6175d5c07c50f5cffff001d0452403b34000000070d0765da76980d542b5670d27ccda9e717073270f8921ad017deb7ee83fe6c4c22830a48cc8392197f1742a3d81805bd2aa4eb7469f38c74f674429d682cb87efcff1713fd0c388a50f0d11adf05363397ef79d16cdb786d47c941c2cb89f2588f42aced9c05f4203b4440616a9e4266ec47909b9580a643f402b5d4e82cc3cddd73df05a33abf6af3975d973d3033d10c4168a73d58d01f685a96d5a2519cd0de9c77faf3f8725ddf155cbdc8ab8102f173e2d0a0d74767f3bff22f588158d6d5bc71c079c7659cc864819c43877635007650502eb4060fcc9feec3280a41d602ad0a" '000000200798039affceb7a381e15dc62443c286efe7cae852393b656807000000000000cf9a7d6c654fd1c4558229a619a550ff749b373fa0f0027eed68b1abf6175d5c07c50f5cffff001d0452403b34000000070d0765da76980d542b5670d27ccda9e717073270f8921ad017deb7ee83fe6c4c22830a48cc8392197f1742a3d81805bd2aa4eb7469f38c74f674429d682cb87efcff1713fd0c388a50f0d11adf05363397ef79d16cdb786d47c941c2cb89f2588f42aced9c05f4203b4440616a9e4266ec47909b9580a643f402b5d4e82cc3cddd73df05a33abf6af3975d973d3033d10c4168a73d58d01f685a96d5a2519cd0de9c77faf3f8725ddf155cbdc8ab8102f173e2d0a0d74767f3bff22f588158d6d5bc71c079c7659cc864819c43877635007650502eb4060fcc9feec3280a41d602ad0a'
const mockAncestors = [ const mockAncestors = [
"070504de968fba8a23a1362fd7fc0546a420663f2bcc18099d894a41ecf9cce5", '070504de968fba8a23a1362fd7fc0546a420663f2bcc18099d894a41ecf9cce5',
"20af6b7fcd21a3f38bb65d32fe2d66fb1ae554ed9c00eaf1b17d7e81362381a9", '20af6b7fcd21a3f38bb65d32fe2d66fb1ae554ed9c00eaf1b17d7e81362381a9',
"227e08663a812cf44d91106b09fc8493fe1a2b7c146f30ca09ebd86c9b84b2a5", '227e08663a812cf44d91106b09fc8493fe1a2b7c146f30ca09ebd86c9b84b2a5',
"228833be79d4a833165ea199be160efcaeb9325e71e9455f424b27abc9688ba8", '228833be79d4a833165ea199be160efcaeb9325e71e9455f424b27abc9688ba8',
"2de27df02001e7243435cef6d41ea2312b94a5964912eb21466b936852c0b822", '2de27df02001e7243435cef6d41ea2312b94a5964912eb21466b936852c0b822',
"2ef76ae690d6f6c9559e20358a75a0ce02250b9b8e80e0aa6f782576239f292a", '2ef76ae690d6f6c9559e20358a75a0ce02250b9b8e80e0aa6f782576239f292a',
"3e314678f52d52694726e6da88b94c9f0ea086d2f9af297b01d014b62247839f", '3e314678f52d52694726e6da88b94c9f0ea086d2f9af297b01d014b62247839f',
"4773f840d5255f16c0e5effce48316d8b8824a1d3ddd5da275c5fd99e7b3ee42", '4773f840d5255f16c0e5effce48316d8b8824a1d3ddd5da275c5fd99e7b3ee42',
"4804b97112da07e2c187516a915dd4cbd4a3814f3c900b8d08eb1d508ab0ac47", '4804b97112da07e2c187516a915dd4cbd4a3814f3c900b8d08eb1d508ab0ac47',
"4ca69f31081a256cb4711315336fe4358d8f04b67792ca5ee17b5f4089a2446a", '4ca69f31081a256cb4711315336fe4358d8f04b67792ca5ee17b5f4089a2446a',
"6ac17dce10ae3a3d1d8e6f42546ad9a4bb981e62cba8d48bab03e01b9ea38ae3", '6ac17dce10ae3a3d1d8e6f42546ad9a4bb981e62cba8d48bab03e01b9ea38ae3',
"6ce71182ef341c481a90d8b081a99d9788bdd91a917f9a6594fdfad2c2024a14", '6ce71182ef341c481a90d8b081a99d9788bdd91a917f9a6594fdfad2c2024a14',
"8413343ac973fb0968ae1809faa8f0ddf18252975481daa683e88b04afa66ccd", '8413343ac973fb0968ae1809faa8f0ddf18252975481daa683e88b04afa66ccd',
"9430300f3d7ecdabe39671b6fd0e9c36f763f01a202e619c070554ddc5e8713e", '9430300f3d7ecdabe39671b6fd0e9c36f763f01a202e619c070554ddc5e8713e',
"94f82b5a2cd1647d8168807ccfdebe41931ea1ab789a050d615f08000a63b624", '94f82b5a2cd1647d8168807ccfdebe41931ea1ab789a050d615f08000a63b624',
"9848db6a7d2835002ca7e1097a685d75eb629f44df94007674635b45d0e97199", '9848db6a7d2835002ca7e1097a685d75eb629f44df94007674635b45d0e97199',
"98d809993353df6c4d55fda5e7b0ed2e383601cef1180a9aa9c87e67e8f4dd94", '98d809993353df6c4d55fda5e7b0ed2e383601cef1180a9aa9c87e67e8f4dd94',
"9f5513a35ae8fad3dd061865a97d8127c914e3464527641c089993c06d092535", '9f5513a35ae8fad3dd061865a97d8127c914e3464527641c089993c06d092535',
"a7201ac90abd2aa42112faa809181c45047b6988a2f649a70a1d5bfb95f46884", 'a7201ac90abd2aa42112faa809181c45047b6988a2f649a70a1d5bfb95f46884',
"a9f1284710c655357412a062b01efaeb174820613784ccbfe44b33b86a8bdbc4", 'a9f1284710c655357412a062b01efaeb174820613784ccbfe44b33b86a8bdbc4',
"b814f44c9c8ee24a218b9f295a8c1831106b70c15ffe8aeb7534e10d99935fd0", 'b814f44c9c8ee24a218b9f295a8c1831106b70c15ffe8aeb7534e10d99935fd0',
"e130ccdcc94dc5eaa4ae37fa50e3ee96283b0e157616bc6e28454b64bae34f48", 'e130ccdcc94dc5eaa4ae37fa50e3ee96283b0e157616bc6e28454b64bae34f48',
"e68dad4a7292105cfa84fcaef7f99e5d4f2ece9613ca625d4d2ebf61efa84118", 'e68dad4a7292105cfa84fcaef7f99e5d4f2ece9613ca625d4d2ebf61efa84118',
"fe94caf5da672be3772d2304a6272eb8bc3d3f5cb4a886f39b7981e1485cf74b" 'fe94caf5da672be3772d2304a6272eb8bc3d3f5cb4a886f39b7981e1485cf74b'
] ]
module.exports = { module.exports = {
+5 -5
View File
@@ -2,7 +2,7 @@
This library contains mocking data for running unit tests on the address route. This library contains mocking data for running unit tests on the address route.
*/ */
"use strict" 'use strict'
const mockGetInfo = { const mockGetInfo = {
version: 170200, version: 170200,
@@ -12,21 +12,21 @@ const mockGetInfo = {
blocks: 1266726, blocks: 1266726,
timeoffset: 0, timeoffset: 0,
connections: 8, connections: 8,
proxy: "", proxy: '',
difficulty: 1, difficulty: 1,
testnet: true, testnet: true,
keypoololdest: 1536331195, keypoololdest: 1536331195,
keypoolsize: 2000, keypoolsize: 2000,
paytxfee: 0, paytxfee: 0,
relayfee: 0.00001, relayfee: 0.00001,
errors: "Warning: unknown new rules activated (versionbit 28)" errors: 'Warning: unknown new rules activated (versionbit 28)'
} }
const mockGetNetworkInfo = { const mockGetNetworkInfo = {
version: 190700, version: 190700,
subversion: "/Bitcoin ABC:0.19.7(EB32.0)/", subversion: '/Bitcoin ABC:0.19.7(EB32.0)/',
protocolversion: 70015, protocolversion: 70015,
localservices: "0000000000000425", localservices: '0000000000000425',
localrelay: true, localrelay: true,
timeoffset: 0, timeoffset: 0,
networkactive: true, networkactive: true,
+7 -8
View File
@@ -2,27 +2,27 @@
Contains mocks of Express req and res objects. Contains mocks of Express req and res objects.
*/ */
"use strict" 'use strict'
const sinon = require("sinon") const sinon = require('sinon')
// Inspect JS Objects. // Inspect JS Objects.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true colors: true
} }
// mock for res.send() // mock for res.send()
function fakeSend(arg) { function fakeSend (arg) {
//console.log(`res.send: ${util.inspect(arg)}`); // console.log(`res.send: ${util.inspect(arg)}`);
mockRes.output = arg mockRes.output = arg
return arg return arg
} }
// mock for res.json() // mock for res.json()
function fakeJson(arg) { function fakeJson (arg) {
//console.log(`res.json: ${util.inspect(arg)}`); // console.log(`res.json: ${util.inspect(arg)}`);
mockRes.output = arg mockRes.output = arg
return arg return arg
} }
@@ -55,7 +55,6 @@ const mockRes = {
cookie: sinon.stub().returns({}), cookie: sinon.stub().returns({}),
download: sinon.stub().returns({}), download: sinon.stub().returns({}),
end: sinon.stub().returns({}), end: sinon.stub().returns({}),
format: {},
get: sinon.stub().returns({}), get: sinon.stub().returns({}),
headersSent: sinon.stub().returns({}), headersSent: sinon.stub().returns({}),
json: sinon.stub().callsFake(fakeJson), json: sinon.stub().callsFake(fakeJson),
+2 -2
View File
@@ -2,7 +2,7 @@
This library contains mocking data for running unit tests on the address route. This library contains mocking data for running unit tests on the address route.
*/ */
"use strict" 'use strict'
const mockMiningInfo = { const mockMiningInfo = {
blocks: 1270185, blocks: 1270185,
@@ -14,7 +14,7 @@ const mockMiningInfo = {
"Warning: Unknown block versions being mined! It's possible unknown rules are in effect", "Warning: Unknown block versions being mined! It's possible unknown rules are in effect",
networkhashps: 517410290.9365583, networkhashps: 517410290.9365583,
pooledtx: 5, pooledtx: 5,
chain: "test" chain: 'test'
} }
module.exports = { module.exports = {
+43 -43
View File
@@ -2,23 +2,23 @@
This library contains mocking data for running unit tests. This library contains mocking data for running unit tests.
*/ */
"use strict" 'use strict'
const mockDecodeRawTransaction = { const mockDecodeRawTransaction = {
txid: "a332237d82a2543af1b0e1ae3c8cea1610c290ebcaf084a7e9894a61de0be988", txid: 'a332237d82a2543af1b0e1ae3c8cea1610c290ebcaf084a7e9894a61de0be988',
hash: "a332237d82a2543af1b0e1ae3c8cea1610c290ebcaf084a7e9894a61de0be988", hash: 'a332237d82a2543af1b0e1ae3c8cea1610c290ebcaf084a7e9894a61de0be988',
size: 226, size: 226,
version: 2, version: 2,
locktime: 0, locktime: 0,
vin: [ vin: [
{ {
txid: "21cced645eab150585ed7ca7c96edebab5793cc0a3b3b286c42fd7d6d798b5b9", txid: '21cced645eab150585ed7ca7c96edebab5793cc0a3b3b286c42fd7d6d798b5b9',
vout: 1, vout: 1,
scriptSig: { scriptSig: {
asm: asm:
"3045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd41390[ALL|FORKID] 0360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413d", '3045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd41390[ALL|FORKID] 0360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413d',
hex: hex:
"483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413d" '483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413d'
}, },
sequence: 4294967295 sequence: 4294967295
} }
@@ -29,10 +29,10 @@ const mockDecodeRawTransaction = {
n: 0, n: 0,
scriptPubKey: { scriptPubKey: {
asm: asm:
"OP_DUP OP_HASH160 eb4b180def88e3f5625b2d8ae2c098ff7d85f664 OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 eb4b180def88e3f5625b2d8ae2c098ff7d85f664 OP_EQUALVERIFY OP_CHECKSIG',
hex: "76a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac", hex: '76a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac',
reqSigs: 1, reqSigs: 1,
type: "pubkeyhash", type: 'pubkeyhash',
addresses: [Array] addresses: [Array]
} }
}, },
@@ -41,10 +41,10 @@ const mockDecodeRawTransaction = {
n: 1, n: 1,
scriptPubKey: { scriptPubKey: {
asm: asm:
"OP_DUP OP_HASH160 eb4b180def88e3f5625b2d8ae2c098ff7d85f664 OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 eb4b180def88e3f5625b2d8ae2c098ff7d85f664 OP_EQUALVERIFY OP_CHECKSIG',
hex: "76a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac", hex: '76a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac',
reqSigs: 1, reqSigs: 1,
type: "pubkeyhash", type: 'pubkeyhash',
addresses: [Array] addresses: [Array]
} }
} }
@@ -53,31 +53,31 @@ const mockDecodeRawTransaction = {
const mockDecodeScript = { const mockDecodeScript = {
asm: asm:
"0 0 -57 OP_NOP6 OP_LSHIFT OP_UNKNOWN OP_UNKNOWN OP_UNKNOWN c486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa OP_2OVER OP_NUMEQUALVERIFY OP_OR OP_INVERT OP_UNKNOWN OP_UNKNOWN 2ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab 67c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b [error]", '0 0 -57 OP_NOP6 OP_LSHIFT OP_UNKNOWN OP_UNKNOWN OP_UNKNOWN c486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa OP_2OVER OP_NUMEQUALVERIFY OP_OR OP_INVERT OP_UNKNOWN OP_UNKNOWN 2ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab 67c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b [error]',
type: "nonstandard", type: 'nonstandard',
p2sh: "bchtest:pzy6dwfy6yf373w0dr05a6flfqksurjhwcl3awhdvm" p2sh: 'bchtest:pzy6dwfy6yf373w0dr05a6flfqksurjhwcl3awhdvm'
} }
const mockRawTransactionConcise = const mockRawTransactionConcise =
"02000000014e6b52500110b1c30315b85805fb274f0f4afceffc1589f889b27709e59e987d000000006a473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02d1778f950a0000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac00000000" '02000000014e6b52500110b1c30315b85805fb274f0f4afceffc1589f889b27709e59e987d000000006a473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02d1778f950a0000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac00000000'
const mockRawTransactionVerbose = { const mockRawTransactionVerbose = {
hex: hex:
"02000000014e6b52500110b1c30315b85805fb274f0f4afceffc1589f889b27709e59e987d000000006a473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02d1778f950a0000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac00000000", '02000000014e6b52500110b1c30315b85805fb274f0f4afceffc1589f889b27709e59e987d000000006a473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02d1778f950a0000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac00000000',
txid: "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971", txid: 'bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971',
hash: "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971", hash: 'bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971',
size: 225, size: 225,
version: 2, version: 2,
locktime: 0, locktime: 0,
vin: [ vin: [
{ {
txid: "7d989ee50977b289f88915fceffc4a0f4f27fb0558b81503c3b1100150526b4e", txid: '7d989ee50977b289f88915fceffc4a0f4f27fb0558b81503c3b1100150526b4e',
vout: 0, vout: 0,
scriptSig: { scriptSig: {
asm: asm:
"3044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7[ALL|FORKID] 03c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792", '3044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7[ALL|FORKID] 03c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792',
hex: hex:
"473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792" '473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792'
}, },
sequence: 4294967295 sequence: 4294967295
} }
@@ -88,11 +88,11 @@ const mockRawTransactionVerbose = {
n: 0, n: 0,
scriptPubKey: { scriptPubKey: {
asm: asm:
"OP_DUP OP_HASH160 36d2f27bbd826a86db1e93618ce3de89ef331693 OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 36d2f27bbd826a86db1e93618ce3de89ef331693 OP_EQUALVERIFY OP_CHECKSIG',
hex: "76a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac", hex: '76a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac',
reqSigs: 1, reqSigs: 1,
type: "pubkeyhash", type: 'pubkeyhash',
addresses: ["bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35"] addresses: ['bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35']
} }
}, },
{ {
@@ -100,34 +100,34 @@ const mockRawTransactionVerbose = {
n: 1, n: 1,
scriptPubKey: { scriptPubKey: {
asm: asm:
"OP_DUP OP_HASH160 152ea3cd65f18cb8fa9146c84ea1a97af8f051de OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 152ea3cd65f18cb8fa9146c84ea1a97af8f051de OP_EQUALVERIFY OP_CHECKSIG',
hex: "76a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac", hex: '76a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac',
reqSigs: 1, reqSigs: 1,
type: "pubkeyhash", type: 'pubkeyhash',
addresses: ["bchtest:qq2jag7dvhccew86j9rvsn4p49a03uz3mcpw3d6aca"] addresses: ['bchtest:qq2jag7dvhccew86j9rvsn4p49a03uz3mcpw3d6aca']
} }
} }
], ],
blockhash: "000000000000026fa244de975ca89ea08008aa566564ce2e8ebb3144361b601b", blockhash: '000000000000026fa244de975ca89ea08008aa566564ce2e8ebb3144361b601b',
confirmations: 125, confirmations: 125,
time: 1542646373, time: 1542646373,
blocktime: 1542646373 blocktime: 1542646373
} }
const mockWHDecode = { const mockWHDecode = {
txid: "f8a9857fe3b8a288b5fcafb1b0fc196731f433add6d962f77acd7c10b970ff89", txid: 'f8a9857fe3b8a288b5fcafb1b0fc196731f433add6d962f77acd7c10b970ff89',
fee: "500", fee: '500',
sendingaddress: "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", sendingaddress: 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
referenceaddress: "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", referenceaddress: 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
ismine: false, ismine: false,
version: 0, version: 0,
type_int: 0, type_int: 0,
type: "Simple Send", type: 'Simple Send',
propertyid: 368, propertyid: 368,
precision: "8", precision: '8',
amount: "10.00000000", amount: '10.00000000',
valid: true, valid: true,
blockhash: "0000000046ba0bcef78caaa4492622176bbc563cf249ab52340a0449bc8e26f6", blockhash: '0000000046ba0bcef78caaa4492622176bbc563cf249ab52340a0449bc8e26f6',
blocktime: 1542814183, blocktime: 1542814183,
positioninblock: 57, positioninblock: 57,
block: 1269008, block: 1269008,
@@ -135,15 +135,15 @@ const mockWHDecode = {
} }
const mockWHCreateInput = { const mockWHCreateInput = {
txid: "f7ed9cf23dee85910f6269c9a101a75fcfd2f3c6fc81f17fad824ff7aaf99ab2", txid: 'f7ed9cf23dee85910f6269c9a101a75fcfd2f3c6fc81f17fad824ff7aaf99ab2',
vout: 1, vout: 1,
scriptPubKey: "76a914a4b98b0c118de83e7d39c834445f51dce62425f588ac", scriptPubKey: '76a914a4b98b0c118de83e7d39c834445f51dce62425f588ac',
amount: 0.09984138, amount: 0.09984138,
satoshis: 9984138, satoshis: 9984138,
height: 1269011, height: 1269011,
confirmations: 4, confirmations: 4,
legacyAddress: "mvXwPH74hW2yVTWwDwzsjGoaUAqJvWk7ZJ", legacyAddress: 'mvXwPH74hW2yVTWwDwzsjGoaUAqJvWk7ZJ',
cashAddress: "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", cashAddress: 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
value: 0.09984138 value: 0.09984138
} }
+99 -99
View File
@@ -2,27 +2,27 @@
This library contains mocking data for running unit tests. This library contains mocking data for running unit tests.
*/ */
"use strict" 'use strict'
const mockList = { const mockList = {
t: [ t: [
{ {
tokenDetails: { tokenDetails: {
tokenIdHex: tokenIdHex:
"df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb", 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
documentUri: "", documentUri: '',
documentSha256: "", documentSha256: '',
symbol: "NAKAMOTO", symbol: 'NAKAMOTO',
name: "NAKAMOTO", name: 'NAKAMOTO',
decimals: 8 decimals: 8
}, },
tokenStats: { tokenStats: {
qty_valid_txns_since_genesis: 241, qty_valid_txns_since_genesis: 241,
qty_valid_token_utxos: 151, qty_valid_token_utxos: 151,
qty_valid_token_addresses: 113, qty_valid_token_addresses: 113,
qty_token_circulating_supply: "20995990", qty_token_circulating_supply: '20995990',
qty_token_burned: "4010", qty_token_burned: '4010',
qty_token_minted: "21000000", qty_token_minted: '21000000',
qty_satoshis_locked_up: 81900 qty_satoshis_locked_up: 81900
} }
} }
@@ -35,18 +35,18 @@ const mockSingleToken = {
tokenDetails: { tokenDetails: {
decimals: 0, decimals: 0,
tokenIdHex: tokenIdHex:
"38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0", '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
timestamp: "2019-06-14 06:31:37", timestamp: '2019-06-14 06:31:37',
timestamp_unix: 1560493897, timestamp_unix: 1560493897,
transactionType: "GENESIS", transactionType: 'GENESIS',
versionType: 1, versionType: 1,
documentUri: "ot@ot.com", documentUri: 'ot@ot.com',
documentSha256Hex: null, documentSha256Hex: null,
symbol: "OT", symbol: 'OT',
name: "Test First SLP oasis Token", name: 'Test First SLP oasis Token',
batonVout: 2, batonVout: 2,
containsBaton: true, containsBaton: true,
genesisOrMintQuantity: "10000", genesisOrMintQuantity: '10000',
sendOutputs: null sendOutputs: null
}, },
tokenStats: { tokenStats: {
@@ -56,11 +56,11 @@ const mockSingleToken = {
qty_valid_txns_since_genesis: 11, qty_valid_txns_since_genesis: 11,
qty_valid_token_utxos: 9, qty_valid_token_utxos: 9,
qty_valid_token_addresses: 2, qty_valid_token_addresses: 2,
qty_token_minted: "20000", qty_token_minted: '20000',
qty_token_burned: "0", qty_token_burned: '0',
qty_token_circulating_supply: "20000", qty_token_circulating_supply: '20000',
qty_satoshis_locked_up: 4914, qty_satoshis_locked_up: 4914,
minting_baton_status: "ALIVE" minting_baton_status: 'ALIVE'
} }
} }
] ]
@@ -73,14 +73,14 @@ const mockSingleTokenError = {
const mockSingleAddress = { const mockSingleAddress = {
a: [ a: [
{ {
_id: "5c93ed62a19119333d1595bc", _id: '5c93ed62a19119333d1595bc',
tokenDetails: { tokenDetails: {
tokenIdHex: tokenIdHex:
"6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29" '6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29'
}, },
address: "slptest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqv7sq3kk7", address: 'slptest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqv7sq3kk7',
satoshis_balance: 546, satoshis_balance: 546,
token_balance: "4616984" token_balance: '4616984'
} }
], ],
t: [ t: [
@@ -88,17 +88,17 @@ const mockSingleAddress = {
tokenDetails: { tokenDetails: {
decimals: 8, decimals: 8,
tokenIdHex: tokenIdHex:
"6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29", '6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29',
timestamp: "2019-02-25 10:05", timestamp: '2019-02-25 10:05',
transactionType: "GENESIS", transactionType: 'GENESIS',
versionType: 1, versionType: 1,
documentUri: "https://developer.bitcoin.com", documentUri: 'https://developer.bitcoin.com',
documentSha256Hex: "", documentSha256Hex: '',
symbol: "DEV", symbol: 'DEV',
name: "DEVCOIN", name: 'DEVCOIN',
batonVout: 2, batonVout: 2,
containsBaton: true, containsBaton: true,
genesisOrMintQuantity: "500000000", genesisOrMintQuantity: '500000000',
sendOutputs: null sendOutputs: null
}, },
tokenStats: { tokenStats: {
@@ -108,125 +108,125 @@ const mockSingleAddress = {
qty_valid_txns_since_genesis: 172, qty_valid_txns_since_genesis: 172,
qty_valid_token_utxos: 170, qty_valid_token_utxos: 170,
qty_valid_token_addresses: 170, qty_valid_token_addresses: 170,
qty_token_minted: "1000000000", qty_token_minted: '1000000000',
qty_token_burned: "4.99999995", qty_token_burned: '4.99999995',
qty_token_circulating_supply: "999999995.00000005", qty_token_circulating_supply: '999999995.00000005',
qty_satoshis_locked_up: 92820, qty_satoshis_locked_up: 92820,
minting_baton_status: "ALIVE" minting_baton_status: 'ALIVE'
} }
} }
] ]
} }
const mockTx = { const mockTx = {
txid: "57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457822d446", txid: '57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457822d446',
version: 2, version: 2,
locktime: 0, locktime: 0,
vin: [ vin: [
{ {
txid: "61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49", txid: '61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49',
vout: 2, vout: 2,
sequence: 4294967295, sequence: 4294967295,
n: 0, n: 0,
scriptSig: { scriptSig: {
hex: hex:
"4730440220409e79fec552f01203f41d3d621ae3db89c720af261c8268ce5f0453de009f5d022001e7ffefeba7b0716d32ea55cb6ace267b6ee9cbcc8a017bb9c3b6acf7889418412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585", '4730440220409e79fec552f01203f41d3d621ae3db89c720af261c8268ce5f0453de009f5d022001e7ffefeba7b0716d32ea55cb6ace267b6ee9cbcc8a017bb9c3b6acf7889418412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585',
asm: asm:
"30440220409e79fec552f01203f41d3d621ae3db89c720af261c8268ce5f0453de009f5d022001e7ffefeba7b0716d32ea55cb6ace267b6ee9cbcc8a017bb9c3b6acf7889418[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585" '30440220409e79fec552f01203f41d3d621ae3db89c720af261c8268ce5f0453de009f5d022001e7ffefeba7b0716d32ea55cb6ace267b6ee9cbcc8a017bb9c3b6acf7889418[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585'
}, },
value: 546, value: 546,
legacyAddress: "mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL", legacyAddress: 'mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL',
cashAddress: "bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09" cashAddress: 'bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09'
}, },
{ {
txid: "61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49", txid: '61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49',
vout: 1, vout: 1,
sequence: 4294967295, sequence: 4294967295,
n: 1, n: 1,
scriptSig: { scriptSig: {
hex: hex:
"483045022100a743bee56c99bd103be48a78fa4c7342100815d9d2448dbe6e1d338c3a13b241022066728b5279fc22eef5cd019582ff34771e29175835fc98aa3168a1548fd78ac8412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585", '483045022100a743bee56c99bd103be48a78fa4c7342100815d9d2448dbe6e1d338c3a13b241022066728b5279fc22eef5cd019582ff34771e29175835fc98aa3168a1548fd78ac8412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585',
asm: asm:
"3045022100a743bee56c99bd103be48a78fa4c7342100815d9d2448dbe6e1d338c3a13b241022066728b5279fc22eef5cd019582ff34771e29175835fc98aa3168a1548fd78ac8[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585" '3045022100a743bee56c99bd103be48a78fa4c7342100815d9d2448dbe6e1d338c3a13b241022066728b5279fc22eef5cd019582ff34771e29175835fc98aa3168a1548fd78ac8[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585'
}, },
value: 546, value: 546,
legacyAddress: "mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL", legacyAddress: 'mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL',
cashAddress: "bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09" cashAddress: 'bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09'
}, },
{ {
txid: "61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49", txid: '61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49',
vout: 3, vout: 3,
sequence: 4294967295, sequence: 4294967295,
n: 2, n: 2,
scriptSig: { scriptSig: {
hex: hex:
"483045022100821473902eec5f1ce7d43b1ba7f9ec453bfe8b8dfc3de3e0723c883ab109922f02206162960e80618531fab2c16aee260fddd7979bab62471c8686af7de75f8732ec412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585", '483045022100821473902eec5f1ce7d43b1ba7f9ec453bfe8b8dfc3de3e0723c883ab109922f02206162960e80618531fab2c16aee260fddd7979bab62471c8686af7de75f8732ec412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585',
asm: asm:
"3045022100821473902eec5f1ce7d43b1ba7f9ec453bfe8b8dfc3de3e0723c883ab109922f02206162960e80618531fab2c16aee260fddd7979bab62471c8686af7de75f8732ec[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585" '3045022100821473902eec5f1ce7d43b1ba7f9ec453bfe8b8dfc3de3e0723c883ab109922f02206162960e80618531fab2c16aee260fddd7979bab62471c8686af7de75f8732ec[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585'
}, },
value: 9997521, value: 9997521,
legacyAddress: "mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL", legacyAddress: 'mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL',
cashAddress: "bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09" cashAddress: 'bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09'
} }
], ],
vout: [ vout: [
{ {
value: "0.00000000", value: '0.00000000',
n: 0, n: 0,
scriptPubKey: { scriptPubKey: {
hex: hex:
"6a04534c500001010453454e44207ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796080000000049504f80080000001c71e64280", '6a04534c500001010453454e44207ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796080000000049504f80080000001c71e64280',
asm: asm:
"OP_RETURN 5262419 1 1145980243 7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796 0000000049504f80 0000001c71e64280" 'OP_RETURN 5262419 1 1145980243 7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796 0000000049504f80 0000001c71e64280'
}, },
spentTxId: null, spentTxId: null,
spentIndex: null, spentIndex: null,
spentHeight: null spentHeight: null
}, },
{ {
value: "0.00000546", value: '0.00000546',
n: 1, n: 1,
scriptPubKey: { scriptPubKey: {
hex: "76a914396b8e57ad0cb58d30e2992f22047b3c20377aa688ac", hex: '76a914396b8e57ad0cb58d30e2992f22047b3c20377aa688ac',
asm: asm:
"OP_DUP OP_HASH160 396b8e57ad0cb58d30e2992f22047b3c20377aa6 OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 396b8e57ad0cb58d30e2992f22047b3c20377aa6 OP_EQUALVERIFY OP_CHECKSIG',
addresses: ["mkkZf7T3fU3vHSzNPy51HBmM46ghN1gnN9"], addresses: ['mkkZf7T3fU3vHSzNPy51HBmM46ghN1gnN9'],
type: "pubkeyhash" type: 'pubkeyhash'
}, },
spentTxId: null, spentTxId: null,
spentIndex: null, spentIndex: null,
spentHeight: null spentHeight: null
}, },
{ {
value: "0.00000546", value: '0.00000546',
n: 2, n: 2,
scriptPubKey: { scriptPubKey: {
hex: "76a914aa099b067337dc20993bbfdb5c4f3c6867ba2c5b88ac", hex: '76a914aa099b067337dc20993bbfdb5c4f3c6867ba2c5b88ac',
asm: asm:
"OP_DUP OP_HASH160 aa099b067337dc20993bbfdb5c4f3c6867ba2c5b OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 aa099b067337dc20993bbfdb5c4f3c6867ba2c5b OP_EQUALVERIFY OP_CHECKSIG',
addresses: ["mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL"], addresses: ['mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL'],
type: "pubkeyhash" type: 'pubkeyhash'
}, },
spentTxId: null, spentTxId: null,
spentIndex: null, spentIndex: null,
spentHeight: null spentHeight: null
}, },
{ {
value: "0.09996891", value: '0.09996891',
n: 3, n: 3,
scriptPubKey: { scriptPubKey: {
hex: "76a914aa099b067337dc20993bbfdb5c4f3c6867ba2c5b88ac", hex: '76a914aa099b067337dc20993bbfdb5c4f3c6867ba2c5b88ac',
asm: asm:
"OP_DUP OP_HASH160 aa099b067337dc20993bbfdb5c4f3c6867ba2c5b OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 aa099b067337dc20993bbfdb5c4f3c6867ba2c5b OP_EQUALVERIFY OP_CHECKSIG',
addresses: ["mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL"], addresses: ['mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL'],
type: "pubkeyhash" type: 'pubkeyhash'
}, },
spentTxId: null, spentTxId: null,
spentIndex: null, spentIndex: null,
spentHeight: null spentHeight: null
} }
], ],
blockhash: "000000000000ce978accc64a6bb567acf0c653c202309a0f8e220149bf0c6968", blockhash: '000000000000ce978accc64a6bb567acf0c653c202309a0f8e220149bf0c6968',
blockheight: 1287490, blockheight: 1287490,
confirmations: 746, confirmations: 746,
time: 1550855104, time: 1550855104,
@@ -237,28 +237,28 @@ const mockTx = {
fees: 0.0000063, fees: 0.0000063,
tokenInfo: { tokenInfo: {
versionType: 1, versionType: 1,
transactionType: "SEND", transactionType: 'SEND',
tokenIdHex: tokenIdHex:
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796", '7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796',
sendOutputs: ["0", "1230000000", "122170000000"] sendOutputs: ['0', '1230000000', '122170000000']
}, },
tokenIsValid: true tokenIsValid: true
} }
const mockConvert = { const mockConvert = {
slpAddress: "slptest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2shlcycvd5", slpAddress: 'slptest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2shlcycvd5',
cashAddress: "bchtest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2svtllzmlf", cashAddress: 'bchtest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2svtllzmlf',
legacyAddress: "mvQPGnzRT6gMWASZBMg7NcT3vmvsSKSQtf" legacyAddress: 'mvQPGnzRT6gMWASZBMg7NcT3vmvsSKSQtf'
} }
const mockTokenDetails = { const mockTokenDetails = {
tokenIdHex: tokenIdHex:
"df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb", 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
documentUri: "", documentUri: '',
symbol: "NAKAMOTO", symbol: 'NAKAMOTO',
name: "NAKAMOTO", name: 'NAKAMOTO',
decimals: 8, decimals: 8,
timestamp: "", timestamp: '',
containsBaton: true, containsBaton: true,
versionType: 1 versionType: 1
} }
@@ -267,29 +267,29 @@ const mockTokenStats = {
qty_valid_txns_since_genesis: 241, qty_valid_txns_since_genesis: 241,
qty_valid_token_utxos: 151, qty_valid_token_utxos: 151,
qty_valid_token_addresses: 113, qty_valid_token_addresses: 113,
qty_token_circulating_supply: "20995990", qty_token_circulating_supply: '20995990',
qty_token_burned: "4010", qty_token_burned: '4010',
qty_token_minted: "21000000", qty_token_minted: '21000000',
qty_satoshis_locked_up: 81900 qty_satoshis_locked_up: 81900
} }
const mockBalance = { const mockBalance = {
slpAddress: "simpleledger:qp9d8mn8ypryfvea2mev0ggc3wg6plpn4suuaeuss3", slpAddress: 'simpleledger:qp9d8mn8ypryfvea2mev0ggc3wg6plpn4suuaeuss3',
satoshis_balance: 546, satoshis_balance: 546,
token_balance: "1000" token_balance: '1000'
} }
const mockTransactions = [ const mockTransactions = [
{ {
txid: "a302f045be8efa1cd982833a7f187ff4fac8baac36da0c887eb2787d8b45e2af", txid: 'a302f045be8efa1cd982833a7f187ff4fac8baac36da0c887eb2787d8b45e2af',
tokenDetails: { tokenDetails: {
valid: true, valid: true,
detail: { detail: {
decimals: null, decimals: null,
tokenIdHex: tokenIdHex:
"495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a", '495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a',
timestamp: null, timestamp: null,
transactionType: "MINT", transactionType: 'MINT',
versionType: 1, versionType: 1,
documentUri: null, documentUri: null,
documentSha256Hex: null, documentSha256Hex: null,
@@ -298,7 +298,7 @@ const mockTransactions = [
batonVout: 2, batonVout: 2,
containsBaton: true, containsBaton: true,
genesisOrMintQuantity: { genesisOrMintQuantity: {
$numberDecimal: "1000" $numberDecimal: '1000'
}, },
sendOutputs: null sendOutputs: null
}, },
@@ -316,9 +316,9 @@ const mockFoobar = {
const mockSingleValidTxid = { const mockSingleValidTxid = {
c: [ c: [
{ {
_id: "5d965fc27f1cf2184ca2fe73", _id: '5d965fc27f1cf2184ca2fe73',
tx: { tx: {
h: "77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d" h: '77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d'
}, },
slp: { slp: {
valid: true, valid: true,
@@ -332,9 +332,9 @@ const mockSingleValidTxid = {
const mockTwoValidTxid = { const mockTwoValidTxid = {
c: [ c: [
{ {
_id: "5d965fc27f1cf2184ca2fe73", _id: '5d965fc27f1cf2184ca2fe73',
tx: { tx: {
h: "77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d" h: '77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d'
}, },
slp: { slp: {
valid: true, valid: true,
@@ -342,9 +342,9 @@ const mockTwoValidTxid = {
} }
}, },
{ {
_id: "5d71e69758380a002c492a90", _id: '5d71e69758380a002c492a90',
tx: { tx: {
h: "552112f9e458dc7d1d8b328b0a6685e8af74a64b60b6846e7c86407f27f47e42" h: '552112f9e458dc7d1d8b328b0a6685e8af74a64b60b6846e7c86407f27f47e42'
}, },
slp: { slp: {
valid: true, valid: true,
@@ -358,9 +358,9 @@ const mockTwoValidTxid = {
const mockTwoRedundentTxid = { const mockTwoRedundentTxid = {
c: [ c: [
{ {
_id: "5db99c72a391ae2afd604bde", _id: '5db99c72a391ae2afd604bde',
tx: { tx: {
h: "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56" h: 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56'
}, },
slp: { slp: {
valid: true, valid: true,
+15 -15
View File
@@ -2,18 +2,18 @@
Mocks used for unit tests that interact with slpjs. Mocks used for unit tests that interact with slpjs.
*/ */
"use strict" 'use strict'
const sinon = require("sinon") // const sinon = require('sinon')
const proxyquire = require("proxyquire") // const proxyquire = require('proxyquire')
const BigNumber = require("bignumber.js") const BigNumber = require('bignumber.js')
const slpMocks = require("./slp-mocks") const slpMocks = require('./slp-mocks')
// Mock the BitboxNetwork class. // Mock the BitboxNetwork class.
class BitboxNetwork { class BitboxNetwork {
constructor() {} // constructor () {}
async getAllSlpBalancesAndUtxos(address) { async getAllSlpBalancesAndUtxos (address) {
return { return {
satoshis_available_bch: 9996891, satoshis_available_bch: 9996891,
satoshis_in_slp_baton: 546, satoshis_in_slp_baton: 546,
@@ -21,15 +21,15 @@ class BitboxNetwork {
satoshis_in_invalid_token_dag: 0, satoshis_in_invalid_token_dag: 0,
satoshis_in_invalid_baton_dag: 0, satoshis_in_invalid_baton_dag: 0,
slpTokenBalances: { slpTokenBalances: {
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796": new BigNumber( '7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796': new BigNumber(
123400000000 123400000000
) )
}, },
slpTokenUtxos: { slpTokenUtxos: {
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796": [] '7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796': []
}, },
slpBatonUtxos: { slpBatonUtxos: {
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796": [] '7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796': []
}, },
nonSlpUtxos: [{}], nonSlpUtxos: [{}],
invalidTokenUtxos: [], invalidTokenUtxos: [],
@@ -37,15 +37,15 @@ class BitboxNetwork {
} }
} }
async getTokenInformation(txid) { async getTokenInformation (txid) {
BigNumber.set({ DECIMAL_PLACES: 8, ROUNDING_MODE: 4 }) BigNumber.set({ DECIMAL_PLACES: 8, ROUNDING_MODE: 4 })
const obj = { const obj = {
versionType: 1, versionType: 1,
transactionType: 0, transactionType: 0,
symbol: "SLPSDK", symbol: 'SLPSDK',
name: "SLP SDK example using BITBOX", name: 'SLP SDK example using BITBOX',
documentUri: "developer.bitcoin.com", documentUri: 'developer.bitcoin.com',
documentSha256: null, documentSha256: null,
decimals: 8, decimals: 8,
batonVout: 2, batonVout: 2,
@@ -56,7 +56,7 @@ class BitboxNetwork {
return obj return obj
} }
async getTransactionDetails(txid) { async getTransactionDetails (txid) {
return slpMocks.mockTx return slpMocks.mockTx
} }
} }
+7 -7
View File
@@ -2,7 +2,7 @@
This library contains mocking data for running unit tests on the address route. This library contains mocking data for running unit tests on the address route.
*/ */
"use strict" 'use strict'
/* /*
const mockDetails = { const mockDetails = {
@@ -24,17 +24,17 @@ const mockDetails = {
*/ */
const mockDetails = { const mockDetails = {
txid: "6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40", txid: '6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40',
version: 2, version: 2,
locktime: 0, locktime: 0,
vin: [ vin: [
{ {
txid: "273d616d1c48f4b075c497f36ffdc79da5c8d6ed75485808b3599aac504f8525", txid: '273d616d1c48f4b075c497f36ffdc79da5c8d6ed75485808b3599aac504f8525',
vout: 0, vout: 0,
sequence: 4294967295, sequence: 4294967295,
n: 0, n: 0,
scriptSig: [{}], scriptSig: [{}],
addr: "bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35", addr: 'bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35',
valueSat: 45078867559, valueSat: 45078867559,
value: 450.78867559, value: 450.78867559,
doubleSpentTxID: null doubleSpentTxID: null
@@ -42,7 +42,7 @@ const mockDetails = {
], ],
vout: [ vout: [
{ {
value: "450.68867333", value: '450.68867333',
n: 0, n: 0,
scriptPubKey: [{}], scriptPubKey: [{}],
spentTxId: null, spentTxId: null,
@@ -50,7 +50,7 @@ const mockDetails = {
spentHeight: null spentHeight: null
}, },
{ {
value: "0.10000000", value: '0.10000000',
n: 1, n: 1,
scriptPubKey: [{}], scriptPubKey: [{}],
spentTxId: null, spentTxId: null,
@@ -58,7 +58,7 @@ const mockDetails = {
spentHeight: null spentHeight: null
} }
], ],
blockhash: "00000000e7232ff12462dedf9c11985f5b54202515277c337ccc59812758f28b", blockhash: '00000000e7232ff12462dedf9c11985f5b54202515277c337ccc59812758f28b',
blockheight: 1270188, blockheight: 1270188,
confirmations: 3, confirmations: 3,
time: 1543436253, time: 1543436253,
+38 -38
View File
@@ -2,12 +2,12 @@
This library contains mocking data for running unit tests on the address route. This library contains mocking data for running unit tests on the address route.
*/ */
"use strict" 'use strict'
const mockAddress = { const mockAddress = {
isvalid: true, isvalid: true,
address: "bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y", address: 'bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y',
scriptPubKey: "76a914016a935f87e8deeab04501889b22d87e4e52db0988ac", scriptPubKey: '76a914016a935f87e8deeab04501889b22d87e4e52db0988ac',
ismine: false, ismine: false,
iswatchonly: false, iswatchonly: false,
isscript: false isscript: false
@@ -17,32 +17,32 @@ const mockBalance = {
page: 1, page: 1,
totalPages: 1, totalPages: 1,
itemsOnPage: 1000, itemsOnPage: 1000,
address: "bitcoincash:qzp7gdl52edm24xlpkyqnza9rv33u3mdxyc77j3u6k", address: 'bitcoincash:qzp7gdl52edm24xlpkyqnza9rv33u3mdxyc77j3u6k',
balance: "2546", balance: '2546',
totalReceived: "2546", totalReceived: '2546',
totalSent: "0", totalSent: '0',
unconfirmedBalance: "0", unconfirmedBalance: '0',
unconfirmedTxs: 0, unconfirmedTxs: 0,
txs: 2, txs: 2,
txids: [ txids: [
"e190d13b88578132608ab912a4d2be3e55aa2792d6042d481ae21d700639de56", 'e190d13b88578132608ab912a4d2be3e55aa2792d6042d481ae21d700639de56',
"44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6" '44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6'
] ]
} }
const mockUtxos = [ const mockUtxos = [
{ {
txid: "e190d13b88578132608ab912a4d2be3e55aa2792d6042d481ae21d700639de56", txid: 'e190d13b88578132608ab912a4d2be3e55aa2792d6042d481ae21d700639de56',
vout: 0, vout: 0,
value: "2000", value: '2000',
height: 605873, height: 605873,
confirmations: 298, confirmations: 298,
satoshis: 2000 satoshis: 2000
}, },
{ {
txid: "44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6", txid: '44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6',
vout: 1, vout: 1,
value: "546", value: '546',
height: 605873, height: 605873,
confirmations: 298, confirmations: 298,
satoshis: 546 satoshis: 546
@@ -51,25 +51,25 @@ const mockUtxos = [
const mockThreeUtxos = [ const mockThreeUtxos = [
{ {
txid: "e190d13b88578132608ab912a4d2be3e55aa2792d6042d481ae21d700639de56", txid: 'e190d13b88578132608ab912a4d2be3e55aa2792d6042d481ae21d700639de56',
vout: 0, vout: 0,
value: "2000", value: '2000',
height: 605873, height: 605873,
confirmations: 298, confirmations: 298,
satoshis: 2000 satoshis: 2000
}, },
{ {
txid: "44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6", txid: '44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6',
vout: 1, vout: 1,
value: "546", value: '546',
height: 605873, height: 605873,
confirmations: 298, confirmations: 298,
satoshis: 546 satoshis: 546
}, },
{ {
txid: "44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6", txid: '44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6',
vout: 1, vout: 1,
value: "546", value: '546',
height: 605873, height: 605873,
confirmations: 298, confirmations: 298,
satoshis: 546 satoshis: 546
@@ -79,38 +79,38 @@ const mockThreeUtxos = [
const mockIsTokenUtxos = [ const mockIsTokenUtxos = [
false, false,
{ {
txid: "44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6", txid: '44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6',
vout: 1, vout: 1,
value: "546", value: '546',
height: 605873, height: 605873,
confirmations: 298, confirmations: 298,
satoshis: 546, satoshis: 546,
utxoType: "token", utxoType: 'token',
transactionType: "send", transactionType: 'send',
tokenId: "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7", tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
tokenTicker: "TOK-CH", tokenTicker: 'TOK-CH',
tokenName: "TokyoCash", tokenName: 'TokyoCash',
tokenDocumentUrl: "", tokenDocumentUrl: '',
tokenDocumentHash: "", tokenDocumentHash: '',
decimals: 8, decimals: 8,
tokenQty: 2 tokenQty: 2
} }
] ]
const tapUtxo = { const tapUtxo = {
txid: "2e030df12390186baf817fa2760540b886511e04bc520e88f6b4c2124cc2a7d4", txid: '2e030df12390186baf817fa2760540b886511e04bc520e88f6b4c2124cc2a7d4',
vout: 1, vout: 1,
value: "546", value: '546',
height: 606564, height: 606564,
confirmations: 3, confirmations: 3,
satoshis: 546, satoshis: 546,
utxoType: "token", utxoType: 'token',
transactionType: "send", transactionType: 'send',
tokenId: "dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d", tokenId: 'dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d',
tokenTicker: "TAP", tokenTicker: 'TAP',
tokenName: "Thoughts and Prayers", tokenName: 'Thoughts and Prayers',
tokenDocumentUrl: "", tokenDocumentUrl: '',
tokenDocumentHash: "", tokenDocumentHash: '',
decimals: 0, decimals: 0,
tokenQty: 1 tokenQty: 1
} }
+222 -223
View File
@@ -1,45 +1,44 @@
"use strict" 'use strict'
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const nock = require("nock") // HTTP mocking // const nock = require('nock') // HTTP mocking
const sinon = require("sinon") const sinon = require('sinon')
// Used for debugging. // Used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
// Mocking data. // Mocking data.
const { mockReq, mockRes, mockNext } = require("./mocks/express-mocks") const { mockReq, mockRes, mockNext } = require('./mocks/express-mocks')
// Libraries under test // Libraries under test
const RateLimits = require("../../src/middleware/route-ratelimit") const RateLimits = require('../../src/middleware/route-ratelimit')
let rateLimits = new RateLimits() let rateLimits = new RateLimits()
let rateLimitMiddleware = rateLimits.routeRateLimit let rateLimitMiddleware = rateLimits.routeRateLimit
const controlRoute = require("../../src/routes/v3/full-node/control") // const controlRoute = require('../../src/routes/v3/full-node/control')
const jwtAuth = require("../../src/middleware/jwt-auth") const jwtAuth = require('../../src/middleware/jwt-auth')
let req, res, next let req, res, next
let originalEnvVars // Used during transition from integration to unit tests. // let originalEnvVars // Used during transition from integration to unit tests.
// JWT token used in tests. // JWT token used in tests.
const jwt = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWRlM2Y1NzM5ZTZjMGZmMDM0YjlhMSIsImlhdCI6MTU3MTY3NzQ1MCwiZXhwIjoxNTc0MjY5NDUwfQ.SSz7F7ETyBB3eoNG2VKCzPOhddtB-vrtmEoj7PxicrQ` const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWRlM2Y1NzM5ZTZjMGZmMDM0YjlhMSIsImlhdCI6MTU3MTY3NzQ1MCwiZXhwIjoxNTc0MjY5NDUwfQ.SSz7F7ETyBB3eoNG2VKCzPOhddtB-vrtmEoj7PxicrQ'
describe("#route-ratelimits & jwt-auth", () => { describe('#route-ratelimits & jwt-auth', () => {
let sandbox let sandbox
before(() => { before(() => {
// Save existing environment variables. // Save existing environment variables.
originalEnvVars = { // originalEnvVars = {
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, // BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
RPC_BASEURL: process.env.RPC_BASEURL, // RPC_BASEURL: process.env.RPC_BASEURL,
RPC_USERNAME: process.env.RPC_USERNAME, // RPC_USERNAME: process.env.RPC_USERNAME,
RPC_PASSWORD: process.env.RPC_PASSWORD // RPC_PASSWORD: process.env.RPC_PASSWORD
} // }
if (!process.env.JWT_AUTH_SERVER) if (!process.env.JWT_AUTH_SERVER) { process.env.JWT_AUTH_SERVER = 'http://fakeurl.com/' }
process.env.JWT_AUTH_SERVER = `http://fakeurl.com/`
}) })
// Setup the mocks before each test. // Setup the mocks before each test.
@@ -66,9 +65,9 @@ describe("#route-ratelimits & jwt-auth", () => {
rateLimits.closeRedis() rateLimits.closeRedis()
}) })
describe("#jwt-auth.js", () => { describe('#jwt-auth.js', () => {
describe("#getTokenFromHeaders", () => { describe('#getTokenFromHeaders', () => {
it(`should populate the req.locals object correctly`, () => { it('should populate the req.locals object correctly', () => {
// Initialize req.locals // Initialize req.locals
req.locals = { req.locals = {
proLimit: false, proLimit: false,
@@ -82,23 +81,23 @@ describe("#route-ratelimits & jwt-auth", () => {
// console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) // console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`)
assert.property(req.locals, "proLimit") assert.property(req.locals, 'proLimit')
assert.property(req.locals, "apiLevel") assert.property(req.locals, 'apiLevel')
assert.property(req.locals, "jwtToken") assert.property(req.locals, 'jwtToken')
assert.equal(req.locals.jwtToken, jwt) assert.equal(req.locals.jwtToken, jwt)
}) })
}) })
}) })
describe("#routeRateLimit", () => { describe('#routeRateLimit', () => {
rateLimitMiddleware = new RateLimits() rateLimitMiddleware = new RateLimits()
let routeRateLimit = rateLimitMiddleware.routeRateLimit let routeRateLimit = rateLimitMiddleware.routeRateLimit
const getInfo = controlRoute.testableComponents.getInfo // const getInfo = controlRoute.testableComponents.getInfo
it("should pass through rate-limit middleware", async () => { it('should pass through rate-limit middleware', async () => {
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.method = "GET" req.method = 'GET'
// Call the route twice to trigger the rate handling. // Call the route twice to trigger the rate handling.
await routeRateLimit(req, res, next) await routeRateLimit(req, res, next)
@@ -108,27 +107,27 @@ describe("#route-ratelimits & jwt-auth", () => {
assert.equal(next.called, true) assert.equal(next.called, true)
}) })
it("should trigger rate-limit handler if rate limits exceeds 5 request per minute", async () => { it('should trigger rate-limit handler if rate limits exceeds 5 request per minute', async () => {
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.method = "GET" req.method = 'GET'
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next) await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
false, false,
`next should not be called if rate limit was triggered.` 'next should not be called if rate limit was triggered.'
) )
}) })
it("should NOT trigger rate-limit for free-tier at 5 RPM", async () => { it('should NOT trigger rate-limit for free-tier at 5 RPM', async () => {
// Clear the require cache before running this test. // Clear the require cache before running this test.
// delete require.cache[ // delete require.cache[
// require.resolve("../../src/middleware/route-ratelimit") // require.resolve("../../src/middleware/route-ratelimit")
@@ -137,9 +136,9 @@ describe("#route-ratelimits & jwt-auth", () => {
rateLimitMiddleware = new RateLimits() rateLimitMiddleware = new RateLimits()
routeRateLimit = rateLimitMiddleware.routeRateLimit routeRateLimit = rateLimitMiddleware.routeRateLimit
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.method = "GET" req.method = 'GET'
req.locals.proLimit = true req.locals.proLimit = true
req.locals.apiLevel = 0 req.locals.apiLevel = 0
@@ -148,23 +147,23 @@ describe("#route-ratelimits & jwt-auth", () => {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next) await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
//console.log(`req.locals after test: ${util.inspect(req.locals)}`) // console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
true, true,
`next should be called if rate limit was not triggered.` 'next should be called if rate limit was not triggered.'
) )
}) })
it("should trigger rate-limit for free tier after 10 RPM", async () => { it('should trigger rate-limit for free tier after 10 RPM', async () => {
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.method = "GET" req.method = 'GET'
req.locals.proLimit = true req.locals.proLimit = true
req.locals.apiLevel = 0 req.locals.apiLevel = 0
@@ -173,18 +172,18 @@ describe("#route-ratelimits & jwt-auth", () => {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next) await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
false, false,
`next should not be called if rate limit was triggered.` 'next should not be called if rate limit was triggered.'
) )
}) })
it("should NOT trigger rate-limit handler for pro-tier at 25 RPM", async () => { it('should NOT trigger rate-limit handler for pro-tier at 25 RPM', async () => {
// Clear the require cache before running this test. // Clear the require cache before running this test.
// delete require.cache[ // delete require.cache[
// require.resolve("../../src/middleware/route-ratelimit") // require.resolve("../../src/middleware/route-ratelimit")
@@ -193,9 +192,9 @@ describe("#route-ratelimits & jwt-auth", () => {
rateLimitMiddleware = new RateLimits() rateLimitMiddleware = new RateLimits()
routeRateLimit = rateLimitMiddleware.routeRateLimit routeRateLimit = rateLimitMiddleware.routeRateLimit
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.method = "GET" req.method = 'GET'
req.locals.proLimit = true req.locals.proLimit = true
req.locals.apiLevel = 10 req.locals.apiLevel = 10
@@ -204,20 +203,20 @@ describe("#route-ratelimits & jwt-auth", () => {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next) await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
//console.log(`req.locals after test: ${util.inspect(req.locals)}`) // console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
true, true,
`next should be called if rate limit was not triggered.` 'next should be called if rate limit was not triggered.'
) )
}) })
it("rate-limiting should still kick in at a higher RPM for pro-tier", async () => { it('rate-limiting should still kick in at a higher RPM for pro-tier', async () => {
// Clear the require cache before running this test. // Clear the require cache before running this test.
// delete require.cache[ // delete require.cache[
// require.resolve("../../src/middleware/route-ratelimit") // require.resolve("../../src/middleware/route-ratelimit")
@@ -226,9 +225,9 @@ describe("#route-ratelimits & jwt-auth", () => {
rateLimitMiddleware = new RateLimits() rateLimitMiddleware = new RateLimits()
routeRateLimit = rateLimitMiddleware.routeRateLimit routeRateLimit = rateLimitMiddleware.routeRateLimit
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.method = "GET" req.method = 'GET'
req.locals.proLimit = true req.locals.proLimit = true
req.locals.apiLevel = 10 req.locals.apiLevel = 10
@@ -237,166 +236,166 @@ describe("#route-ratelimits & jwt-auth", () => {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next) await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
//console.log(`req.locals after test: ${util.inspect(req.locals)}`) // console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
false, false,
`next should NOT be called if rate limit was triggered.` 'next should NOT be called if rate limit was triggered.'
) )
}) })
}) })
describe("#getResource", () => { describe('#getResource', () => {
it("should decode a blockchain request", () => { it('should decode a blockchain request', () => {
const url = const url =
"/blockchain/getTxOut/62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8/0?include_mempool=true" '/blockchain/getTxOut/62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8/0?include_mempool=true'
const result = rateLimits.getResource(url) const result = rateLimits.getResource(url)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, "blockchain") assert.equal(result, 'blockchain')
}) })
}) })
describe("#calcPoints", () => { describe('#calcPoints', () => {
it("should return 30 points for anonymous user", () => { it('should return 30 points for anonymous user', () => {
const result = rateLimits.calcPoints() const result = rateLimits.calcPoints()
// console.log(`result: ${result}`) // console.log(`result: ${result}`)
assert.equal(result, 30) assert.equal(result, 30)
}) })
it("should return 10 points for free tier requesting full node access", () => { it('should return 10 points for free tier requesting full node access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 10, apiLevel: 10,
resource: "blockchain", resource: 'blockchain',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 10) assert.equal(result, 10)
}) })
it("should return 10 points for free tier requesting indexer access", () => { it('should return 10 points for free tier requesting indexer access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 10, apiLevel: 10,
resource: "blockbook", resource: 'blockbook',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 10) assert.equal(result, 10)
}) })
it("should return 10 points for free tier requesting SLPDB access", () => { it('should return 10 points for free tier requesting SLPDB access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 10, apiLevel: 10,
resource: "slp", resource: 'slp',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 10) assert.equal(result, 10)
}) })
it("should return 1 point for full node tier requesting full node access", () => { it('should return 1 point for full node tier requesting full node access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 20, apiLevel: 20,
resource: "blockchain", resource: 'blockchain',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 1) assert.equal(result, 1)
}) })
it("should return 10 points for full-node tier requesting indexer access", () => { it('should return 10 points for full-node tier requesting indexer access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 20, apiLevel: 20,
resource: "blockbook", resource: 'blockbook',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 10) assert.equal(result, 10)
}) })
it("should return 10 points for full node tier requesting SLPDB access", () => { it('should return 10 points for full node tier requesting SLPDB access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 20, apiLevel: 20,
resource: "slp", resource: 'slp',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 10) assert.equal(result, 10)
}) })
it("should return 1 point for indexer tier requesting full node access", () => { it('should return 1 point for indexer tier requesting full node access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 30, apiLevel: 30,
resource: "blockchain", resource: 'blockchain',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 1) assert.equal(result, 1)
}) })
it("should return 1 points for indexer tier requesting indexer access", () => { it('should return 1 points for indexer tier requesting indexer access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 30, apiLevel: 30,
resource: "blockbook", resource: 'blockbook',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 1) assert.equal(result, 1)
}) })
it("should return 10 points for indexer tier requesting SLPDB access", () => { it('should return 10 points for indexer tier requesting SLPDB access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 30, apiLevel: 30,
resource: "slp", resource: 'slp',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 10) assert.equal(result, 10)
}) })
it("should return 1 point for SLP tier requesting full node access", () => { it('should return 1 point for SLP tier requesting full node access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 40, apiLevel: 40,
resource: "blockchain", resource: 'blockchain',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 1) assert.equal(result, 1)
}) })
it("should return 1 points for SLP tier requesting indexer access", () => { it('should return 1 points for SLP tier requesting indexer access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 40, apiLevel: 40,
resource: "blockbook", resource: 'blockbook',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 1) assert.equal(result, 1)
}) })
it("should return 1 points for SLP tier requesting SLPDB access", () => { it('should return 1 points for SLP tier requesting SLPDB access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 40, apiLevel: 40,
resource: "slp", resource: 'slp',
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
@@ -404,28 +403,28 @@ describe("#route-ratelimits & jwt-auth", () => {
}) })
}) })
describe("#calcPoints2", () => { describe('#calcPoints2', () => {
it("should return 30 points for anonymous user", () => { it('should return 30 points for anonymous user', () => {
const result = rateLimits.calcPoints2({}) const result = rateLimits.calcPoints2({})
// console.log(`result: ${result}`) // console.log(`result: ${result}`)
assert.equal(result, 30) assert.equal(result, 30)
}) })
it("should return 10 point for free tier", () => { it('should return 10 point for free tier', () => {
const jwtInfo = { const jwtInfo = {
rateLimit: 10, rateLimit: 10,
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints2(jwtInfo) const result = rateLimits.calcPoints2(jwtInfo)
assert.equal(result, 10) assert.equal(result, 10)
}) })
it("should return 1 point for pro tier", () => { it('should return 1 point for pro tier', () => {
const jwtInfo = { const jwtInfo = {
rateLimit: 100, rateLimit: 100,
id: "5e3a0415eb29a962da2708b4" id: '5e3a0415eb29a962da2708b4'
} }
const result = rateLimits.calcPoints2(jwtInfo) const result = rateLimits.calcPoints2(jwtInfo)
@@ -433,12 +432,12 @@ describe("#route-ratelimits & jwt-auth", () => {
}) })
}) })
describe("#rateLimitByResource", () => { describe('#rateLimitByResource', () => {
it("should pass through rate-limit middleware", async () => { it('should pass through rate-limit middleware', async () => {
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
// Call the route twice to trigger the rate handling. // Call the route twice to trigger the rate handling.
await rateLimits.rateLimitByResource(req, res, next) await rateLimits.rateLimitByResource(req, res, next)
@@ -448,176 +447,176 @@ describe("#route-ratelimits & jwt-auth", () => {
assert.equal(next.called, true) assert.equal(next.called, true)
}) })
it("should trigger rate-limit handler if rate limits exceeds 5 request per minute", async () => { it('should trigger rate-limit handler if rate limits exceeds 5 request per minute', async () => {
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitByResource(req, res, next) await rateLimits.rateLimitByResource(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
false, false,
`next should not be called if rate limit was triggered.` 'next should not be called if rate limit was triggered.'
) )
}) })
it("should NOT trigger rate-limit for free-tier at 5 RPM", async () => { it('should NOT trigger rate-limit for free-tier at 5 RPM', async () => {
// Create a new instance of the rate limit so we start with zeroed tracking. // Create a new instance of the rate limit so we start with zeroed tracking.
rateLimits = new RateLimits() rateLimits = new RateLimits()
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
req.locals.jwtToken = "some-token" req.locals.jwtToken = 'some-token'
const jwtInfo = { const jwtInfo = {
apiLevel: 10, apiLevel: 10,
id: "5e3a0415eb29a962da2708b1" id: '5e3a0415eb29a962da2708b1'
} }
// Mock the call to the jwt library. // Mock the call to the jwt library.
sandbox.stub(rateLimits.jwt, "verify").returns(jwtInfo) sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo)
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitByResource(req, res, next) await rateLimits.rateLimitByResource(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
//console.log(`req.locals after test: ${util.inspect(req.locals)}`) // console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
true, true,
`next should be called if rate limit was not triggered.` 'next should be called if rate limit was not triggered.'
) )
}) })
it("should trigger rate-limit for free tier after 10 RPM", async () => { it('should trigger rate-limit for free tier after 10 RPM', async () => {
// Create a new instance of the rate limit so we start with zeroed tracking. // Create a new instance of the rate limit so we start with zeroed tracking.
rateLimits = new RateLimits() rateLimits = new RateLimits()
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
req.locals.jwtToken = "some-token" req.locals.jwtToken = 'some-token'
const jwtInfo = { const jwtInfo = {
apiLevel: 10, apiLevel: 10,
id: "5e3a0415eb29a962da2708b2" id: '5e3a0415eb29a962da2708b2'
} }
// Mock the call to the jwt library. // Mock the call to the jwt library.
sandbox.stub(rateLimits.jwt, "verify").returns(jwtInfo) sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo)
for (let i = 0; i < 12; i++) { for (let i = 0; i < 12; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitByResource(req, res, next) await rateLimits.rateLimitByResource(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
false, false,
`next should not be called if rate limit was triggered.` 'next should not be called if rate limit was triggered.'
) )
}) })
it("should NOT trigger rate-limit handler for indexer-tier at 25 RPM", async () => { it('should NOT trigger rate-limit handler for indexer-tier at 25 RPM', async () => {
// Create a new instance of the rate limit so we start with zeroed tracking. // Create a new instance of the rate limit so we start with zeroed tracking.
rateLimits = new RateLimits() rateLimits = new RateLimits()
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
req.locals.jwtToken = "some-token" req.locals.jwtToken = 'some-token'
const jwtInfo = { const jwtInfo = {
apiLevel: 20, apiLevel: 20,
id: "5e3a0415eb29a962da2708b3" id: '5e3a0415eb29a962da2708b3'
} }
// Mock the call to the jwt library. // Mock the call to the jwt library.
sandbox.stub(rateLimits.jwt, "verify").returns(jwtInfo) sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo)
for (let i = 0; i < 25; i++) { for (let i = 0; i < 25; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitByResource(req, res, next) await rateLimits.rateLimitByResource(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
//console.log(`req.locals after test: ${util.inspect(req.locals)}`) // console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
true, true,
`next should be called if rate limit was not triggered.` 'next should be called if rate limit was not triggered.'
) )
}) })
it("should still rate-limit at a higher RPM for pro-tier", async () => { it('should still rate-limit at a higher RPM for pro-tier', async () => {
// Create a new instance of the rate limit so we start with zeroed tracking. // Create a new instance of the rate limit so we start with zeroed tracking.
rateLimits = new RateLimits() rateLimits = new RateLimits()
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
req.locals.jwtToken = "some-token" req.locals.jwtToken = 'some-token'
const jwtInfo = { const jwtInfo = {
apiLevel: 20, apiLevel: 20,
id: "5e3a0415eb29a962da2708b5" id: '5e3a0415eb29a962da2708b5'
} }
// Mock the call to the jwt library. // Mock the call to the jwt library.
sandbox.stub(rateLimits.jwt, "verify").returns(jwtInfo) sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo)
for (let i = 0; i < 150; i++) { for (let i = 0; i < 150; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitByResource(req, res, next) await rateLimits.rateLimitByResource(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
//console.log(`req.locals after test: ${util.inspect(req.locals)}`) // console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
false, false,
`next should NOT be called if rate limit was triggered.` 'next should NOT be called if rate limit was triggered.'
) )
}) })
}) })
describe("#rateLimitSimple", () => { describe('#rateLimitSimple', () => {
it("should pass through rate-limit middleware", async () => { it('should pass through rate-limit middleware', async () => {
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
// Call the route twice to trigger the rate handling. // Call the route twice to trigger the rate handling.
await rateLimits.rateLimitSimple(req, res, next) await rateLimits.rateLimitSimple(req, res, next)
@@ -627,179 +626,179 @@ describe("#route-ratelimits & jwt-auth", () => {
assert.equal(next.called, true) assert.equal(next.called, true)
}) })
it("should trigger rate-limit handler if rate limits exceeds 5 request per minute", async () => { it('should trigger rate-limit handler if rate limits exceeds 5 request per minute', async () => {
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitSimple(req, res, next) await rateLimits.rateLimitSimple(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
false, false,
`next should not be called if rate limit was triggered.` 'next should not be called if rate limit was triggered.'
) )
}) })
it("should NOT trigger rate-limit for free-tier at 5 RPM", async () => { it('should NOT trigger rate-limit for free-tier at 5 RPM', async () => {
// Create a new instance of the rate limit so we start with zeroed tracking. // Create a new instance of the rate limit so we start with zeroed tracking.
rateLimits = new RateLimits() rateLimits = new RateLimits()
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
req.locals.jwtToken = "some-token" req.locals.jwtToken = 'some-token'
const jwtInfo = { const jwtInfo = {
rateLimit: 10, rateLimit: 10,
id: "5e3a0415eb29a962da2708c1" id: '5e3a0415eb29a962da2708c1'
} }
// Mock the call to the jwt library. // Mock the call to the jwt library.
sandbox.stub(rateLimits.jwt, "verify").returns(jwtInfo) sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo)
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitSimple(req, res, next) await rateLimits.rateLimitSimple(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
//console.log(`req.locals after test: ${util.inspect(req.locals)}`) // console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
true, true,
`next should be called if rate limit was not triggered.` 'next should be called if rate limit was not triggered.'
) )
}) })
it("should trigger rate-limit for free tier after 10 RPM", async () => { it('should trigger rate-limit for free tier after 10 RPM', async () => {
// Create a new instance of the rate limit so we start with zeroed tracking. // Create a new instance of the rate limit so we start with zeroed tracking.
rateLimits = new RateLimits() rateLimits = new RateLimits()
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
req.locals.jwtToken = "some-token" req.locals.jwtToken = 'some-token'
const jwtInfo = { const jwtInfo = {
rateLimit: 10, rateLimit: 10,
id: "5e3a0415eb29a962da2708c2" id: '5e3a0415eb29a962da2708c2'
} }
// Mock the call to the jwt library. // Mock the call to the jwt library.
sandbox.stub(rateLimits.jwt, "verify").returns(jwtInfo) sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo)
for (let i = 0; i < 12; i++) { for (let i = 0; i < 12; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitSimple(req, res, next) await rateLimits.rateLimitSimple(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
false, false,
`next should not be called if rate limit was triggered.` 'next should not be called if rate limit was triggered.'
) )
}) })
it("should NOT trigger rate-limit handler for pro-tier at 25 RPM", async () => { it('should NOT trigger rate-limit handler for pro-tier at 25 RPM', async () => {
// Create a new instance of the rate limit so we start with zeroed tracking. // Create a new instance of the rate limit so we start with zeroed tracking.
rateLimits = new RateLimits() rateLimits = new RateLimits()
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
req.locals.jwtToken = "some-token" req.locals.jwtToken = 'some-token'
const jwtInfo = { const jwtInfo = {
rateLimit: 100, rateLimit: 100,
id: "5e3a0415eb29a962da2708c3" id: '5e3a0415eb29a962da2708c3'
} }
// Mock the call to the jwt library. // Mock the call to the jwt library.
sandbox.stub(rateLimits.jwt, "verify").returns(jwtInfo) sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo)
for (let i = 0; i < 25; i++) { for (let i = 0; i < 25; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitSimple(req, res, next) await rateLimits.rateLimitSimple(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
//console.log(`req.locals after test: ${util.inspect(req.locals)}`) // console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
true, true,
`next should be called if rate limit was not triggered.` 'next should be called if rate limit was not triggered.'
) )
}) })
it("should still rate-limit at a higher RPM for pro-tier", async () => { it('should still rate-limit at a higher RPM for pro-tier', async () => {
// Create a new instance of the rate limit so we start with zeroed tracking. // Create a new instance of the rate limit so we start with zeroed tracking.
rateLimits = new RateLimits() rateLimits = new RateLimits()
req.baseUrl = "/v3" req.baseUrl = '/v3'
req.path = "/control/getNetworkInfo" req.path = '/control/getNetworkInfo'
req.url = req.path req.url = req.path
req.method = "GET" req.method = 'GET'
req.locals.jwtToken = "some-token" req.locals.jwtToken = 'some-token'
const jwtInfo = { const jwtInfo = {
rateLimit: 100, rateLimit: 100,
id: "5e3a0415eb29a962da2708c4" id: '5e3a0415eb29a962da2708c4'
} }
// Mock the call to the jwt library. // Mock the call to the jwt library.
sandbox.stub(rateLimits.jwt, "verify").returns(jwtInfo) sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo)
for (let i = 0; i < 150; i++) { for (let i = 0; i < 150; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitSimple(req, res, next) await rateLimits.rateLimitSimple(req, res, next)
//console.log(`next() called: ${next.called}`) // console.log(`next() called: ${next.called}`)
} }
//console.log(`req.locals after test: ${util.inspect(req.locals)}`) // console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in. // Note: next() will be called unless the rate-limit kicks in.
assert.equal( assert.equal(
next.called, next.called,
false, false,
`next should NOT be called if rate limit was triggered.` 'next should NOT be called if rate limit was triggered.'
) )
}) })
}) })
}) })
// Generates a Basic authorization header. // Generates a Basic authorization header.
function generateAuthHeader(pass) { // function generateAuthHeader (pass) {
// https://en.wikipedia.org/wiki/Basic_access_authentication // // https://en.wikipedia.org/wiki/Basic_access_authentication
const username = "BITBOX" // const username = 'BITBOX'
const combined = `${username}:${pass}` // const combined = `${username}:${pass}`
//
var base64Credential = Buffer.from(combined).toString("base64") // var base64Credential = Buffer.from(combined).toString('base64')
var readyCredential = `Basic ${base64Credential}` // var readyCredential = `Basic ${base64Credential}`
//
return readyCredential // return readyCredential
} // }
File diff suppressed because it is too large Load Diff
+186 -187
View File
@@ -9,50 +9,50 @@
-See listSingleToken() tests. -See listSingleToken() tests.
*/ */
"use strict" 'use strict'
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const nock = require("nock") // HTTP mocking const nock = require('nock') // HTTP mocking
const sinon = require("sinon") const sinon = require('sinon')
const proxyquire = require("proxyquire").noPreserveCache() // const proxyquire = require('proxyquire').noPreserveCache()
const axios = require("axios") // const axios = require('axios')
// Save existing environment variables. // Save existing environment variables.
// Used during transition from integration to unit tests. // Used during transition from integration to unit tests.
let originalEnvVars, mockServerUrl let mockServerUrl
originalEnvVars = { const originalEnvVars = {
BITDB_URL: process.env.BITDB_URL, BITDB_URL: process.env.BITDB_URL,
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
SLPDB_URL: process.env.SLPDB_URL SLPDB_URL: process.env.SLPDB_URL
} }
// Set default environment variables for unit tests. // Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit" if (!process.env.TEST) process.env.TEST = 'unit'
// Block network connections for unit tests. // Block network connections for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
process.env.BITDB_URL = "http://fakeurl/" process.env.BITDB_URL = 'http://fakeurl/'
process.env.BITCOINCOM_BASEURL = "http://fakeurl/" process.env.BITCOINCOM_BASEURL = 'http://fakeurl/'
process.env.SLPDB_URL = "http://fakeurl/" process.env.SLPDB_URL = 'http://fakeurl/'
mockServerUrl = `http://fakeurl` mockServerUrl = 'http://fakeurl'
} }
// Prepare the slpRoute for stubbing dependcies on slpjs. // Prepare the slpRoute for stubbing dependcies on slpjs.
const slpRoute = require("../../src/routes/v3/slp") const slpRoute = require('../../src/routes/v3/slp')
const pathStub = {} // Used to stub methods within slpjs. // const pathStub = {} // Used to stub methods within slpjs.
const slpRouteStub = proxyquire("../../src/routes/v3/slp", { slpjs: pathStub }) // const slpRouteStub = proxyquire('../../src/routes/v3/slp', { slpjs: pathStub })
// Mocking data. // Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks") const { mockReq, mockRes } = require('./mocks/express-mocks')
const mockData = require("./mocks/slp-mocks") const mockData = require('./mocks/slp-mocks')
const slpjsMock = require("./mocks/slpjs-mocks") // const slpjsMock = require('./mocks/slpjs-mocks')
// Used for debugging. // Used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
describe("#SLP", () => { describe('#SLP', () => {
let req, res let req, res
let sandbox let sandbox
@@ -65,7 +65,7 @@ describe("#SLP", () => {
res = mockRes res = mockRes
// Explicitly reset the parmas and body. // Explicitly reset the parmas and body.
//req.params = {} // req.params = {}
req.body = {} req.body = {}
req.query = {} req.query = {}
req.locals = {} req.locals = {}
@@ -91,15 +91,15 @@ describe("#SLP", () => {
process.env.SLPDB_URL = originalEnvVars.SLPDB_URL process.env.SLPDB_URL = originalEnvVars.SLPDB_URL
}) })
describe("#root", async () => { describe('#root', async () => {
// root route handler. // root route handler.
const root = slpRoute.testableComponents.root const root = slpRoute.testableComponents.root
it("should respond to GET for base route", async () => { it('should respond to GET for base route', async () => {
const result = root(req, res) const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "slp", "Returns static string") assert.equal(result.status, 'slp', 'Returns static string')
}) })
}) })
/* /*
@@ -430,46 +430,46 @@ describe("#SLP", () => {
}) })
}) })
*/ */
describe("balancesForAddress()", () => { describe('balancesForAddress()', () => {
const balancesForAddress = slpRoute.testableComponents.balancesForAddress const balancesForAddress = slpRoute.testableComponents.balancesForAddress
it("should throw 400 if address is empty", async () => { it('should throw 400 if address is empty', async () => {
const result = await balancesForAddress(req, res) const result = await balancesForAddress(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "address can not be empty") assert.include(result.error, 'address can not be empty')
}) })
it("should throw 400 if address is invalid", async () => { it('should throw 400 if address is invalid', async () => {
req.params.address = "badAddress" req.params.address = 'badAddress'
const result = await balancesForAddress(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Invalid BCH address.")
})
it("should throw 400 if address network mismatch", async () => {
req.params.address = "slptest:qzcvpw3ah7r880d49wsqzrsl90pg0rqjjurmj3g4nk"
const result = await balancesForAddress(req, res) const result = await balancesForAddress(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "Invalid") assert.include(result.error, 'Invalid BCH address.')
}) })
it("should throw 5XX error when network issues", async () => { it('should throw 400 if address network mismatch', async () => {
req.params.address = 'slptest:qzcvpw3ah7r880d49wsqzrsl90pg0rqjjurmj3g4nk'
const result = await balancesForAddress(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ['error'])
assert.include(result.error, 'Invalid')
})
it('should throw 5XX error when network issues', async () => {
// Save the existing SLPDB_URL. // Save the existing SLPDB_URL.
const savedUrl2 = process.env.SLPDB_URL const savedUrl2 = process.env.SLPDB_URL
// Manipulate the URL to cause a 500 network error. // Manipulate the URL to cause a 500 network error.
process.env.SLPDB_URL = "http://fakeurl/api/" process.env.SLPDB_URL = 'http://fakeurl/api/'
req.params.address = req.params.address =
"simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn" 'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
const result = await balancesForAddress(req, res) const result = await balancesForAddress(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
@@ -480,83 +480,83 @@ describe("#SLP", () => {
assert.isAbove( assert.isAbove(
res.statusCode, res.statusCode,
499, 499,
"HTTP status code 500 or greater expected." 'HTTP status code 500 or greater expected.'
) )
assert.include( assert.include(
result.error, result.error,
"Network error: Could not communicate", 'Network error: Could not communicate',
"Error message expected" 'Error message expected'
) )
}) })
it("should get token balance for an address", async () => { it('should get token balance for an address', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(mockServerUrl) nock(mockServerUrl)
.get(uri => uri.includes("/")) .get(uri => uri.includes('/'))
.times(2) .times(2)
.reply(200, mockData.mockSingleAddress) .reply(200, mockData.mockSingleAddress)
} }
req.params.address = req.params.address =
"simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn" 'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
const result = await balancesForAddress(req, res) const result = await balancesForAddress(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAllKeys(result[0], [ assert.hasAllKeys(result[0], [
"tokenId", 'tokenId',
"balance", 'balance',
"slpAddress", 'slpAddress',
"decimalCount" 'decimalCount'
]) ])
}) })
}) })
describe("balancesForAddressBulk()", () => { describe('balancesForAddressBulk()', () => {
const balancesForAddressBulk = const balancesForAddressBulk =
slpRoute.testableComponents.balancesForAddressBulk slpRoute.testableComponents.balancesForAddressBulk
it("should throw 400 if addresses is empty", async () => { it('should throw 400 if addresses is empty', async () => {
const result = await balancesForAddressBulk(req, res) const result = await balancesForAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "addresses needs to be an array") assert.include(result.error, 'addresses needs to be an array')
}) })
it("should throw 400 if address is invalid", async () => { it('should throw 400 if address is invalid', async () => {
req.body.addresses = ["badAddress"] req.body.addresses = ['badAddress']
const result = await balancesForAddressBulk(req, res) const result = await balancesForAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "Invalid BCH address.") assert.include(result.error, 'Invalid BCH address.')
}) })
it("should throw 400 if address network mismatch", async () => { it('should throw 400 if address network mismatch', async () => {
req.body.addresses = [ req.body.addresses = [
"slptest:qzcvpw3ah7r880d49wsqzrsl90pg0rqjjurmj3g4nk" 'slptest:qzcvpw3ah7r880d49wsqzrsl90pg0rqjjurmj3g4nk'
] ]
const result = await balancesForAddressBulk(req, res) const result = await balancesForAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "Invalid") assert.include(result.error, 'Invalid')
}) })
it("should throw 5XX error when network issues", async () => { it('should throw 5XX error when network issues', async () => {
// Save the existing SLPDB_URL. // Save the existing SLPDB_URL.
const savedUrl2 = process.env.SLPDB_URL const savedUrl2 = process.env.SLPDB_URL
// Manipulate the URL to cause a 500 network error. // Manipulate the URL to cause a 500 network error.
process.env.SLPDB_URL = "http://fakeurl/api/" process.env.SLPDB_URL = 'http://fakeurl/api/'
req.body.addresses = [ req.body.addresses = [
"simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn" 'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
] ]
const result = await balancesForAddressBulk(req, res) const result = await balancesForAddressBulk(req, res)
@@ -568,20 +568,20 @@ describe("#SLP", () => {
assert.isAbove( assert.isAbove(
res.statusCode, res.statusCode,
499, 499,
"HTTP status code 500 or greater expected." 'HTTP status code 500 or greater expected.'
) )
assert.include( assert.include(
result.error, result.error,
"Network error: Could not communicate", 'Network error: Could not communicate',
"Error message expected" 'Error message expected'
) )
}) })
// Only run as an integration test. Too complex to stub accurately. // Only run as an integration test. Too complex to stub accurately.
if (process.env.TEST !== "unit") { if (process.env.TEST !== 'unit') {
it("should get token balance for an address", async () => { it('should get token balance for an address', async () => {
req.body.addresses = [ req.body.addresses = [
"simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn" 'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
] ]
const result = await balancesForAddressBulk(req, res) const result = await balancesForAddressBulk(req, res)
@@ -590,11 +590,11 @@ describe("#SLP", () => {
assert.isArray(result) assert.isArray(result)
assert.isArray(result[0]) assert.isArray(result[0])
assert.hasAnyKeys(result[0][0], [ assert.hasAnyKeys(result[0][0], [
"tokenId", 'tokenId',
"balance", 'balance',
"balanceString", 'balanceString',
"slpAddress", 'slpAddress',
"decimalCount" 'decimalCount'
]) ])
}) })
} }
@@ -807,112 +807,112 @@ describe("#SLP", () => {
}) })
}) })
*/ */
describe("validateBulk()", () => { describe('validateBulk()', () => {
const validateBulk = slpRoute.testableComponents.validateBulk const validateBulk = slpRoute.testableComponents.validateBulk
it("should throw 400 if txid array is empty", async () => { it('should throw 400 if txid array is empty', async () => {
const result = await validateBulk(req, res) const result = await validateBulk(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "txids needs to be an array") assert.include(result.error, 'txids needs to be an array')
assert.equal(res.statusCode, 400) assert.equal(res.statusCode, 400)
}) })
it("should throw 400 error if array is too large", async () => { it('should throw 400 error if array is too large', async () => {
const testArray = [] const testArray = []
for (var i = 0; i < 25; i++) testArray.push("") for (var i = 0; i < 25; i++) testArray.push('')
req.body.txids = testArray req.body.txids = testArray
const result = await validateBulk(req, res) const result = await validateBulk(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "Array too large") assert.include(result.error, 'Array too large')
}) })
it("should validate array with single element", async () => { it('should validate array with single element', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.SLPDB_URL}`) nock(`${process.env.SLPDB_URL}`)
.get(uri => uri.includes("/")) .get(uri => uri.includes('/'))
.reply(200, mockData.mockSingleValidTxid) .reply(200, mockData.mockSingleValidTxid)
} }
req.body.txids = [ req.body.txids = [
"77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d" '77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d'
] ]
const result = await validateBulk(req, res) const result = await validateBulk(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAllKeys(result[0], ["txid", "valid"]) assert.hasAllKeys(result[0], ['txid', 'valid'])
}) })
it("should validate array with two elements", async () => { it('should validate array with two elements', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.SLPDB_URL}`) nock(`${process.env.SLPDB_URL}`)
.get(uri => uri.includes("/")) .get(uri => uri.includes('/'))
.reply(200, mockData.mockTwoValidTxid) .reply(200, mockData.mockTwoValidTxid)
} }
req.body.txids = [ req.body.txids = [
"77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d", '77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d',
"552112f9e458dc7d1d8b328b0a6685e8af74a64b60b6846e7c86407f27f47e42" '552112f9e458dc7d1d8b328b0a6685e8af74a64b60b6846e7c86407f27f47e42'
] ]
const result = await validateBulk(req, res) const result = await validateBulk(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAllKeys(result[0], ["txid", "valid"]) assert.hasAllKeys(result[0], ['txid', 'valid'])
assert.equal(result.length, 2) assert.equal(result.length, 2)
}) })
// Captures a regression bug that went out to production, captured in this // Captures a regression bug that went out to production, captured in this
// GitHub Issue: https://github.com/Bitcoin-com/rest.bitcoin.com/issues/518 // GitHub Issue: https://github.com/Bitcoin-com/rest.bitcoin.com/issues/518
it("should return two elements if given two elements", async () => { it('should return two elements if given two elements', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.SLPDB_URL}`) nock(`${process.env.SLPDB_URL}`)
.get(uri => uri.includes("/")) .get(uri => uri.includes('/'))
.reply(200, mockData.mockTwoRedundentTxid) .reply(200, mockData.mockTwoRedundentTxid)
} }
req.body.txids = [ req.body.txids = [
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56", 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56" 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56'
] ]
const result = await validateBulk(req, res) const result = await validateBulk(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAllKeys(result[0], ["txid", "valid"]) assert.hasAllKeys(result[0], ['txid', 'valid'])
assert.equal(result.length, 2) assert.equal(result.length, 2)
}) })
}) })
describe("tokenStatsSingle()", () => { describe('tokenStatsSingle()', () => {
const tokenStatsSingle = slpRoute.testableComponents.tokenStats const tokenStatsSingle = slpRoute.testableComponents.tokenStats
it("should throw 400 if tokenID is empty", async () => { it('should throw 400 if tokenID is empty', async () => {
req.params.tokenId = "" req.params.tokenId = ''
const result = await tokenStatsSingle(req, res) const result = await tokenStatsSingle(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "tokenId can not be empty") assert.include(result.error, 'tokenId can not be empty')
}) })
// //
it("should get token stats for tokenId", async () => { it('should get token stats for tokenId', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.SLPDB_URL}`) nock(`${process.env.SLPDB_URL}`)
.get(uri => uri.includes("/")) .get(uri => uri.includes('/'))
.reply(200, { .reply(200, {
t: [ t: [
{ {
@@ -924,106 +924,106 @@ describe("#SLP", () => {
} }
req.params.tokenId = req.params.tokenId =
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
const result = await tokenStatsSingle(req, res) const result = await tokenStatsSingle(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [ assert.hasAnyKeys(result, [
"blockCreated", 'blockCreated',
"blockLastActiveMint", 'blockLastActiveMint',
"blockLastActiveSend", 'blockLastActiveSend',
"containsBaton", 'containsBaton',
"initialTokenQty", 'initialTokenQty',
"mintingBatonStatus", 'mintingBatonStatus',
"circulatingSupply", 'circulatingSupply',
"decimals", 'decimals',
"documentHash", 'documentHash',
"versionType", 'versionType',
"timestamp", 'timestamp',
"documentUri", 'documentUri',
"name", 'name',
"symbol", 'symbol',
"id", 'id',
"totalBurned", 'totalBurned',
"totalMinted", 'totalMinted',
"txnsSinceGenesis", 'txnsSinceGenesis',
"validAddresses" 'validAddresses'
]) ])
}) })
}) })
describe("balancesForTokenSingle()", () => { describe('balancesForTokenSingle()', () => {
const balancesForTokenSingle = const balancesForTokenSingle =
slpRoute.testableComponents.balancesForTokenSingle slpRoute.testableComponents.balancesForTokenSingle
it("should throw 400 if tokenID is empty", async () => { it('should throw 400 if tokenID is empty', async () => {
req.params.tokenId = "" req.params.tokenId = ''
const result = await balancesForTokenSingle(req, res) const result = await balancesForTokenSingle(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "tokenId can not be empty") assert.include(result.error, 'tokenId can not be empty')
}) })
it("should get balances for tokenId", async () => { it('should get balances for tokenId', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.SLPDB_URL}`) nock(`${process.env.SLPDB_URL}`)
.get(uri => uri.includes("/")) .get(uri => uri.includes('/'))
.reply(200, { .reply(200, {
a: [mockData.mockBalance] a: [mockData.mockBalance]
}) })
} }
req.params.tokenId = req.params.tokenId =
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
const result = await balancesForTokenSingle(req, res) const result = await balancesForTokenSingle(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result[0], ["tokenId", "slpAddress", "tokenBalance"]) assert.hasAllKeys(result[0], ['tokenId', 'slpAddress', 'tokenBalance'])
}) })
}) })
describe("txDetails()", () => { describe('txDetails()', () => {
const txDetails = slpRoute.testableComponents.txDetails const txDetails = slpRoute.testableComponents.txDetails
it("should throw 400 if txid is empty", async () => { it('should throw 400 if txid is empty', async () => {
const result = await txDetails(req, res) const result = await txDetails(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "txid can not be empty") assert.include(result.error, 'txid can not be empty')
}) })
it("should throw 400 for malformed txid", async () => { it('should throw 400 for malformed txid', async () => {
req.params.txid = req.params.txid =
"57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457" '57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457'
const result = await txDetails(req, res) const result = await txDetails(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "This is not a txid") assert.include(result.error, 'This is not a txid')
}) })
it("should throw 400 for non-existant txid", async () => { it('should throw 400 for non-existant txid', async () => {
// Integration test // Integration test
if (process.env.TEST !== "unit") { if (process.env.TEST !== 'unit') {
req.params.txid = req.params.txid =
"57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b94578223333" '57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b94578223333'
const result = await txDetails(req, res) const result = await txDetails(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "TXID not found") assert.include(result.error, 'TXID not found')
} }
}) })
if (process.env.TEST === "integration") { if (process.env.TEST === 'integration') {
it("should get tx details with token info", async () => { it('should get tx details with token info', async () => {
// TODO: add mocking for unit testing. How do I mock reponse form SLPDB // TODO: add mocking for unit testing. How do I mock reponse form SLPDB
// since it's not an object? // since it's not an object?
@@ -1034,38 +1034,38 @@ describe("#SLP", () => {
// } // }
req.params.txid = req.params.txid =
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
const result = await txDetails(req, res) const result = await txDetails(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, ["tokenIsValid", "tokenInfo"]) assert.hasAnyKeys(result, ['tokenIsValid', 'tokenInfo'])
}) })
} }
}) })
describe("txsTokenIdAddressSingle()", () => { describe('txsTokenIdAddressSingle()', () => {
const txsTokenIdAddressSingle = const txsTokenIdAddressSingle =
slpRoute.testableComponents.txsTokenIdAddressSingle slpRoute.testableComponents.txsTokenIdAddressSingle
it("should throw 400 if tokenId is empty", async () => { it('should throw 400 if tokenId is empty', async () => {
req.params.tokenId = "" req.params.tokenId = ''
const result = await txsTokenIdAddressSingle(req, res) const result = await txsTokenIdAddressSingle(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "tokenId can not be empty") assert.include(result.error, 'tokenId can not be empty')
}) })
it("should throw 400 if address is empty", async () => { it('should throw 400 if address is empty', async () => {
req.params.tokenId = req.params.tokenId =
"495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a" '495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a'
req.params.address = "" req.params.address = ''
const result = await txsTokenIdAddressSingle(req, res) const result = await txsTokenIdAddressSingle(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "address can not be empty") assert.include(result.error, 'address can not be empty')
}) })
/* /*
it("should get tx details with tokenId and address", async () => { it("should get tx details with tokenId and address", async () => {
@@ -1085,7 +1085,6 @@ describe("#SLP", () => {
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796" "7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796"
req.params.address = "slptest:qpwa35xq0q0cnmdu0rwzkct369hddzsqpsqdzw6h9h" req.params.address = "slptest:qpwa35xq0q0cnmdu0rwzkct369hddzsqpsqdzw6h9h"
const result = await txsTokenIdAddressSingle(req, res) const result = await txsTokenIdAddressSingle(req, res)
console.log(`result: ${JSON.stringify(result, null, 2)}`) console.log(`result: ${JSON.stringify(result, null, 2)}`)
+157 -157
View File
@@ -6,27 +6,27 @@
to 'integration' to run the tests against BCH mainnet. to 'integration' to run the tests against BCH mainnet.
*/ */
"use strict" 'use strict'
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const utilRoute = require("../../src/routes/v3/util") const utilRoute = require('../../src/routes/v3/util')
const nock = require("nock") // HTTP mocking const nock = require('nock') // HTTP mocking
const sinon = require("sinon") const sinon = require('sinon')
let originalEnvVars // Used during transition from integration to unit tests. let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data. // Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks") const { mockReq, mockRes } = require('./mocks/express-mocks')
const mockData = require("./mocks/util-mocks") const mockData = require('./mocks/util-mocks')
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
const UtilRoute = utilRoute.UtilRoute // const UtilRoute = utilRoute.UtilRoute
const utilRouteInst = new utilRoute.UtilRoute() const utilRouteInst = new utilRoute.UtilRoute()
describe("#Util", () => { describe('#Util', () => {
let req, res let req, res
let sandbox let sandbox
@@ -40,12 +40,12 @@ describe("#Util", () => {
} }
// Set default environment variables for unit tests. // Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit" if (!process.env.TEST) process.env.TEST = 'unit'
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" process.env.BITCOINCOM_BASEURL = 'http://fakeurl/api/'
process.env.RPC_BASEURL = "http://fakeurl/api" process.env.RPC_BASEURL = 'http://fakeurl/api'
process.env.RPC_USERNAME = "fakeusername" process.env.RPC_USERNAME = 'fakeusername'
process.env.RPC_PASSWORD = "fakepassword" process.env.RPC_PASSWORD = 'fakepassword'
} }
}) })
@@ -82,43 +82,43 @@ describe("#Util", () => {
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
}) })
describe("#root", async () => { describe('#root', async () => {
// root route handler. // root route handler.
const root = utilRouteInst.root const root = utilRouteInst.root
it("should respond to GET for base route", async () => { it('should respond to GET for base route', async () => {
const result = root(req, res) const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "util", "Returns static string") assert.equal(result.status, 'util', 'Returns static string')
}) })
}) })
describe("#validateAddressSingle", async () => { describe('#validateAddressSingle', async () => {
const validateAddress = utilRouteInst.validateAddressSingle const validateAddress = utilRouteInst.validateAddressSingle
it("should throw an error for an empty address", async () => { it('should throw an error for an empty address', async () => {
const result = await validateAddress(req, res) const result = await validateAddress(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include( assert.include(
result.error, result.error,
"address can not be empty", 'address can not be empty',
"Proper error message" 'Proper error message'
) )
}) })
it("should throw 503 when network issues", async () => { it('should throw 503 when network issues', async () => {
// Save the existing RPC URL. // Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error. // Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/" process.env.RPC_BASEURL = 'http://fakeurl/api/'
req.params.address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` req.params.address = 'bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y'
const result = await validateAddress(req, res) await validateAddress(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL. // Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2 process.env.RPC_BASEURL = savedUrl2
@@ -126,120 +126,120 @@ describe("#Util", () => {
assert.isAbove( assert.isAbove(
res.statusCode, res.statusCode,
499, 499,
"HTTP status code 500 or greater expected." 'HTTP status code 500 or greater expected.'
) )
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") // assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
}) })
it("should validate address", async () => { it('should validate address', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.RPC_BASEURL}`) nock(`${process.env.RPC_BASEURL}`)
.post(uri => uri.includes("/")) .post(uri => uri.includes('/'))
.reply(200, { result: mockData.mockAddress }) .reply(200, { result: mockData.mockAddress })
} }
req.params.address = `bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd` req.params.address = 'bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd'
const result = await validateAddress(req, res) const result = await validateAddress(req, res)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [ assert.hasAnyKeys(result, [
"isvalid", 'isvalid',
"address", 'address',
"scriptPubKey", 'scriptPubKey',
"ismine", 'ismine',
"iswatchonly", 'iswatchonly',
"isscript" 'isscript'
]) ])
}) })
}) })
describe("#validateAddressBulk", async () => { describe('#validateAddressBulk', async () => {
const validateAddressBulk = utilRouteInst.validateAddressBulk const validateAddressBulk = utilRouteInst.validateAddressBulk
it("should throw an error for an empty body", async () => { it('should throw an error for an empty body', async () => {
const result = await validateAddressBulk(req, res) const result = await validateAddressBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include( assert.include(
result.error, result.error,
"addresses needs to be an array. Use GET for single address.", 'addresses needs to be an array. Use GET for single address.',
"Proper error message" 'Proper error message'
) )
}) })
it("should error on non-array single address", async () => { it('should error on non-array single address', async () => {
req.body = { req.body = {
addresses: `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` addresses: 'bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y'
} }
const result = await validateAddressBulk(req, res) const result = await validateAddressBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include( assert.include(
result.error, result.error,
"addresses needs to be an array. Use GET for single address.", 'addresses needs to be an array. Use GET for single address.',
"Proper error message" 'Proper error message'
) )
}) })
it("should throw 400 error if addresses array is too large", async () => { it('should throw 400 error if addresses array is too large', async () => {
const testArray = [] const testArray = []
for (var i = 0; i < 25; i++) testArray.push("") for (var i = 0; i < 25; i++) testArray.push('')
req.body.addresses = testArray req.body.addresses = testArray
const result = await validateAddressBulk(req, res) const result = await validateAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "Array too large") assert.include(result.error, 'Array too large')
}) })
it("should error on invalid address", async () => { it('should error on invalid address', async () => {
req.body = { req.body = {
addresses: [`bchtest:qqqk4y6lsl5da64sg5qc3xezmpl`] addresses: ['bchtest:qqqk4y6lsl5da64sg5qc3xezmpl']
} }
const result = await validateAddressBulk(req, res) const result = await validateAddressBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include( assert.include(
result.error, result.error,
"Invalid BCH address. Double check your address is valid", 'Invalid BCH address. Double check your address is valid',
"Proper error message" 'Proper error message'
) )
}) })
it("should error on mainnet address when using testnet", async () => { it('should error on mainnet address when using testnet', async () => {
req.body = { req.body = {
addresses: [`bchtest:qrls6vzjkkxlds7aqv9075u0fttwc7u9jvczn5fdt9`] addresses: ['bchtest:qrls6vzjkkxlds7aqv9075u0fttwc7u9jvczn5fdt9']
} }
const result = await validateAddressBulk(req, res) const result = await validateAddressBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include( assert.include(
result.error, result.error,
"Invalid network. Trying to use a testnet address on mainnet, or vice versa.", 'Invalid network. Trying to use a testnet address on mainnet, or vice versa.',
"Proper error message" 'Proper error message'
) )
}) })
it("should throw 503 when network issues", async () => { it('should throw 503 when network issues', async () => {
// Save the existing RPC URL. // Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error. // Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/" process.env.RPC_BASEURL = 'http://fakeurl/api/'
req.body.addresses = [ req.body.addresses = [
`bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd` 'bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd'
] ]
const result = await validateAddressBulk(req, res) await validateAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL. // Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2 process.env.RPC_BASEURL = savedUrl2
@@ -247,157 +247,157 @@ describe("#Util", () => {
assert.isAbove( assert.isAbove(
res.statusCode, res.statusCode,
499, 499,
"HTTP status code 500 or greater expected." 'HTTP status code 500 or greater expected.'
) )
}) })
it("should validate a single address", async () => { it('should validate a single address', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.RPC_BASEURL}`) nock(`${process.env.RPC_BASEURL}`)
.post(uri => uri.includes("/")) .post(uri => uri.includes('/'))
.reply(200, { result: mockData.mockAddress }) .reply(200, { result: mockData.mockAddress })
} }
req.body.addresses = [ req.body.addresses = [
`bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd` 'bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd'
] ]
const result = await validateAddressBulk(req, res) const result = await validateAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAnyKeys(result[0], [ assert.hasAnyKeys(result[0], [
"isvalid", 'isvalid',
"address", 'address',
"scriptPubKey", 'scriptPubKey',
"ismine", 'ismine',
"iswatchonly", 'iswatchonly',
"isscript" 'isscript'
]) ])
}) })
it("should validate a multiple addresses", async () => { it('should validate a multiple addresses', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
nock(`${process.env.RPC_BASEURL}`) nock(`${process.env.RPC_BASEURL}`)
.post(uri => uri.includes("/")) .post(uri => uri.includes('/'))
.times(2) .times(2)
.reply(200, { result: mockData.mockAddress }) .reply(200, { result: mockData.mockAddress })
} }
req.body.addresses = [ req.body.addresses = [
`bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd`, 'bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd',
`bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd` 'bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd'
] ]
const result = await validateAddressBulk(req, res) const result = await validateAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAnyKeys(result[0], [ assert.hasAnyKeys(result[0], [
"isvalid", 'isvalid',
"address", 'address',
"scriptPubKey", 'scriptPubKey',
"ismine", 'ismine',
"iswatchonly", 'iswatchonly',
"isscript" 'isscript'
]) ])
}) })
}) })
describe("#sweepWif", () => { describe('#sweepWif', () => {
it("should throw 400 if WIF is not included", async () => { it('should throw 400 if WIF is not included', async () => {
req.body = {} req.body = {}
const result = await utilRouteInst.sweepWif(req, res) const result = await utilRouteInst.sweepWif(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include( assert.include(
result.error, result.error,
"WIF needs to a proper compressed WIF starting with K or L", 'WIF needs to a proper compressed WIF starting with K or L',
"Proper error message" 'Proper error message'
) )
}) })
it("should throw 400 if WIF is malformed", async () => { it('should throw 400 if WIF is malformed', async () => {
req.body = { req.body = {
wif: `abc123` wif: 'abc123'
} }
const result = await utilRouteInst.sweepWif(req, res) const result = await utilRouteInst.sweepWif(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include( assert.include(
result.error, result.error,
"WIF needs to a proper compressed WIF starting with K or L", 'WIF needs to a proper compressed WIF starting with K or L',
"Proper error message" 'Proper error message'
) )
}) })
it("should throw 400 if destination address is not included", async () => { it('should throw 400 if destination address is not included', async () => {
req.body = { req.body = {
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt" wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt'
} }
const result = await utilRouteInst.sweepWif(req, res) const result = await utilRouteInst.sweepWif(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include( assert.include(
result.error, result.error,
"address can not be empty", 'address can not be empty',
"Proper error message" 'Proper error message'
) )
}) })
// Unit test only. // Unit test only.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
it("should generate transaction for valid token sweep", async () => { it('should generate transaction for valid token sweep', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
sandbox sandbox
.stub( .stub(
utilRouteInst.blockbook.testableComponents, utilRouteInst.blockbook.testableComponents,
"balanceFromBlockbook" 'balanceFromBlockbook'
) )
.resolves(mockData.mockBalance) .resolves(mockData.mockBalance)
sandbox sandbox
.stub( .stub(
utilRouteInst.blockbook.testableComponents, utilRouteInst.blockbook.testableComponents,
"utxosFromBlockbook" 'utxosFromBlockbook'
) )
.resolves(mockData.mockUtxos) .resolves(mockData.mockUtxos)
sandbox sandbox
.stub(utilRouteInst.bchjs.SLP.Utils, "tokenUtxoDetails") .stub(utilRouteInst.bchjs.SLP.Utils, 'tokenUtxoDetails')
.resolves(mockData.mockIsTokenUtxos) .resolves(mockData.mockIsTokenUtxos)
// Mock sendRawTransaction() so that the hex does not actually get broadcast // Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network. // to the network.
sandbox sandbox
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction") .stub(utilRouteInst.bchjs.RawTransactions, 'sendRawTransaction')
.resolves("test-txid") .resolves('test-txid')
req.body = { req.body = {
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt", wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt',
toAddr: "bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p" toAddr: 'bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p'
} }
const result = await utilRouteInst.sweepWif(req, res) const result = await utilRouteInst.sweepWif(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, "test-txid") assert.equal(result, 'test-txid')
}) })
} }
it("should return balance if balance-only is true", async () => { it('should return balance if balance-only is true', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
sandbox sandbox
.stub( .stub(
utilRouteInst.blockbook.testableComponents, utilRouteInst.blockbook.testableComponents,
"balanceFromBlockbook" 'balanceFromBlockbook'
) )
.resolves(mockData.mockBalance) .resolves(mockData.mockBalance)
} }
@@ -405,11 +405,11 @@ describe("#Util", () => {
// Mock sendRawTransaction() so that the hex does not actually get broadcast // Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network. // to the network.
sandbox sandbox
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction") .stub(utilRouteInst.bchjs.RawTransactions, 'sendRawTransaction')
.resolves("test-txid") .resolves('test-txid')
req.body = { req.body = {
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt", wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt',
balanceOnly: true balanceOnly: true
} }
@@ -420,125 +420,125 @@ describe("#Util", () => {
}) })
// Unit tests only // Unit tests only
if (process.env.TEST === "unit") { if (process.env.TEST === 'unit') {
it("should generate transaction for valid BCH-only sweep", async () => { it('should generate transaction for valid BCH-only sweep', async () => {
sandbox sandbox
.stub( .stub(
utilRouteInst.blockbook.testableComponents, utilRouteInst.blockbook.testableComponents,
"balanceFromBlockbook" 'balanceFromBlockbook'
) )
.resolves(mockData.mockBalance) .resolves(mockData.mockBalance)
sandbox sandbox
.stub( .stub(
utilRouteInst.blockbook.testableComponents, utilRouteInst.blockbook.testableComponents,
"utxosFromBlockbook" 'utxosFromBlockbook'
) )
.resolves(mockData.mockUtxos) .resolves(mockData.mockUtxos)
// Force token utxo to appear as regular BCH utxo. // Force token utxo to appear as regular BCH utxo.
sandbox sandbox
.stub(utilRouteInst.bchjs.SLP.Utils, "tokenUtxoDetails") .stub(utilRouteInst.bchjs.SLP.Utils, 'tokenUtxoDetails')
.resolves([false, false]) .resolves([false, false])
// Mock sendRawTransaction() so that the hex does not actually get broadcast // Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network. // to the network.
sandbox sandbox
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction") .stub(utilRouteInst.bchjs.RawTransactions, 'sendRawTransaction')
.resolves("test-txid") .resolves('test-txid')
req.body = { req.body = {
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt", wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt',
toAddr: "bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p" toAddr: 'bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p'
} }
const result = await utilRouteInst.sweepWif(req, res) const result = await utilRouteInst.sweepWif(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, "test-txid") assert.equal(result, 'test-txid')
}) })
it("should throw 422 error if no non-token UTXOs", async () => { it('should throw 422 error if no non-token UTXOs', async () => {
sandbox sandbox
.stub( .stub(
utilRouteInst.blockbook.testableComponents, utilRouteInst.blockbook.testableComponents,
"balanceFromBlockbook" 'balanceFromBlockbook'
) )
.resolves(mockData.mockBalance) .resolves(mockData.mockBalance)
sandbox sandbox
.stub( .stub(
utilRouteInst.blockbook.testableComponents, utilRouteInst.blockbook.testableComponents,
"utxosFromBlockbook" 'utxosFromBlockbook'
) )
.resolves(mockData.mockUtxos) .resolves(mockData.mockUtxos)
// Force token utxo to appear as regular BCH utxo. // Force token utxo to appear as regular BCH utxo.
sandbox sandbox
.stub(utilRouteInst.bchjs.SLP.Utils, "tokenUtxoDetails") .stub(utilRouteInst.bchjs.SLP.Utils, 'tokenUtxoDetails')
.resolves(mockData.tokensOnly) .resolves(mockData.tokensOnly)
// Mock sendRawTransaction() so that the hex does not actually get broadcast // Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network. // to the network.
sandbox sandbox
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction") .stub(utilRouteInst.bchjs.RawTransactions, 'sendRawTransaction')
.resolves("test-txid") .resolves('test-txid')
req.body = { req.body = {
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt", wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt',
toAddr: "bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p" toAddr: 'bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p'
} }
const result = await utilRouteInst.sweepWif(req, res) const result = await utilRouteInst.sweepWif(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(res.statusCode, 422) assert.equal(res.statusCode, 422)
assert.property(result, "error") assert.property(result, 'error')
assert.include( assert.include(
result.error, result.error,
"Tokens found, but no BCH UTXOs found. Add BCH to wallet to move tokens" 'Tokens found, but no BCH UTXOs found. Add BCH to wallet to move tokens'
) )
}) })
it("should detect and throw error for multiple token classes", async () => { it('should detect and throw error for multiple token classes', async () => {
sandbox sandbox
.stub( .stub(
utilRouteInst.blockbook.testableComponents, utilRouteInst.blockbook.testableComponents,
"balanceFromBlockbook" 'balanceFromBlockbook'
) )
.resolves(mockData.mockBalance) .resolves(mockData.mockBalance)
sandbox sandbox
.stub( .stub(
utilRouteInst.blockbook.testableComponents, utilRouteInst.blockbook.testableComponents,
"utxosFromBlockbook" 'utxosFromBlockbook'
) )
.resolves(mockData.mockThreeUtxos) .resolves(mockData.mockThreeUtxos)
// Force token utxo to appear as regular BCH utxo. // Force token utxo to appear as regular BCH utxo.
sandbox sandbox
.stub(utilRouteInst.bchjs.SLP.Utils, "tokenUtxoDetails") .stub(utilRouteInst.bchjs.SLP.Utils, 'tokenUtxoDetails')
.resolves(mockData.multipleTokens) .resolves(mockData.multipleTokens)
// Mock sendRawTransaction() so that the hex does not actually get broadcast // Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network. // to the network.
sandbox sandbox
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction") .stub(utilRouteInst.bchjs.RawTransactions, 'sendRawTransaction')
.resolves("test-txid") .resolves('test-txid')
req.body = { req.body = {
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt", wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt',
toAddr: "bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p" toAddr: 'bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p'
} }
const result = await utilRouteInst.sweepWif(req, res) const result = await utilRouteInst.sweepWif(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(res.statusCode, 422) assert.equal(res.statusCode, 422)
assert.property(result, "error") assert.property(result, 'error')
assert.include( assert.include(
result.error, result.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'
) )
}) })
} }
+28 -28
View File
@@ -6,30 +6,30 @@
to 'integration' to run the tests against BCH mainnet. to 'integration' to run the tests against BCH mainnet.
*/ */
"use strict" 'use strict'
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const xpubRoute = require("../../src/routes/v3/xpub") const xpubRoute = require('../../src/routes/v3/xpub')
const nock = require("nock") // HTTP mocking const nock = require('nock') // HTTP mocking
let originalUrl // Used during transition from integration to unit tests. // let originalUrl // Used during transition from integration to unit tests.
// Mocking data. // Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks") const { mockReq, mockRes } = require('./mocks/express-mocks')
const mockData = require("./mocks/address-mock") // const mockData = require('./mocks/address-mock')
// Used for debugging. // Used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
describe("#XPUBRouter", () => { describe('#XPUBRouter', () => {
let req, res let req, res
before(() => { before(() => {
// Set default environment variables for unit tests. // Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit" if (!process.env.TEST) process.env.TEST = 'unit'
//if (process.env.TEST === "unit") // if (process.env.TEST === "unit")
// process.env.BITCOINCOM_BASEURL = "http://fakeurl/api // process.env.BITCOINCOM_BASEURL = "http://fakeurl/api
}) })
@@ -55,44 +55,44 @@ describe("#XPUBRouter", () => {
}) })
after(() => { after(() => {
//process.env.BITCOINCOM_BASEURL = originalUrl // process.env.BITCOINCOM_BASEURL = originalUrl
}) })
describe("#root", () => { describe('#root', () => {
// root route handler. // root route handler.
const root = xpubRoute.testableComponents.root const root = xpubRoute.testableComponents.root
it("should respond to GET for base route", async () => { it('should respond to GET for base route', async () => {
const result = root(req, res) const result = root(req, res)
assert.equal(result.status, "address", "Returns static string") assert.equal(result.status, 'address', 'Returns static string')
}) })
}) })
describe("#FromXPubSingle", () => { describe('#FromXPubSingle', () => {
// details route handler. // details route handler.
const fromXPubSingle = xpubRoute.testableComponents.fromXPubSingle const fromXPubSingle = xpubRoute.testableComponents.fromXPubSingle
it("should throw 400 if xpub is empty", async () => { it('should throw 400 if xpub is empty', async () => {
const result = await fromXPubSingle(req, res) const result = await fromXPubSingle(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"]) assert.hasAllKeys(result, ['error'])
assert.include(result.error, "xpub can not be empty") assert.include(result.error, 'xpub can not be empty')
}) })
it("should error on an array", async () => { it('should error on an array', async () => {
req.params.xpub = [ req.params.xpub = [
`tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM` 'tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM'
] ]
const result = await fromXPubSingle(req, res) const result = await fromXPubSingle(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include( assert.include(
result.error, result.error,
"xpub can not be an array", 'xpub can not be an array',
"Proper error message" 'Proper error message'
) )
}) })
/* /*
@@ -118,8 +118,8 @@ describe("#XPUBRouter", () => {
} }
}) })
*/ */
it("should create an address from xpub", async () => { it('should create an address from xpub', async () => {
req.params.xpub = `tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM` req.params.xpub = 'tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM'
// Mock the Insight URL for unit tests. // Mock the Insight URL for unit tests.
// TODO add unit test // TODO add unit test
@@ -133,7 +133,7 @@ describe("#XPUBRouter", () => {
// Call the details API. // Call the details API.
const result = await fromXPubSingle(req, res) const result = await fromXPubSingle(req, res)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
// Assert that required fields exist in the returned object. // Assert that required fields exist in the returned object.
assert.exists(result.legacyAddress) assert.exists(result.legacyAddress)