mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-21 16:52:04 -07:00
fix(linting): Switching to standard linter. Fixed linting errors
This commit is contained in:
+6
-50
@@ -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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1164
-284
File diff suppressed because it is too large
Load Diff
+3
-2
@@ -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
@@ -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
@@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
@@ -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) })
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) })
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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,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
@@ -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' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
File diff suppressed because it is too large
Load Diff
+94
-87
@@ -1,50 +1,50 @@
|
|||||||
"use strict"
|
'use strict'
|
||||||
|
|
||||||
const express = require("express")
|
const express = require('express')
|
||||||
const router = express.Router()
|
const 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
@@ -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
@@ -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/`
|
||||||
|
|||||||
@@ -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
File diff suppressed because it is too large
Load Diff
+536
-536
File diff suppressed because it is too large
Load Diff
+42
-42
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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
@@ -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 = {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,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 = {
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
}
|
// }
|
||||||
|
|||||||
+293
-291
File diff suppressed because it is too large
Load Diff
+186
-187
@@ -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
@@ -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
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user