Compare commits

...
25 Commits
Author SHA1 Message Date
Chris Troutner ab198b958e Merge pull request #154 from Permissionless-Software-Foundation/ct-unstable
fix(rate limits): Adding object for debugging rate limit issues
2021-08-07 11:35:29 -07:00
Chris Troutner 2618008247 fix(rate limits): Adding object for debugging rate limit issues 2021-08-07 11:32:04 -07:00
Chris Troutner df27ba2063 Merge pull request #153 from Permissionless-Software-Foundation/ct-unstable
Fixing unit tests
2021-08-03 11:41:02 -07:00
Chris Troutner f19e70ae40 feat(sweep): Removing sweep utility from v5/util route 2021-08-03 11:36:23 -07:00
Chris Troutner 63fa97c998 fix(electrumx): Fixing unit tests for v5 Electrumx route 2021-08-03 11:30:18 -07:00
Chris Troutner 61b1b8ebdf Merge pull request #152 from Permissionless-Software-Foundation/ct-unstable
fix(eCash): Fixing price feed
2021-08-03 09:35:46 -07:00
Chris Troutner de6c19e877 fix(eCash): Fixing price feed 2021-08-03 09:23:10 -07:00
Chris Troutner 66efb76f94 fix(v5): Changed references for v5 route documentation to v5 2021-08-03 09:00:26 -07:00
Chris Troutner 1250a40ff0 Merge pull request #151 from Permissionless-Software-Foundation/ct-unstable
feat(jwt): Created new route for debugging JWT tokens
2021-07-12 10:45:04 -07:00
Chris Troutner 6786ca9ee5 feat(jwt): Created new route for debugging JWT tokens 2021-07-12 10:44:07 -07:00
Chris Troutner e96b9e7a44 Merge pull request #149 from Permissionless-Software-Foundation/ct-unstable
fix(v5 Fulcrum): Requiring FULCRUM_API env var when starting server
2021-06-16 18:48:12 -07:00
Chris Troutner d29f99965f fix(v5 Fulcrum): Requiring FULCRUM_API env var when starting server 2021-06-16 18:33:12 -07:00
Chris Troutner 9825ec1bab Merge pull request #147 from Permissionless-Software-Foundation/dh-v5-dsproof-endpoint
feat(dsproof): Created v5 GET dsproof endpoint
2021-06-09 07:38:37 -07:00
Chris Troutner fb61cfeca3 Merge branch 'master' into dh-v5-dsproof-endpoint 2021-06-09 07:08:52 -07:00
Chris Troutner 5f9a635ab5 Merge pull request #146 from Permissionless-Software-Foundation/dh-v5-fulcrum-unconfirmed
feat(unconfirmed): Created v5 Fulcrum /unconfirmed endpoints
2021-06-09 07:08:16 -07:00
Daniel Gonzalez 41c5504846 feat(dsproof): Created v5 GET dsproof endpoint 2021-06-08 20:45:03 -04:00
Daniel Gonzalez e5f9455fbb feat(unconfirmed): Created v5 Fulcrum /unconfirmed endpoints 2021-06-07 23:39:51 -04:00
Chris Troutner 20cb48caff Merge pull request #145 from Permissionless-Software-Foundation/dh-v5-fulcrum-transactions
feat(txs): Created v5 Fulcrum /transactions endpoints in bch-api
2021-06-05 08:35:26 -07:00
Daniel Gonzalez f241aed5b2 feat(txs): Created v5 Fulcrum /transactions endpoints in bch-api 2021-06-04 21:43:45 -04:00
Chris Troutner 87c2faa6af Merge pull request #144 from Permissionless-Software-Foundation/dh-v5-fulcrum-block
feat(block): Created v5 Fulcrum /block endpoints in bch-api
2021-06-04 08:07:05 -07:00
Daniel Gonzalez 10a840f48d feat(block): Created v5 Fulcrum /block endpoints in bch-api 2021-06-02 23:43:25 -04:00
Chris Troutner 61f9531046 Merge pull request #143 from Permissionless-Software-Foundation/dh-fulcrum-tx-endpoint
feat(tx): Created v5 Fulcrum /tx endpoints in bch-api
2021-06-02 07:10:45 -07:00
Daniel Gonzalez 1ab408a757 feat(tx): Created v5 Fulcrum /tx endpoints in bch-api 2021-06-01 22:20:57 -04:00
Chris Troutner 84df2be3b8 Merge pull request #142 from Permissionless-Software-Foundation/dh-v5-utxo
feat(utxo): Created v5 Fulcrum utxo endpoints in bch-api
2021-05-29 06:51:37 -07:00
Daniel Gonzalez 17376d9dba feat(utxo): Created v5 Fulcrum utxo endpoints in bch-api 2021-05-28 21:12:21 -04:00
21 changed files with 2489 additions and 1402 deletions
+5 -5
View File
@@ -14,7 +14,7 @@
"scripts": {
"start": "node ./src/app.js",
"dev": "nodemon ./dist/app.js",
"test": "npm run lint && npm run test-v4",
"test": "npm run lint && npm run test-v5",
"lint": "standard --env mocha --fix",
"test-v4": "export NETWORK=mainnet && nyc --reporter=text mocha --exit --timeout 60000 test/v4/",
"test-v5": "export NETWORK=mainnet && nyc --reporter=text mocha --exit --timeout 60000 test/v5/",
@@ -23,9 +23,9 @@
"test:integration:nft": "mocha --timeout 25000 -g '#nft' test/v4/integration/nft.js",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "export NETWORK=mainnet && nyc --reporter=html mocha --timeout 25000 test/v4/",
"docs": "./node_modules/.bin/apidoc -i src/routes/v4 -o docs",
"test:temp1": "export NETWORK=mainnet && export TEST=integration && mocha -g '#balanceBulk' --exit --timeout 30000 test/v5/",
"test:temp2": "export NETWORK=mainnet && mocha -g '#balanceBulk' --exit --timeout 30000 test/v5/"
"docs": "./node_modules/.bin/apidoc -i src/routes/v5 -o docs",
"test:temp1": "export NETWORK=mainnet && export TEST=integration && mocha -g '#utxosBulk' --exit --timeout 30000 test/v5/",
"test:temp2": "export NETWORK=mainnet && mocha -g '#utxosBulk' --exit --timeout 30000 test/v5/"
},
"engines": {
"node": ">=10.15.1"
@@ -86,6 +86,6 @@
},
"apidoc": {
"title": "bch-api",
"url": "https://api.fullstack.cash/v4"
"url": "https://api.fullstack.cash/v5"
}
}
+6 -1
View File
@@ -46,12 +46,14 @@ const ControlV5 = require('./routes/v5/full-node/control')
const MiningV5 = require('./routes/v5/full-node/mining')
const networkV5 = require('./routes/v5/full-node/network')
const RawtransactionsV5 = require('./routes/v5/full-node/rawtransactions')
const DSProofV5 = require('./routes/v5/full-node/dsproof')
const UtilV5 = require('./routes/v5/util')
const SlpV5 = require('./routes/v5/slp')
const xpubV5 = require('./routes/v5/xpub')
const ElectrumXV5 = require('./routes/v5/electrumx')
const EncryptionV5 = require('./routes/v5/encryption')
const PriceV5 = require('./routes/v5/price')
const JWTV5 = require('./routes/v5/jwt')
// const Ninsight = require('./routes/v5/ninsight')
require('dotenv').config()
@@ -78,7 +80,8 @@ const electrumxv5 = new ElectrumXV5()
const encryptionv5 = new EncryptionV5()
const pricev5 = new PriceV5()
const utilV5 = new UtilV5({ electrumx: electrumxv5 })
const dsproofV5 = new DSProofV5()
const jwtV5 = new JWTV5()
const app = express()
app.locals.env = process.env
@@ -187,6 +190,8 @@ app.use(`/${v5prefix}/` + 'electrumx', electrumxv5.router)
app.use(`/${v5prefix}/` + 'encryption', encryptionv5.router)
app.use(`/${v5prefix}/` + 'price', pricev5.router)
app.use(`/${v5prefix}/` + 'util', utilV5.router)
app.use(`/${v5prefix}/` + 'dsproof', dsproofV5.router)
app.use(`/${v5prefix}/` + 'jwt', jwtV5.router)
// const ninsight = new Ninsight()
app.use(`/${v5prefix}/` + 'ninsight', ninsight.router)
+11
View File
@@ -220,11 +220,16 @@ class RateLimits {
// it will return the 'res' object with an error status and message, which
// should be returned by the middleware.
async trackRateLimits (req, res, jwtToken) {
const debugInfo = {
jwtToken
}
// Anonymous rate limits are used by default.
let pointsToConsume = ANON_LIMITS
// console.log('pointsToConsume: ', pointsToConsume)
let key = req.ip // Use the IP address as the key, by default.
debugInfo.ip = req.ip
// console.log('jwtToken: ', jwtToken)
@@ -236,8 +241,10 @@ class RateLimits {
// Preferentially use the decoded ID in the JWT payload, as the key.
key = decoded.id
debugInfo.id = key
pointsToConsume = decoded.pointsToConsume
debugInfo.pointsToConsume = pointsToConsume
}
// console.log(`rate limit key: ${key}`)
@@ -261,6 +268,10 @@ class RateLimits {
res.locals.rateLimitTriggered = true
// console.log('res.locals: ', res.locals)
console.log(
`rate limit debug info: ${JSON.stringify(debugInfo, null, 2)}`
)
// Rate limited was triggered
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
+1 -1
View File
@@ -25,7 +25,7 @@ class Price {
this.priceUrl = 'https://api.coinbase.com/v2/exchange-rates?currency=BCH'
this.coinexPriceUrl =
'https://api.coinex.com/v1/market/ticker?market=bchausdt'
'https://api.coinex.com/v1/market/ticker?market=xecusdt'
this.bchCoinexPriceUrl =
'https://api.coinex.com/v1/market/ticker?market=bchusdt'
+548 -662
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -18,7 +18,7 @@ const routeUtils = new RouteUtils()
const BCHJS = require('@psf/bch-js')
const restURL = process.env.LOCAL_RESTURL
? process.env.LOCAL_RESTURL
: 'https://api.fullstack.cash/v4/'
: 'https://api.fullstack.cash/v5/'
const bchjs = new BCHJS({ restURL })
let _this
@@ -76,7 +76,7 @@ class Encryption {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/encryption/publickey/bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/encryption/publickey/bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" -H "accept: application/json"
*
*/
async getPublicKey (req, res, next) {
+17 -17
View File
@@ -81,7 +81,7 @@ class Blockchain {
* block chain.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getBestBlockHash" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getBestBlockHash" -H "accept: application/json"
*
* @apiSuccess {String} bestBlockHash 000000000000000002bc884334336d99c9a9c616670a9244c6a8c1fc35aa91a1
*/
@@ -112,7 +112,7 @@ class Blockchain {
* @apiDescription Returns an object containing various state info regarding blockchain processing.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getBlockchainInfo" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getBlockchainInfo" -H "accept: application/json"
*
* @apiSuccess {Object} object Object containing data
* @apiSuccess {String} object.chain "main"
@@ -156,7 +156,7 @@ class Blockchain {
* @apiDescription Returns the number of blocks in the longest blockchain.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getBlockCount" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getBlockCount" -H "accept: application/json"
*
* @apiSuccess {Number} bestBlockCount 587665
*/
@@ -189,7 +189,7 @@ class Blockchain {
* returns an Object with information about blockheader hash.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getBlockHeader/000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201?verbose=true" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getBlockHeader/000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201?verbose=true" -H "accept: application/json"
*
* @apiParam {String} hash block hash
* @apiParam {Boolean} verbose Return verbose data
@@ -250,7 +250,7 @@ class Blockchain {
* returns an Object with information about blockheader hash.
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/blockchain/getBlockHeader" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"hashes\":[\"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201\",\"00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3\"],\"verbose\":true}"
* curl -X POST "https://api.fullstack.cash/v5/blockchain/getBlockHeader" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"hashes\":[\"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201\",\"00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3\"],\"verbose\":true}"
*
* @apiParam {String} hash block hash
* @apiParam {Boolean} verbose Return verbose data
@@ -342,7 +342,7 @@ class Blockchain {
* including the main chain as well as orphaned branches.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getChainTips" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getChainTips" -H "accept: application/json"
*
*/
async getChainTips (req, res, next) {
@@ -373,7 +373,7 @@ class Blockchain {
* power on the network.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getDifficulty" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getDifficulty" -H "accept: application/json"
*
*/
async getDifficulty (req, res, next) {
@@ -405,7 +405,7 @@ class Blockchain {
* mempool (unconfirmed)
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getMempoolEntry/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getMempoolEntry/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
*
*/
async getMempoolEntrySingle (req, res, next) {
@@ -443,7 +443,7 @@ class Blockchain {
* @apiDescription Returns mempool data for multiple transactions
*
* @apiExample Example usage:
* curl -X POST https://api.fullstack.cash/v4/blockchain/getMempoolEntry -H "Content-Type: application/json" -d "{\"txids\":[\"a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1\",\"5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e\"]}"
* curl -X POST https://api.fullstack.cash/v5/blockchain/getMempoolEntry -H "Content-Type: application/json" -d "{\"txids\":[\"a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1\",\"5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e\"]}"
*/
async getMempoolEntryBulk (req, res, next) {
try {
@@ -516,7 +516,7 @@ class Blockchain {
* against the 25 ancestor chain-limit.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getMempoolAncestors/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getMempoolAncestors/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
*
*/
async getMempoolAncestorsSingle (req, res, next) {
@@ -558,7 +558,7 @@ class Blockchain {
* @apiDescription Returns details on the active state of the TX memory pool.
*
* @apiExample Example usage:
* curl -X GET https://api.fullstack.cash/v4/getMempoolInfo -H "accept: application/json"
* curl -X GET https://api.fullstack.cash/v5/getMempoolInfo -H "accept: application/json"
*
*/
async getMempoolInfo (req, res, next) {
@@ -588,7 +588,7 @@ class Blockchain {
* @apiDescription Returns details on the active state of the TX memory pool.
*
* @apiExample Example usage:
* curl -X GET https://api.fullstack.cash/v4/getMempoolInfo -H "accept: application/json"
* curl -X GET https://api.fullstack.cash/v5/getMempoolInfo -H "accept: application/json"
*
*/
@@ -600,7 +600,7 @@ class Blockchain {
* of string transaction ids.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/getRawMempool/?verbose=true" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/getRawMempool/?verbose=true" -H "accept: application/json"
*
* @apiParam {Boolean} verbose Return verbose data
*
@@ -636,7 +636,7 @@ class Blockchain {
* @apiDescription Returns details about an unspent transaction output.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getTxOut/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33/0?mempool=false" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getTxOut/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33/0?mempool=false" -H "accept: application/json"
*
* @apiParam {String} txid Transaction id (required)
* @apiParam {Number} n Output number (required)
@@ -693,7 +693,7 @@ class Blockchain {
* @apiDescription Returns details about an unspent transaction output (UTXO).
*
* @apiExample Example usage:
* curl "https://api.fullstack.cash/v4/blockchain/getTxOut/" -X POST -H "Content-Type: application/json" --data-binary '{"txid":"d5228d2cdc77fbe5a9aa79f19b0933b6802f9f0067f42847fc4fe343664723e5","vout":0,"mempool":true}'
* curl "https://api.fullstack.cash/v5/blockchain/getTxOut/" -X POST -H "Content-Type: application/json" --data-binary '{"txid":"d5228d2cdc77fbe5a9aa79f19b0933b6802f9f0067f42847fc4fe343664723e5","vout":0,"mempool":true}'
*
* @apiParam {String} txid Transaction id (required)
* @apiParam {Number} vout of transaction (required)
@@ -747,7 +747,7 @@ class Blockchain {
* @apiDescription Returns a hex-encoded proof that 'txid' was included in a block.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/blockchain/getTxOutProofSingle/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/blockchain/getTxOutProofSingle/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
*
* @apiParam {String} txid Transaction id (required)
*
@@ -946,7 +946,7 @@ class Blockchain {
* @apiDescription Returns block details
*
* @apiExample Example usage:
* curl "https://api.fullstack.cash/v4/blockchain/getblock/" -X POST -H "Content-Type: application/json" --data-binary '{"blockhash":"000000000000000002a5fe0bdd6e3f04342a975c0f55e57f97e73bb90041676b","verbosity":0 }'
* curl "https://api.fullstack.cash/v5/blockchain/getblock/" -X POST -H "Content-Type: application/json" --data-binary '{"blockhash":"000000000000000002a5fe0bdd6e3f04342a975c0f55e57f97e73bb90041676b","verbosity":0 }'
*
* @apiParam {String} blockhash Block hash (required)
* @apiParam {Number} verbosity Default 1 (optional)
+1 -37
View File
@@ -53,7 +53,7 @@ class Control {
* @apiDescription RPC call which gets basic full node information.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/control/getnetworkinfo" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/control/getnetworkinfo" -H "accept: application/json"
*
*/
async getNetworkInfo (req, res, next) {
@@ -75,42 +75,6 @@ class Control {
return _this.errorHandler(error, res)
}
}
// router.get('/getMemoryInfo', (req, res, next) => {
// BitboxHTTP({
// method: 'post',
// auth: {
// username: username,
// password: password
// },
// data: {
// jsonrpc: "1.0",
// id:"getmemoryinfo",
// method: "getmemoryinfo"
// }
// })
// .then((response) => {
// res.json(response.data.result);
// })
// .catch((error) => {
// res.send(error.response.data.error.message);
// });
// });
//
// router.get('/help', (req, res, next) => {
// BITBOX.Control.help()
// .then((result) => {
// res.json(result);
// }, (err) => { console.log(err);
// });
// });
//
// router.post('/stop', (req, res, next) => {
// BITBOX.Control.stop()
// .then((result) => {
// res.json(result);
// }, (err) => { console.log(err);
// });
// });
}
module.exports = Control
+95
View File
@@ -0,0 +1,95 @@
'use strict'
const express = require('express')
const router = express.Router()
const axios = require('axios')
const wlogger = require('../../../util/winston-logging')
const RouteUtils = require('../../../util/route-utils')
const routeUtils = new RouteUtils()
// Used to convert error messages to strings, to safely pass to users.
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
let _this
class DSProof {
constructor () {
_this = this
_this.axios = axios
_this.routeUtils = routeUtils
_this.router = router
_this.router.get('/', this.root)
_this.router.get('/getdsproof/:txid', _this.getDSProof)
}
// DRY error handler.
errorHandler (err, res) {
// Attempt to decode the error message.
const { msg, status } = _this.routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
res.status(500)
return res.json({ error: util.inspect(err) })
}
root (req, res, next) {
return res.json({ status: 'dsproof' })
}
/**
* @api {get} /dsproof/getdsproof/:txid Get Double-Spend Proof.
* @apiName DS Proof.
* @apiGroup DSProof
* @apiDescription Get information for a double-spend proof.
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v5/dsproof/getdsproof/a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d" -H "accept: application/json"
*
*
*/
async getDSProof (req, res, next) {
try {
const txid = req.params.txid
let verbose = 2 // default
if (req.query.verbose === 'true') verbose = 3
if (!txid || txid === '') {
res.status(400)
return res.json({
success: false,
error: 'txid can not be empty'
})
}
if (txid.length !== 64) {
res.status(400)
return res.json({
success: false,
error: `txid must be of length 64 (not ${txid.length})`
})
}
const options = _this.routeUtils.getAxiosOptions()
options.data.id = 'getdsproof'
options.data.method = 'getdsproof'
options.data.params = [txid, verbose]
const response = await _this.axios.request(options)
return res.json(response.data.result)
} catch (err) {
wlogger.error('Error in dsproof.js/getDSProof().', err)
return _this.errorHandler(err, res)
}
}
}
module.exports = DSProof
+2 -2
View File
@@ -77,7 +77,7 @@ class Mining {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/mining/getMiningInfo" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/mining/getMiningInfo" -H "accept: application/json"
*
*
*/
@@ -107,7 +107,7 @@ class Mining {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/mining/getNetworkHashps?nblocks=120&height=-1" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/mining/getNetworkHashps?nblocks=120&height=-1" -H "accept: application/json"
*
*
*/
+8 -8
View File
@@ -65,7 +65,7 @@ class RawTransactions {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/rawtransactions/decodeRawTransaction/02000000010e991f7ccec410f27d333f737f149b5d3be6728687da81072e638aed0063a176010000006b483045022100cd20443b0af090053450bc4ab00d563d4ac5955bb36e0135b00b8a96a19f233302205047f2c70a08c6ef4b76f2d198b33a31d17edfaa7e1e9e865894da0d396009354121024d4e7f522f67105b7bf5f9dbe557e7b2244613fdfcd6fe09304f93877328f6beffffffff02a0860100000000001976a9140ee020c07f39526ac5505c54fa1ab98490979b8388acb5f0f70b000000001976a9143a9b2b0c12fe722fcf653b6ef5dcc38732d6ff5188ac00000000" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/rawtransactions/decodeRawTransaction/02000000010e991f7ccec410f27d333f737f149b5d3be6728687da81072e638aed0063a176010000006b483045022100cd20443b0af090053450bc4ab00d563d4ac5955bb36e0135b00b8a96a19f233302205047f2c70a08c6ef4b76f2d198b33a31d17edfaa7e1e9e865894da0d396009354121024d4e7f522f67105b7bf5f9dbe557e7b2244613fdfcd6fe09304f93877328f6beffffffff02a0860100000000001976a9140ee020c07f39526ac5505c54fa1ab98490979b8388acb5f0f70b000000001976a9143a9b2b0c12fe722fcf653b6ef5dcc38732d6ff5188ac00000000" -H "accept: application/json"
*/
async decodeRawTransactionSingle (req, res, next) {
try {
@@ -103,7 +103,7 @@ class RawTransactions {
*
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/rawtransactions/decodeRawTransaction" -H "accept: application/json" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
* curl -X POST "https://api.fullstack.cash/v5/rawtransactions/decodeRawTransaction" -H "accept: application/json" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
*
*
*/
@@ -177,7 +177,7 @@ class RawTransactions {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/rawtransactions/decodeScript/4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/rawtransactions/decodeScript/4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" -H "accept: application/json"
*
*
*/
@@ -218,7 +218,7 @@ class RawTransactions {
*
*
* @apiExample Example usage:
*curl -X POST "https://api.fullstack.cash/v4/rawtransactions/decodeScript" -H "accept:" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
*curl -X POST "https://api.fullstack.cash/v5/rawtransactions/decodeScript" -H "accept:" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
*
*
*/
@@ -308,7 +308,7 @@ class RawTransactions {
*
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/rawtransactions/getRawTransaction" -H "accept: application/json" -H "Content-Type: application/json" -d '{"txids":["a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1","5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e"],"verbose":true}'
* curl -X POST "https://api.fullstack.cash/v5/rawtransactions/getRawTransaction" -H "accept: application/json" -H "Content-Type: application/json" -d '{"txids":["a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1","5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e"],"verbose":true}'
*
*/
async getRawTransactionBulk (req, res, next) {
@@ -375,7 +375,7 @@ class RawTransactions {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/rawtransactions/getRawTransaction/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33?verbose=true" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/rawtransactions/getRawTransaction/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33?verbose=true" -H "accept: application/json"
*
*
*/
@@ -421,7 +421,7 @@ class RawTransactions {
*
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/rawtransactions/sendRawTransaction" -H "accept:application/json" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
* curl -X POST "https://api.fullstack.cash/v5/rawtransactions/sendRawTransaction" -H "accept:application/json" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
*
*
*/
@@ -515,7 +515,7 @@ class RawTransactions {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/rawtransactions/sendRawTransaction/01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" -H "accept: "
* curl -X GET "https://api.fullstack.cash/v5/rawtransactions/sendRawTransaction/01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" -H "accept: "
*
*
*/
+95
View File
@@ -0,0 +1,95 @@
/*
Utility functions for troubleshooting a JWT token.
*/
'use strict'
// Public npm libraries.
const jwt = require('jsonwebtoken')
const axios = require('axios')
const express = require('express')
const router = express.Router()
const config = require('../../../config')
const wlogger = require('../../util/winston-logging')
const RouteUtils = require('../../util/route-utils')
const routeUtils = new RouteUtils()
// Used for processing error messages before sending them to the user.
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
let _this
class JWT {
constructor () {
_this = this
_this.axios = axios
_this.routeUtils = routeUtils
_this.jwt = jwt
_this.config = config
_this.router = router
_this.router.get('/', _this.root)
_this.router.post('/info', _this.jwtInfo)
}
root (req, res, next) {
return res.json({ status: 'jwt' })
}
// DRY error handler.
errorHandler (err, res) {
// Attempt to decode the error message.
const { msg, status } = _this.routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
res.status(500)
return res.json({ error: util.inspect(err) })
}
/**
* @api {post} /jwt/info Get JWT Info
* @apiName GetJWTInfo
* @apiGroup JWT
* @apiDescription
* Get info on your JWT token. Useful for debugging rate limit issues with your
* JWT token.
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v5/jwt/info" -H "accept: application/json" -H "Content-Type: application/json" -d '{"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwZGRmZmI2NzRhM2Q2MDAxOTY3NjE1NCIsImVtYWlsIjoiY2hyaXNAYmNodGVzdC5uZXQiLCJhcGlMZXZlbCI6NjAsInJhdGVMaW1pdCI6MywicG9pbnRzVG9Db25zdW1lIjoxNiwiZHVyYXRpb24iOjMwLCJpYXQiOjE2MjU0MzQ2MzYsImv5cCI6MTYyODAyNjYzNn0.1WLugTkQKVG0yMXD1h5nxfho3gRzvSvs8NMa9obVhPM"}'
*
*/
async jwtInfo (req, res, next) {
try {
const jwtIn = req.body.jwt
// console.log('jwt: ', jwtIn)
// console.log('_this.config.apiTokenSecret: ', _this.config.apiTokenSecret)
const decoded = _this.jwt.verify(jwtIn, _this.config.apiTokenSecret)
// console.log('decoded: ', decoded)
const expiration = new Date(decoded.exp * 1000)
decoded.expiration = expiration.toISOString()
const createdAt = new Date(decoded.iat * 1000)
decoded.createdAt = createdAt.toISOString()
res.status(200)
return res.json(decoded)
} catch (error) {
// console.log('Error in jwt.js/jwtInfo().', error)
wlogger.error('Error in jwt.js/jwtInfo().', error)
return _this.errorHandler(error, res)
}
}
}
module.exports = JWT
+5 -5
View File
@@ -25,7 +25,7 @@ class Price {
this.priceUrl = 'https://api.coinbase.com/v2/exchange-rates?currency=BCH'
this.coinexPriceUrl =
'https://api.coinex.com/v1/market/ticker?market=bchausdt'
'https://api.coinex.com/v1/market/ticker?market=xecusdt'
this.bchCoinexPriceUrl =
'https://api.coinex.com/v1/market/ticker?market=bchusdt'
@@ -64,7 +64,7 @@ class Price {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/price/usd" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/price/usd" -H "accept: application/json"
*
*/
async getUSD (req, res, next) {
@@ -96,7 +96,7 @@ class Price {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/price/rates" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/price/rates" -H "accept: application/json"
*
*/
// Get rates for several different currencies
@@ -129,7 +129,7 @@ class Price {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/price/bchausd" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/price/bchausd" -H "accept: application/json"
*
*/
async getBCHAUSD (req, res, next) {
@@ -163,7 +163,7 @@ class Price {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/price/bchusd" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/price/bchusd" -H "accept: application/json"
*
*/
async getBCHUSD (req, res, next) {
+25 -25
View File
@@ -16,7 +16,7 @@ const wlogger = require('../../util/winston-logging')
// Instantiate a local copy of bch-js using the local REST API server.
const LOCAL_RESTURL = process.env.LOCAL_RESTURL
? process.env.LOCAL_RESTURL
: 'https://api.fullstack.cash/v4/'
: 'https://api.fullstack.cash/v5/'
const BCHJS = require('@psf/bch-js')
// const BCHJS = require('../../../../bch-js')
@@ -51,10 +51,10 @@ const rawTransactions = new RawTransactions()
// Setup REST and TREST URLs used by slpjs
// Dev note: this allows for unit tests to mock the URL.
if (!process.env.REST_URL) {
process.env.REST_URL = 'https://bchn.fullstack.cash/v4/'
process.env.REST_URL = 'https://bchn.fullstack.cash/v5/'
}
if (!process.env.TREST_URL) {
process.env.TREST_URL = 'https://testnet.fullstack.cash/v4/'
process.env.TREST_URL = 'https://testnet.fullstack.cash/v5/'
}
let _this
@@ -181,7 +181,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/list/259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/list/259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1" -H "accept:application/json"
*
*
*/
@@ -214,7 +214,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/slp/list" -H "accept:application/json" -H "Content-Type: application/json" -d '{"tokenIds":["7380843cd1089a1a01783f86af37734dc99667a1cdc577391b5f6ea42fc1bfb4","9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0"]}'
* curl -X POST "https://api.fullstack.cash/v5/slp/list" -H "accept:application/json" -H "Content-Type: application/json" -d '{"tokenIds":["7380843cd1089a1a01783f86af37734dc99667a1cdc577391b5f6ea42fc1bfb4","9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0"]}'
*
*
*/
@@ -362,7 +362,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/balancesForAddress/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/balancesForAddress/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
*
*
*/
@@ -546,7 +546,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/slp/balancesForAddress" -d "{\"addresses\":[\"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57\"]}" -H "accept:application/json"
* curl -X POST "https://api.fullstack.cash/v5/slp/balancesForAddress" -d "{\"addresses\":[\"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57\"]}" -H "accept:application/json"
*
*
*/
@@ -761,7 +761,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/balancesForToken/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/balancesForToken/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
*
*
*/
@@ -868,7 +868,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/convert/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/convert/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
*
*
*/
@@ -914,7 +914,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/slp/convert" -H "accept:application/json" -H "Content-Type: application/json" -d '{"addresses":["simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l"]}'
* curl -X POST "https://api.fullstack.cash/v5/slp/convert" -H "accept:application/json" -H "Content-Type: application/json" -d '{"addresses":["simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l"]}'
*
*
*/
@@ -976,7 +976,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/slp/validateTxid" -H "accept:application/json" -H "Content-Type: application/json" -d '{"txids":["f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a","fb0eeaa501a6e1acb721669c62a3f70741f48ae0fd7f4b8e1d72088785c51952"]}'
* curl -X POST "https://api.fullstack.cash/v5/slp/validateTxid" -H "accept:application/json" -H "Content-Type: application/json" -d '{"txids":["f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a","fb0eeaa501a6e1acb721669c62a3f70741f48ae0fd7f4b8e1d72088785c51952"]}'
*
*
*/
@@ -1122,7 +1122,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/validateTxid/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/validateTxid/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
*
*
*/
@@ -1203,7 +1203,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/validateTxid2/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/validateTxid2/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
*
*
*/
@@ -1268,7 +1268,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/whitelist" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/whitelist" -H "accept:application/json"
*
*/
async getSlpWhitelist (req, res, next) {
@@ -1321,7 +1321,7 @@ class Slp {
* /slp/whitelist endpoint.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/validateTxid3/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/validateTxid3/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
*
*
*/
@@ -1403,7 +1403,7 @@ class Slp {
* /slp/whitelist endpoint.
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/slp/validateTxid3" -H "accept:application/json" -H "Content-Type: application/json" -d '{"txids":["f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a","fb0eeaa501a6e1acb721669c62a3f70741f48ae0fd7f4b8e1d72088785c51952"]}'
* curl -X POST "https://api.fullstack.cash/v5/slp/validateTxid3" -H "accept:application/json" -H "Content-Type: application/json" -d '{"txids":["f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a","fb0eeaa501a6e1acb721669c62a3f70741f48ae0fd7f4b8e1d72088785c51952"]}'
*
*
*/
@@ -1554,7 +1554,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/txDetails/8ab4ac5dea3f9024e3954ee5b61452955d659a34561f79ef62ac44e133d0980e" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/txDetails/8ab4ac5dea3f9024e3954ee5b61452955d659a34561f79ef62ac44e133d0980e" -H "accept:application/json"
*
*
*/
@@ -1646,7 +1646,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/tokenStats/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/tokenStats/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
*
*
*/
@@ -1677,7 +1677,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/transactions/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0/simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/transactions/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0/simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l" -H "accept:application/json"
*
*
*/
@@ -1901,7 +1901,7 @@ class Slp {
* (1 or 2) will also be returned.
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/slp/generateSendOpReturn" -H "accept:application/json" -H "Content-Type: application/json" -d '{"tokenUtxos":[{"tokenId": "0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1","decimals": 0, "tokenQty": 2}], "sendQty": 1.5}'
* curl -X POST "https://api.fullstack.cash/v5/slp/generateSendOpReturn" -H "accept:application/json" -H "Content-Type: application/json" -d '{"tokenUtxos":[{"tokenId": "0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1","decimals": 0, "tokenQty": 2}], "sendQty": 1.5}'
*
*
*/
@@ -1986,7 +1986,7 @@ class Slp {
* not been confirmed.
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/slp/hydrateUtxos" -H "accept:application/json" -H "Content-Type: application/json" -d '{"utxos":[{"utxos":[{"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 3, "value": "6816", "height": 606848, "confirmations": 13, "satoshis": 6816}, {"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 2, "value": "546", "height": 606848, "confirmations": 13, "satoshis": 546}]}]}'
* curl -X POST "https://api.fullstack.cash/v5/slp/hydrateUtxos" -H "accept:application/json" -H "Content-Type: application/json" -d '{"utxos":[{"utxos":[{"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 3, "value": "6816", "height": 606848, "confirmations": 13, "satoshis": 6816}, {"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 2, "value": "546", "height": 606848, "confirmations": 13, "satoshis": 546}]}]}'
*
*
*/
@@ -2095,7 +2095,7 @@ class Slp {
* SLP tokens.
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/slp/hydrateUtxosWL" -H "accept:application/json" -H "Content-Type: application/json" -d '{"utxos":[{"utxos":[{"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 3, "value": "6816", "height": 606848, "confirmations": 13, "satoshis": 6816}, {"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 2, "value": "546", "height": 606848, "confirmations": 13, "satoshis": 546}]}]}'
* curl -X POST "https://api.fullstack.cash/v5/slp/hydrateUtxosWL" -H "accept:application/json" -H "Content-Type: application/json" -d '{"utxos":[{"utxos":[{"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 3, "value": "6816", "height": 606848, "confirmations": 13, "satoshis": 6816}, {"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 2, "value": "546", "height": 606848, "confirmations": 13, "satoshis": 546}]}]}'
*
*
*/
@@ -2196,7 +2196,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/status" -H "accept:application/json" -H "Content-Type: application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/status" -H "accept:application/json" -H "Content-Type: application/json"
*
*/
async getStatus (req, res, next) {
@@ -2239,7 +2239,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/nftChildren/68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/nftChildren/68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a" -H "accept:application/json"
*
*/
async getNftChildren (req, res, next) {
@@ -2315,7 +2315,7 @@ class Slp {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/slp/nftGroup/45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9" -H "accept:application/json"
* curl -X GET "https://api.fullstack.cash/v5/slp/nftGroup/45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9" -H "accept:application/json"
*
*/
async getNftGroup (req, res, next) {
+6 -412
View File
@@ -14,27 +14,8 @@ util.inspect.defaultOptions = { depth: 1 }
const BCHJS = require('@psf/bch-js')
const bchjs = new BCHJS()
// const BCHJS_TESTNET = 'https://testnet.bchjs.cash/v4/'
// const bchjsHTTP = axios.create({
// baseURL: process.env.RPC_BASEURL
// })
// const username = process.env.RPC_USERNAME
// const password = process.env.RPC_PASSWORD
// const requestConfig = {
// method: 'post',
// auth: {
// username: username,
// password: password
// },
// data: {
// jsonrpc: '1.0'
// }
// }
let _this
// let _this
class UtilRoute {
constructor (utilConfig) {
@@ -58,9 +39,9 @@ class UtilRoute {
this.router.get('/', this.root)
this.router.get('/validateAddress/:address', this.validateAddressSingle)
this.router.post('/validateAddress', this.validateAddressBulk)
this.router.post('/sweep', this.sweepWif)
// this.router.post('/sweep', this.sweepWif)
_this = this
// _this = this
}
root (req, res, next) {
@@ -75,7 +56,7 @@ class UtilRoute {
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v4/util/validateAddress/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
* curl -X GET "https://api.fullstack.cash/v5/util/validateAddress/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
*
*
*/
@@ -124,8 +105,8 @@ class UtilRoute {
*
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/util/validateAddress" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c","bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"]}'
* curl -X POST "https://api.fullstack.cash/v4/util/validateAddress" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c","bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"],"from": 1, "to": 5}'
* curl -X POST "https://api.fullstack.cash/v5/util/validateAddress" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c","bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"]}'
* curl -X POST "https://api.fullstack.cash/v5/util/validateAddress" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c","bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"],"from": 1, "to": 5}'
*
*
*/
@@ -214,393 +195,6 @@ class UtilRoute {
return res.json({ error: util.inspect(err) })
}
}
/**
* @api {post} /util/sweep Sweep BCH and tokens
* @apiName Sweep BCH and tokens from a paper wallet
* @apiGroup Util
* @apiDescription This function can be used to check the BCH balance of a
* paper wallet. It can also be used to sweep BCH and tokens from a paper
* wallet and send them to a destination address.
*
* Note: It does not yet support multiple token classes on the same paper wallet.
*
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v4/util/sweep" -H "accept: application/json" -H "Content-Type: application/json" -d '{"wif":"Kz52sdXLAiKtH82iAFRi6aTZanWtW5Eyv37KWdpY6tTv7pjoHste", "balanceOnly": true}'
* curl -X POST "https://api.fullstack.cash/v4/util/sweep" -H "accept: application/json" -H "Content-Type: application/json" -d '{"wif":"Kz52sdXLAiKtH82iAFRi6aTZanWtW5Eyv37KWdpY6tTv7pjoHste", "toAddr": "bitcoincash:qpt8m4kqu963geedyrur6pdggqmv5kxwnq0rn322qu"}'
*
*
*/
async sweepWif (req, res, next) {
try {
// Validate input
const wif = req.body.wif
const toAddr = req.body.toAddr
const balanceOnly = req.body.balanceOnly
if (typeof wif !== 'string' || wif.length !== 52) {
res.status(400)
return res.json({
error: 'WIF needs to a proper compressed WIF starting with K or L'
})
}
if (!balanceOnly) {
// Only throw error if balanceOnly is false or undefined.
if (!toAddr || toAddr === '') {
res.status(400)
return res.json({ error: 'address can not be empty' })
}
}
wlogger.debug('Executing util/sweepWif with this address: ', toAddr)
// Generate a private and public key pair from the WIF.
const ecPair = bchjs.ECPair.fromWIF(wif)
const fromAddr = bchjs.ECPair.toCashAddress(ecPair)
// Get a balance on the public address
const balances = await _this.electrumx._balanceFromElectrumx(fromAddr)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
// Total balance is the sum of the confirmed and unconfirmed balance.
const totalBalance = balances.confirmed + balances.unconfirmed
// Exit if balance is zero.
if (isNaN(totalBalance) || totalBalance === 0) {
res.status(422)
return res.json({ error: 'No balance found at BCH address.' })
}
// Exit if this is a balance-only call.
if (balanceOnly) {
res.status(200)
return res.json(totalBalance)
}
// Get all UTXOs help by the address.
const utxos = await _this.electrumx._utxosFromElectrumx(fromAddr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const tokenUtxos = []
const bchUtxos = []
// Exit if there are no UTXOs.
if (utxos.length === 0) {
res.status(422)
return res.json({ error: 'No utxos found.' })
}
// Figure out which UTXOs are associated with SLP tokens.
const isTokenUtxo = await _this.bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`isTokenUtxo: ${JSON.stringify(isTokenUtxo, null, 2)}`)
// Separate the bch and token UTXOs.
for (let i = 0; i < utxos.length; i++) {
// Filter based on isTokenUtxo.
if (!isTokenUtxo[i]) bchUtxos.push(utxos[i])
else tokenUtxos.push(isTokenUtxo[i])
}
// console.log(
// `bchUtxos.length: ${bchUtxos.length}, tokenUtxos.length: ${tokenUtxos.length}`
// )
// Throw error if no BCH to move tokens.
if (bchUtxos.length === 0 && tokenUtxos.length > 0) {
res.status(422)
return res.json({
error:
'Tokens found, but no BCH UTXOs found. Add BCH to wallet to move tokens.'
})
}
// console.log(`tokenUtxos: ${JSON.stringify(tokenUtxos, null, 2)}`)
const options = {
ecPair,
utxos,
fromAddr,
toAddr,
bchUtxos,
tokenUtxos
}
let hex
// Choose the sweeping algorithm, based on if there are tokens or not.
if (tokenUtxos.length === 0) hex = await _this._sweepBCH(options)
else hex = await _this._sweepTokens(options, bchUtxos, tokenUtxos)
// console.log(`hex: ${hex}`)
// Throw error if there is more than one token class.
// Generate a transaction to move tokens and BCH.
// Broadcast the transaction.
const txid = _this.bchjs.RawTransactions.sendRawTransaction([hex])
res.status(200)
return res.json(txid)
} catch (err) {
// Attempt to decode the error message.
const { msg, status } = routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
// Catch the specific case of multiple tokens.
if (
err.message &&
err.message.indexOf('Multiple token classes detected') > -1
) {
res.status(422)
return res.json({ error: err.message })
}
wlogger.error('Error in util.js/sweepWif().', err)
console.error('Error in util.js/sweepWif().', err)
res.status(500)
return res.json({ error: err.message })
}
}
// Sweep BCH only from a private WIF.
async _sweepBCH (options) {
try {
// const wif = flags.wif
// const toAddr = flags.address
const ecPair = options.ecPair
const toAddr = options.toAddr
// const fromAddr = this.BITBOX.ECPair.toCashAddress(ecPair)
//
// // Get the UTXOs for that address.
// let utxos = await this.BITBOX.Blockbook.utxo(fromAddr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
let utxos = options.utxos
// Ensure all utxos have the satoshis property.
utxos = utxos.map((x) => {
x.satoshis = Number(x.value)
return x
})
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
// instance of transaction builder
let transactionBuilder
if (options.testnet) {
transactionBuilder = new _this.bchjs.TransactionBuilder('testnet')
} else transactionBuilder = new _this.bchjs.TransactionBuilder()
let originalAmount = 0
// Loop through all UTXOs.
for (let i = 0; i < utxos.length; i++) {
const utxo = utxos[i]
originalAmount = originalAmount + utxo.value
transactionBuilder.addInput(utxo.tx_hash, utxo.tx_pos)
}
if (originalAmount < 546) {
throw new Error(
'Original amount less than the dust limit. Not enough BCH to send.'
)
}
// get byte count to calculate fee. paying 1 sat/byte
const byteCount = _this.bchjs.BitcoinCash.getByteCount(
{ P2PKH: utxos.length },
{ P2PKH: 1 }
)
const fee = Math.ceil(1.1 * byteCount)
// amount to send to receiver. It's the original amount - 1 sat/byte for tx size
const sendAmount = originalAmount - fee
// add output w/ address and amount to send
transactionBuilder.addOutput(
_this.bchjs.Address.toLegacyAddress(toAddr),
sendAmount
)
// Loop through each input and sign
let redeemScript
for (var i = 0; i < utxos.length; i++) {
const utxo = utxos[i]
transactionBuilder.sign(
i,
ecPair,
redeemScript,
transactionBuilder.hashTypes.SIGHASH_ALL,
utxo.value
)
}
// build tx
const tx = transactionBuilder.build()
// output rawhex
const hex = tx.toHex()
return hex
} catch (err) {
wlogger.error('Error in util.js/sweepBCH().')
throw err
}
}
// Sweep BCH and tokens from a WIF.
async _sweepTokens (options) {
try {
// const { ecPair, utxos, fromAddr, toAddr, bchUtxos, tokenUtxos } = options
const { ecPair, utxos, toAddr, bchUtxos, tokenUtxos } = options
// Input validation
if (!Array.isArray(bchUtxos) || bchUtxos.length === 0) {
throw new Error('bchUtxos need to be an array with one UTXO.')
}
if (!Array.isArray(tokenUtxos) || tokenUtxos.length === 0) {
throw new Error('tokenUtxos need to be an array with one UTXO.')
}
// if (flags.testnet)
// this.BITBOX = new config.BCHLIB({ restURL: config.TESTNET_REST })
// console.log(`tokenUtxos: ${JSON.stringify(tokenUtxos, null, 2)}`)
// Ensure there is only one class of token in the wallet. Throw an error if
// there is more than one.
const tokenId = tokenUtxos[0].tokenId
const otherTokens = tokenUtxos.filter((x) => x.tokenId !== tokenId)
if (otherTokens.length > 0) {
throw new Error(
'Multiple token classes detected. This function only supports a single class of token.'
)
}
// instance of transaction builder
let transactionBuilder
if (options.testnet) {
transactionBuilder = new _this.bchjs.TransactionBuilder('testnet')
} else transactionBuilder = new _this.bchjs.TransactionBuilder()
// Combine all the UTXOs into a single array.
const allUtxos = utxos
// console.log(`allUtxos: ${JSON.stringify(allUtxos, null, 2)}`)
// Loop through all UTXOs.
let originalAmount = 0
for (let i = 0; i < allUtxos.length; i++) {
const utxo = allUtxos[i]
originalAmount = originalAmount + utxo.value
transactionBuilder.addInput(utxo.tx_hash, utxo.tx_pos)
}
if (originalAmount < 300) {
throw new Error(
'Not enough BCH to send. Send more BCH to the wallet to pay miner fees.'
)
}
// get byte count to calculate fee. paying 1 sat
// Note: This may not be totally accurate. Just guessing on the byteCount size.
// const byteCount = this.BITBOX.BitcoinCash.getByteCount(
// { P2PKH: 3 },
// { P2PKH: 5 }
// )
// //console.log(`byteCount: ${byteCount}`)
// const satoshisPerByte = 1.1
// const txFee = Math.floor(satoshisPerByte * byteCount)
// console.log(`txFee: ${txFee} satoshis\n`)
const txFee = 500
// amount to send back to the sending address. It's the original amount - 1 sat/byte for tx size
const remainder = originalAmount - txFee - 546
if (remainder < 1) {
throw new Error('Selected UTXO does not have enough satoshis')
}
// console.log(`remainder: ${remainder}`)
// Tally up the quantity of tokens
let tokenQty = 0
for (let i = 0; i < tokenUtxos.length; i++) {
tokenQty += tokenUtxos[i].tokenQty
}
// console.log(`tokenQty: ${tokenQty}`)
// Generate the OP_RETURN entry for an SLP SEND transaction.
// console.log(`Generating op-return.`)
const {
script,
outputs
} = _this.bchjs.SLP.TokenType1.generateSendOpReturn(tokenUtxos, tokenQty)
// console.log(`token outputs: ${outputs}`)
// Since we are sweeping all tokens from the WIF, there generateOpReturn()
// function should only compute 1 token output. If it returns 2, then there
// is something unexpected happening.
if (outputs > 1) {
throw new Error(
'More than one class of token detected. Sweep feature not supported.'
)
}
// Add OP_RETURN as first output.
const data = _this.bchjs.Script.encode(script)
transactionBuilder.addOutput(data, 0)
// Send dust transaction representing tokens being sent.
transactionBuilder.addOutput(
_this.bchjs.Address.toLegacyAddress(toAddr),
546
)
// Last output: send remaining BCH
transactionBuilder.addOutput(
_this.bchjs.Address.toLegacyAddress(toAddr),
remainder
)
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
// Sign each UTXO being consumed.
let redeemScript
for (let i = 0; i < allUtxos.length; i++) {
const thisUtxo = allUtxos[i]
// console.log(`thisUtxo: ${JSON.stringify(thisUtxo, null, 2)}`)
transactionBuilder.sign(
i,
ecPair,
redeemScript,
transactionBuilder.hashTypes.SIGHASH_ALL,
thisUtxo.value
)
}
// build tx
const tx = transactionBuilder.build()
// output rawhex
const hex = tx.toHex()
// console.log(`Transaction raw hex: `)
// console.log(hex)
return hex
} catch (err) {
wlogger.error('Error in util.js/sweepBCH().')
throw err
}
}
}
module.exports = UtilRoute
+1269 -12
View File
File diff suppressed because it is too large Load Diff
+190
View File
@@ -0,0 +1,190 @@
/*
TESTS FOR THE DSPROOF.JS LIBRARY
This test file uses the environment variable TEST to switch between unit
and integration tests. By default, TEST is set to 'unit'. Set this variable
to 'integration' to run the tests against BCH mainnet.
*/
'use strict'
const chai = require('chai')
const assert = chai.assert
const DSProofRoute = require('../../src/routes/v5/full-node/dsproof')
const uut = new DSProofRoute()
// const nock = require('nock') // HTTP mocking
const sinon = require('sinon')
let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data.
const { mockReq, mockRes } = require('./mocks/express-mocks')
const mockData = require('./mocks/dsproof-mocks')
// Used for debugging.
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
describe('#DSProof', () => {
let req, res
let sandbox
before(() => {
// Save existing environment variables.
originalEnvVars = {
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
RPC_BASEURL: process.env.RPC_BASEURL,
RPC_USERNAME: process.env.RPC_USERNAME,
RPC_PASSWORD: process.env.RPC_PASSWORD
}
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = 'unit'
if (process.env.TEST === 'unit') {
process.env.BITCOINCOM_BASEURL = 'http://fakeurl/api/'
process.env.RPC_BASEURL = 'http://fakeurl/api'
process.env.RPC_USERNAME = 'fakeusername'
process.env.RPC_PASSWORD = 'fakepassword'
}
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
req.query = {}
sandbox = sinon.createSandbox()
})
afterEach(() => {
// Restore Sandbox
sandbox.restore()
})
after(() => {
// Restore any pre-existing environment variables.
process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL
process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL
process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
})
describe('#root', async () => {
// root route handler.
it('should respond to GET for base route', async () => {
const result = uut.root(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, 'dsproof', 'Returns static string')
})
})
describe('#getDSProof', async () => {
it('should throw 400 error if txid is missing', async () => {
const result = await uut.getDSProof(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ['error', 'success'])
assert.isFalse(result.success)
assert.include(result.error, 'txid can not be empty')
})
it('should throw 400 error if txid is invalid', async () => {
req.params.txid = 'abc123'
const result = await uut.getDSProof(req, res)
assert.hasAllKeys(result, ['error', 'success'])
assert.isFalse(result.success)
assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include(result.error, 'txid must be of length 64')
})
it('should throw 503 when network issues', async () => {
req.params.txid =
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
// Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = 'http://fakeurl/api/'
await uut.getDSProof(req, res)
// console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2
assert.isAbove(
res.statusCode,
499,
'HTTP status code 500 or greater expected.'
)
// assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
})
it('returns proper error when downstream service stalls', async () => {
req.params.txid =
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNABORTED' })
const result = await uut.getDSProof(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
req.params.txid =
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNREFUSED' })
const result = await uut.getDSProof(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should GET double-spend proof', async function () {
req.params.txid =
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
// Mock the RPC call for unit tests.
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.axios, 'request')
.resolves({ data: { result: mockData.mockGetDsProof } })
} else {
return this.skip()
}
const result = await uut.getDSProof(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.property(result, 'dspid')
assert.property(result, 'txid')
assert.property(result, 'outpoint')
assert.property(result, 'path')
assert.property(result, 'descendants')
assert.property(result.outpoint, 'txid')
assert.property(result.outpoint, 'vout')
})
})
})
+99
View File
@@ -0,0 +1,99 @@
/*
TESTS FOR THE JWT.JS LIBRARY
This test file uses the environment variable TEST to switch between unit
and integration tests. By default, TEST is set to 'unit'. Set this variable
to 'integration' to run the tests against BCH mainnet.
*/
'use strict'
const chai = require('chai')
const assert = chai.assert
const JWTRoute = require('../../src/routes/v5/jwt')
const uut = new JWTRoute()
const sinon = require('sinon')
// Mocking data.
const { mockReq, mockRes } = require('./mocks/express-mocks')
// const mockData = require('./mocks/control-mock')
// Used for debugging.
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
describe('#JWTRouter', () => {
let req, res
let sandbox
before(() => {
// Set default environment variables for unit tests.
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
req.query = {}
sandbox = sinon.createSandbox()
})
afterEach(() => {
// Restore Sandbox
sandbox.restore()
})
after(() => {})
describe('#root', async () => {
// root route handler.
// const root = controlRoute.testableComponents.root
it('should respond to GET for base route', async () => {
const result = uut.root(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, 'jwt', 'Returns static string')
})
})
describe('#jwtInfo', () => {
it('should decode the JWT token', async () => {
const jwtToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwZWM3NTY1YTM4ZjkyMjdlOWVjNTM3MCIsImVtYWlsIjoidGVzdHVzZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjAsInJhdGVMaW1pdCI6MywicG9pbnRzVG9Db25zdW1lIjo1MDAsImR1cmF0aW9uIjozMCwiaWF0IjoxNjI2MTA5MzQ4LCJleHAiOjE2Mjg3MDEzNDh9.hF8BKU-1SvlvQBnTNbB65ErQTtNSl-pWlRANSJY-Zb4'
req.body.jwt = jwtToken
const result = await uut.jwtInfo(req, res)
// console.log('result: ', result)
assert.property(result, 'id')
assert.property(result, 'email')
assert.property(result, 'apiLevel')
assert.property(result, 'rateLimit')
assert.property(result, 'pointsToConsume')
assert.property(result, 'duration')
assert.property(result, 'iat')
assert.property(result, 'exp')
assert.property(result, 'expiration')
assert.property(result, 'createdAt')
})
it('should return an error with malformed JWT token', async () => {
const jwtToken =
'eyJhbGciOiJIUzI1NiLsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwZWM3NTY1YTM4ZjkyMjdlOWVjNTM3MCIsImVtYWlsIjoidGVzdHVzZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjAsInJhdGVMaW1pdCI6MywicG9pbnRzVG9Db25zdW1lIjo1MDAsImR1cmF0aW9uIjozMCwiaWF0IjoxNjI2MTA5MzQ4LCJleHAiOjE2Mjg3MDEzNDh9.hF8BKU-1SvlvQBnTNbB65ErQTtNSl-pWlRANSJY-Zb4'
req.body.jwt = jwtToken
const result = await uut.jwtInfo(req, res)
// console.log('result: ', result)
assert.equal(result.error, 'invalid token')
})
})
})
+22
View File
@@ -0,0 +1,22 @@
/*
This library contains mocking data for running unit tests on the dsproof route.
*/
'use strict'
const mockGetDsProof = {
dspid: '6234fff2a9dbcaa50e2c50d74e1d725a2bed1757d7a05a2db7a82df659554397',
txid: 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e',
outpoint: {
txid: '17ebef34f7e9a0692317dd6fbb43ffccff45b7d688a2a754dbccfc5e6d93ce3e',
vout: 1
},
path: ['ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'],
descendants: [
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
]
}
module.exports = {
mockGetDsProof
}
+77 -1
View File
@@ -19,6 +19,43 @@ const utxos = [
}
]
const utxosArray = {
success: true,
utxos: [
{
utxos: [
{
height: 601861,
tx_hash:
'6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d',
tx_pos: 0,
value: 1000
}
],
address: 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
},
{
utxos: [
{
height: 604392,
tx_hash:
'7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41',
tx_pos: 0,
value: 1000
},
{
height: 630834,
tx_hash:
'4fe60a51e0d8f5134bfd8e5f872d6e502d7f01b28a6afebb27f4438a4f638d53',
tx_pos: 0,
value: 6000
}
],
address: 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
}
]
}
const balance = {
success: true,
balance: {
@@ -129,17 +166,56 @@ const txDetails = {
]
}
const txDetailsBulk = {
success: true,
transactions: [{ details: txDetails }]
}
const blockHeaders = [
'010000008b52bbd72c2f49569059f559c1b1794de5192e4f7d6d2b03c7482bad0000000083e4f8a9d502ed0c419075c1abb5d56f878a2e9079e5612bfb76a2dc37d9c42741dd6849ffff001d2b909dd6',
'01000000f528fac1bcb685d0cd6c792320af0300a5ce15d687c7149548904e31000000004e8985a786d864f21e9cbb7cbdf4bc9265fe681b7a0893ac55a8e919ce035c2f85de6849ffff001d385ccb7c'
]
const blockHeadersBulk = {
success: true,
headers: [{ headers: blockHeaders }, { headers: blockHeaders }]
}
const transactions = [
{
height: 603416,
tx_hash: 'eef683d236d88e978bd406419f144057af3fe1b62ef59162941c1a9f05ded62c'
},
{
height: 646894,
tx_hash: '4c695fae636f3e8e2edc571d11756b880ccaae744390f3950d798ce7b5e25754'
}
]
const transactionsBulk = {
success: true,
transactions: [
{
transactions,
address: 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
},
{
transactions,
address: 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
}
]
}
module.exports = {
utxos,
utxosArray,
balance,
txHistory,
mempool,
txDetails,
txDetailsBulk,
blockHeaders,
balances
blockHeadersBulk,
balances,
transactions,
transactionsBulk
}
+5 -212
View File
@@ -19,7 +19,7 @@ const nock = require('nock') // HTTP mocking
const sinon = require('sinon')
// Local libraries
const Electrumx = require('../../src/routes/v5/electrumx')
// const Electrumx = require('../../src/routes/v5/electrumx')
const UtilRoute = require('../../src/routes/v5/util')
let originalEnvVars // Used during transition from integration to unit tests.
@@ -38,8 +38,8 @@ const utilRouteInst = new UtilRoute({ electrumx: {} })
describe('#Util', () => {
let req, res
let sandbox
let electrumx
let utilRoute
// let electrumx
// let utilRoute
before(async () => {
// Save existing environment variables.
@@ -59,7 +59,7 @@ describe('#Util', () => {
process.env.RPC_PASSWORD = 'fakepassword'
}
electrumx = new Electrumx()
// electrumx = new Electrumx()
// await electrumx.connect()
})
@@ -79,7 +79,7 @@ describe('#Util', () => {
sandbox = sinon.createSandbox()
utilRoute = new UtilRoute({ electrumx })
// utilRoute = new UtilRoute({ electrumx })
})
afterEach(() => {
@@ -322,211 +322,4 @@ describe('#Util', () => {
])
})
})
describe('#sweepWif', () => {
it('should throw 400 if WIF is not included', async () => {
req.body = {}
const result = await utilRouteInst.sweepWif(req, res)
assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include(
result.error,
'WIF needs to a proper compressed WIF starting with K or L',
'Proper error message'
)
})
it('should throw 400 if WIF is malformed', async () => {
req.body = {
wif: 'abc123'
}
const result = await utilRouteInst.sweepWif(req, res)
assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include(
result.error,
'WIF needs to a proper compressed WIF starting with K or L',
'Proper error message'
)
})
it('should throw 400 if destination address is not included', async () => {
req.body = {
wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt'
}
const result = await utilRouteInst.sweepWif(req, res)
assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include(
result.error,
'address can not be empty',
'Proper error message'
)
})
// Unit test only.
if (process.env.TEST === 'unit') {
it('should generate transaction for valid token sweep', async () => {
// Mock the RPC call for unit tests.
sandbox
.stub(utilRoute.electrumx, '_balanceFromElectrumx')
.resolves(mockData.mockBalance)
sandbox
.stub(utilRoute.electrumx, '_utxosFromElectrumx')
.resolves(mockData.mockUtxos)
sandbox
.stub(utilRoute.bchjs.SLP.Utils, 'tokenUtxoDetails')
.resolves(mockData.mockIsTokenUtxos)
// Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network.
sandbox
.stub(utilRoute.bchjs.RawTransactions, 'sendRawTransaction')
.resolves('test-txid')
req.body = {
wif: 'L1wAGEN721LHDoiN8pLwwBb87bYrU6Gs21UPcCRR7LjKypQyVaCq',
toAddr: 'bitcoincash:qp2g4cnekxsjspccmtvh5k73mczz6273js4mjr353r'
}
const result = await utilRoute.sweepWif(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, 'test-txid')
})
it('should return balance if balance-only is true', async () => {
// Mock the RPC call for unit tests.
sandbox
.stub(utilRoute.electrumx, '_balanceFromElectrumx')
.resolves(mockData.mockBalance)
// Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network.
sandbox
.stub(utilRoute.bchjs.RawTransactions, 'sendRawTransaction')
.resolves('test-txid')
req.body = {
wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt',
balanceOnly: true
}
const result = await utilRoute.sweepWif(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isNumber(result)
})
it('should generate transaction for valid BCH-only sweep', async () => {
sandbox
.stub(utilRoute.electrumx, '_balanceFromElectrumx')
.resolves(mockData.mockBalance)
sandbox
.stub(utilRoute.electrumx, '_utxosFromElectrumx')
.resolves(mockData.mockUtxos)
// Force token utxo to appear as regular BCH utxo.
sandbox
.stub(utilRoute.bchjs.SLP.Utils, 'tokenUtxoDetails')
.resolves([false, false])
// Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network.
sandbox
.stub(utilRoute.bchjs.RawTransactions, 'sendRawTransaction')
.resolves('test-txid')
req.body = {
wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt',
toAddr: 'bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p'
}
const result = await utilRoute.sweepWif(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, 'test-txid')
})
it('should throw 422 error if no non-token UTXOs', async () => {
sandbox
.stub(utilRoute.electrumx, '_balanceFromElectrumx')
.resolves(mockData.mockBalance)
sandbox
.stub(utilRoute.electrumx, '_utxosFromElectrumx')
.resolves(mockData.mockUtxos)
// Force token utxo to appear as regular BCH utxo.
sandbox
.stub(utilRoute.bchjs.SLP.Utils, 'tokenUtxoDetails')
.resolves(mockData.tokensOnly)
// Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network.
sandbox
.stub(utilRoute.bchjs.RawTransactions, 'sendRawTransaction')
.resolves('test-txid')
req.body = {
wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt',
toAddr: 'bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p'
}
const result = await utilRoute.sweepWif(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(res.statusCode, 422)
assert.property(result, 'error')
assert.include(
result.error,
'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 () => {
sandbox
.stub(utilRoute.electrumx, '_balanceFromElectrumx')
.resolves(mockData.mockBalance)
sandbox
.stub(utilRoute.electrumx, '_utxosFromElectrumx')
.resolves(mockData.mockThreeUtxos)
// Force token utxo to appear as regular BCH utxo.
sandbox
.stub(utilRoute.bchjs.SLP.Utils, 'tokenUtxoDetails')
.resolves(mockData.multipleTokens)
// Mock sendRawTransaction() so that the hex does not actually get broadcast
// to the network.
sandbox
.stub(utilRoute.bchjs.RawTransactions, 'sendRawTransaction')
.resolves('test-txid')
req.body = {
wif: 'L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt',
toAddr: 'bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p'
}
const result = await utilRoute.sweepWif(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(res.statusCode, 422)
assert.property(result, 'error')
assert.include(
result.error,
'Multiple token classes detected. This function only supports a single class of token'
)
})
}
})
})