mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-21 16:52:04 -07:00
fix(tests): Fixed unit and integration tests
This commit is contained in:
Generated
+1348
-1913
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -33,8 +33,8 @@
|
||||
"express": "^4.15.5",
|
||||
"express-basic-auth": "^1.1.3",
|
||||
"express-rate-limit": "^5.0.0",
|
||||
"helmet": "^3.21.1",
|
||||
"level": "^5.0.1",
|
||||
"helmet": "^3.21.2",
|
||||
"level": "^6.0.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mocha": "^6.1.4",
|
||||
"morgan": "^1.9.1",
|
||||
@@ -45,7 +45,7 @@
|
||||
"pg": "^7.11.0",
|
||||
"pg-hstore": "^2.3.2",
|
||||
"slp-sdk": "^4.4.1",
|
||||
"slpjs": "0.21.2",
|
||||
"slpjs": "0.21.4",
|
||||
"strftime": "^0.10.0",
|
||||
"winston": "^3.2.1",
|
||||
"winston-daily-rotate-file": "^4.0.0"
|
||||
|
||||
@@ -138,8 +138,8 @@ async function balanceSingle(req, res, next) {
|
||||
}
|
||||
|
||||
// POST handler for bulk queries on address details
|
||||
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"]}' -H "Content-Type: application/json" http://localhost:3000/v2/address/details
|
||||
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"], "from": 1, "to": 5}' -H "Content-Type: application/json" http://localhost:3000/v2/address/details
|
||||
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details
|
||||
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details
|
||||
async function balanceBulk(req, res, next) {
|
||||
try {
|
||||
let addresses = req.body.addresses
|
||||
|
||||
@@ -601,6 +601,8 @@ module.exports = {
|
||||
utxosSingle,
|
||||
utxosBulk,
|
||||
txSingle,
|
||||
txBulk
|
||||
txBulk,
|
||||
balanceFromBlockbook,
|
||||
utxosFromBlockbook
|
||||
}
|
||||
}
|
||||
|
||||
+13
-13
@@ -49,7 +49,7 @@ function root(req, res, next) {
|
||||
* block chain.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/blockchain/getBestBlockHash" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getBestBlockHash" -H "accept: application/json"
|
||||
*
|
||||
* @apiSuccess {String} bestBlockHash 000000000000000002bc884334336d99c9a9c616670a9244c6a8c1fc35aa91a1
|
||||
*/
|
||||
@@ -92,7 +92,7 @@ async function getBestBlockHash(req, res, next) {
|
||||
* @apiDescription Returns an object containing various state info regarding blockchain processing.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/blockchain/getBlockchainInfo" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getBlockchainInfo" -H "accept: application/json"
|
||||
*
|
||||
* @apiSuccess {Object} object Object containing data
|
||||
* @apiSuccess {String} object.chain "main"
|
||||
@@ -150,7 +150,7 @@ async function getBlockchainInfo(req, res, next) {
|
||||
* @apiDescription Returns the number of blocks in the longest blockchain.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/blockchain/getBlockCount" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getBlockCount" -H "accept: application/json"
|
||||
*
|
||||
* @apiSuccess {Number} bestBlockCount 587665
|
||||
*/
|
||||
@@ -195,7 +195,7 @@ async function getBlockCount(req, res, next) {
|
||||
* returns an Object with information about blockheader hash.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/blockchain/getBlockHeader/000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201?verbose=true" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getBlockHeader/000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201?verbose=true" -H "accept: application/json"
|
||||
*
|
||||
* @apiParam {String} hash block hash
|
||||
* @apiParam {Boolean} verbose Return verbose data
|
||||
@@ -372,7 +372,7 @@ async function getBlockHeaderBulk(req, res, next) {
|
||||
* including the main chain as well as orphaned branches.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/blockchain/getChainTips" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getChainTips" -H "accept: application/json"
|
||||
*
|
||||
*/
|
||||
async function getChainTips(req, res, next) {
|
||||
@@ -415,7 +415,7 @@ async function getChainTips(req, res, next) {
|
||||
* power on the network.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/blockchain/getDifficulty" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getDifficulty" -H "accept: application/json"
|
||||
*
|
||||
*/
|
||||
async function getDifficulty(req, res, next) {
|
||||
@@ -459,7 +459,7 @@ async function getDifficulty(req, res, next) {
|
||||
* mempool (unconfirmed)
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/blockchain/getMempoolEntry/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getMempoolEntry/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
|
||||
*
|
||||
*/
|
||||
async function getMempoolEntrySingle(req, res, next) {
|
||||
@@ -509,7 +509,7 @@ async function getMempoolEntrySingle(req, res, next) {
|
||||
* @apiDescription Returns mempool data for multiple transactions
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST http://localhost:3000/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 function getMempoolEntryBulk(req, res, next) {
|
||||
try {
|
||||
@@ -592,7 +592,7 @@ async function getMempoolEntryBulk(req, res, next) {
|
||||
* @apiDescription Returns details on the active state of the TX memory pool.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET http://localhost:3000/v3/getMempoolInfo -H "accept: application/json"
|
||||
* curl -X GET https://mainnet.bchjs.cash/v3/getMempoolInfo -H "accept: application/json"
|
||||
*
|
||||
*/
|
||||
async function getMempoolInfo(req, res, next) {
|
||||
@@ -634,7 +634,7 @@ async function getMempoolInfo(req, res, next) {
|
||||
* @apiDescription Returns details on the active state of the TX memory pool.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET http://localhost:3000/v3/getMempoolInfo -H "accept: application/json"
|
||||
* curl -X GET https://mainnet.bchjs.cash/v3/getMempoolInfo -H "accept: application/json"
|
||||
*
|
||||
*/
|
||||
/**
|
||||
@@ -645,7 +645,7 @@ async function getMempoolInfo(req, res, next) {
|
||||
* of string transaction ids.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/getRawMempool/?verbose=true" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/getRawMempool/?verbose=true" -H "accept: application/json"
|
||||
*
|
||||
* @apiParam {Boolean} verbose Return verbose data
|
||||
*
|
||||
@@ -693,7 +693,7 @@ async function getRawMempool(req, res, next) {
|
||||
* @apiDescription Returns details about an unspent transaction output.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/blockchain/getTxOut/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33/0?mempool=false" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getTxOut/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33/0?mempool=false" -H "accept: application/json"
|
||||
*
|
||||
* @apiParam {String} txid Transaction id (required)
|
||||
* @apiParam {Number} n Output number (required)
|
||||
@@ -759,7 +759,7 @@ async function getTxOut(req, res, next) {
|
||||
* @apiDescription Returns a hex-encoded proof that 'txid' was included in a block.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/blockchain/getTxOutProofSingle/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getTxOutProofSingle/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
|
||||
*
|
||||
* @apiParam {String} txid Transaction id (required)
|
||||
*
|
||||
|
||||
@@ -26,7 +26,7 @@ function root(req, res, next) {
|
||||
* @apiDescription RPC call which gets basic full node information.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/control/getinfo" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/control/getinfo" -H "accept: application/json"
|
||||
*
|
||||
* @apiSuccess {Object} object Object containing data
|
||||
* @apiSuccess {Number} object.version Full node version
|
||||
@@ -77,7 +77,7 @@ async function getInfo(req, res, next) {
|
||||
* @apiDescription RPC call which gets basic full node information.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/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) {
|
||||
|
||||
@@ -84,8 +84,8 @@ async function detailsFromInsight(thisAddress, currentPage = 0) {
|
||||
}
|
||||
|
||||
// POST handler for bulk queries on address details
|
||||
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"]}' -H "Content-Type: application/json" http://localhost:3000/v2/address/details
|
||||
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"], "from": 1, "to": 5}' -H "Content-Type: application/json" http://localhost:3000/v2/address/details
|
||||
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details
|
||||
// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details
|
||||
/**
|
||||
* @api {post} /address/details Get Address details bulk.
|
||||
* @apiName Address details bulk
|
||||
@@ -94,8 +94,8 @@ async function detailsFromInsight(thisAddress, currentPage = 0) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" http://localhost:3000/v3/insight/address/details
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" http://localhost:3000/v3/insight/address/details
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/details
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/details
|
||||
*
|
||||
*/
|
||||
async function detailsBulk(req, res, next) {
|
||||
@@ -182,7 +182,7 @@ async function detailsBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/insight/address/details/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/details/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -296,8 +296,8 @@ async function utxoFromInsight(thisAddress) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" http://localhost:3000/v3/insight/address/utxo
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" http://localhost:3000/v3/insight/address/utxo
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/utxo
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/utxo
|
||||
*
|
||||
*/
|
||||
// Retrieve UTXO information for an address.
|
||||
@@ -386,7 +386,7 @@ async function utxoBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/insight/address/utxo/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/utxo/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -457,8 +457,8 @@ async function utxoSingle(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" http://localhost:3000/v3/insight/address/unconfirmed
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" http://localhost:3000/v3/insight/address/unconfirmed
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/unconfirmed
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/unconfirmed
|
||||
*
|
||||
*/
|
||||
// Retrieve any unconfirmed TX information for a given address.
|
||||
@@ -553,7 +553,7 @@ async function unconfirmedBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/insight/address/unconfirmed/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/unconfirmed/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -659,8 +659,8 @@ async function transactionsFromInsight(thisAddress, currentPage = 0) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" http://localhost:3000/v3/insight/address/transactions
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" http://localhost:3000/v3/insight/address/transactions
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/transactions
|
||||
* curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/transactions
|
||||
*
|
||||
*/
|
||||
// Get an array of TX information for a given address.
|
||||
@@ -744,7 +744,7 @@ async function transactionsBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/insight/address/transactions/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/transactions/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -822,7 +822,7 @@ async function transactionsSingle(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/insight/address/fromXPub/xpub661MyMwAqRbcG4CnhNYoK1r1TKLwQQ1UdC3LHoWFK61rsnzh7Hx35qQ9Z53ucYcE5WvA7GEDXhqqKjSY2e6Y8n7WNVLYHpXCuuX945VPuYn" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/fromXPub/xpub661MyMwAqRbcG4CnhNYoK1r1TKLwQQ1UdC3LHoWFK61rsnzh7Hx35qQ9Z53ucYcE5WvA7GEDXhqqKjSY2e6Y8n7WNVLYHpXCuuX945VPuYn" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,7 @@ function root(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/insight/block/detailsByHash/000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/block/detailsByHash/000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -85,8 +85,8 @@ async function detailsByHashSingle(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -d '{"hashes":["0000000000000000040e83398a79a16390897f0d18c92bada6350a19a32ec984","000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"]}' -H "Content-Type: application/json" "http://localhost:3000/v3/insight/block/detailsByHash"
|
||||
* curl -d '{"hashes":["0000000000000000040e83398a79a16390897f0d18c92bada6350a19a32ec984","000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"], "from": 1, "to": 5}' -H "Content-Type: application/json" "http://localhost:3000/v3/insight/block/detailsByHash"
|
||||
* curl -d '{"hashes":["0000000000000000040e83398a79a16390897f0d18c92bada6350a19a32ec984","000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"]}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHash"
|
||||
* curl -d '{"hashes":["0000000000000000040e83398a79a16390897f0d18c92bada6350a19a32ec984","000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"], "from": 1, "to": 5}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHash"
|
||||
*
|
||||
*/
|
||||
async function detailsByHashBulk(req, res, next) {
|
||||
@@ -165,7 +165,7 @@ async function detailsByHashBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/insight/block/block/detailsByHeight/500000" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/block/block/detailsByHeight/500000" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -224,8 +224,8 @@ async function detailsByHeightSingle(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -d '{"heights":[499000,500000]}' -H "Content-Type: application/json" "http://localhost:3000/v3/insight/block/detailsByHeight"
|
||||
* curl -d '{"heights":[499000,500000], "from": 1, "to": 5}' -H "Content-Type: application/json" "http://localhost:3000/v3/insight/block/detailsByHeight"
|
||||
* curl -d '{"heights":[499000,500000]}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHeight"
|
||||
* curl -d '{"heights":[499000,500000], "from": 1, "to": 5}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHeight"
|
||||
*
|
||||
*/
|
||||
async function detailsByHeightBulk(req, res, next) {
|
||||
|
||||
@@ -90,7 +90,7 @@ async function transactionsFromInsight(txid) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "http://localhost:3000/v3/insight/transaction/details" -H "Content-Type: application/json" -d "{"txids":["a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1","5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e"]}"
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/insight/transaction/details" -H "Content-Type: application/json" -d "{"txids":["a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1","5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e"]}"
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -150,7 +150,7 @@ async function detailsBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/insight/transaction/details/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/insight/transaction/details/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -68,7 +68,7 @@ function root(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/mining/getMiningInfo" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/mining/getMiningInfo" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -110,7 +110,7 @@ async function getMiningInfo(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/mining/getNetworkHashps?nblocks=120&height=-1" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/mining/getNetworkHashps?nblocks=120&height=-1" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -52,7 +52,7 @@ function root(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/rawtransactions/decodeRawTransaction/02000000010e991f7ccec410f27d333f737f149b5d3be6728687da81072e638aed0063a176010000006b483045022100cd20443b0af090053450bc4ab00d563d4ac5955bb36e0135b00b8a96a19f233302205047f2c70a08c6ef4b76f2d198b33a31d17edfaa7e1e9e865894da0d396009354121024d4e7f522f67105b7bf5f9dbe557e7b2244613fdfcd6fe09304f93877328f6beffffffff02a0860100000000001976a9140ee020c07f39526ac5505c54fa1ab98490979b8388acb5f0f70b000000001976a9143a9b2b0c12fe722fcf653b6ef5dcc38732d6ff5188ac00000000" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/rawtransactions/decodeRawTransaction/02000000010e991f7ccec410f27d333f737f149b5d3be6728687da81072e638aed0063a176010000006b483045022100cd20443b0af090053450bc4ab00d563d4ac5955bb36e0135b00b8a96a19f233302205047f2c70a08c6ef4b76f2d198b33a31d17edfaa7e1e9e865894da0d396009354121024d4e7f522f67105b7bf5f9dbe557e7b2244613fdfcd6fe09304f93877328f6beffffffff02a0860100000000001976a9140ee020c07f39526ac5505c54fa1ab98490979b8388acb5f0f70b000000001976a9143a9b2b0c12fe722fcf653b6ef5dcc38732d6ff5188ac00000000" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -106,7 +106,7 @@ async function decodeRawTransactionSingle(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "http://localhost:3000/v3/rawtransactions/decodeRawTransaction" -H "accept: application/json" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/rawtransactions/decodeRawTransaction" -H "accept: application/json" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -231,7 +231,7 @@ async function decodeRawTransactionBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/rawtransactions/decodeScript/4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/rawtransactions/decodeScript/4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -285,7 +285,7 @@ async function decodeScriptSingle(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*curl -X POST "http://localhost:3000/v3/rawtransactions/decodeScript" -H "accept:" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
|
||||
*curl -X POST "https://mainnet.bchjs.cash/v3/rawtransactions/decodeScript" -H "accept:" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -394,7 +394,7 @@ async function getRawTransactionsFromNode(txid, verbose) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "http://localhost:3000/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) {
|
||||
@@ -478,7 +478,7 @@ async function getRawTransactionBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/rawtransactions/getRawTransaction/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33?verbose=true" -H "accept: application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/rawtransactions/getRawTransaction/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33?verbose=true" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -522,7 +522,7 @@ async function getRawTransactionSingle(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "http://localhost:3000/v3/rawtransactions/sendRawTransaction" -H "accept:application/json" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/rawtransactions/sendRawTransaction" -H "accept:application/json" -H "Content-Type: application/json" -d '{"hexes":["01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000","01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -629,7 +629,7 @@ async function sendRawTransactionBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/rawtransactions/sendRawTransaction/01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" -H "accept: "
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/rawtransactions/sendRawTransaction/01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" -H "accept: "
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
+227
-460
@@ -3,6 +3,7 @@
|
||||
const express = require("express")
|
||||
const router = express.Router()
|
||||
const axios = require("axios")
|
||||
const BigNumber = require("bignumber.js")
|
||||
|
||||
const routeUtils = require("./route-utils")
|
||||
const strftime = require("strftime")
|
||||
@@ -21,8 +22,8 @@ const slpjs = new slp.Slp(SLP)
|
||||
const utils = slp.Utils
|
||||
|
||||
// SLP tx db (LevelDB for caching)
|
||||
const level = require("level")
|
||||
const slpTxDb = level("./slp-tx-db")
|
||||
// const level = require("level")
|
||||
// const slpTxDb = level("./slp-tx-db")
|
||||
|
||||
// Setup JSON RPC
|
||||
const BitboxHTTP = axios.create({
|
||||
@@ -31,6 +32,12 @@ const BitboxHTTP = axios.create({
|
||||
const username = process.env.RPC_USERNAME
|
||||
const password = process.env.RPC_PASSWORD
|
||||
|
||||
// Determine the Access password for a private instance of SLPDB.
|
||||
// https://gist.github.com/christroutner/fc717ca704dec3dded8b52fae387eab2
|
||||
const SLPDB_PASS = process.env.SLPDB_PASS ? process.env.SLPDB_PASS : "BITBOX"
|
||||
|
||||
const transactions = require("./insight/transaction")
|
||||
|
||||
// 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://rest.bitcoin.com/v2/`
|
||||
@@ -53,102 +60,6 @@ router.get("/txDetails/:txid", txDetails)
|
||||
router.get("/tokenStats/:tokenId", tokenStats)
|
||||
router.get("/transactions/:tokenId/:address", txsTokenIdAddressSingle)
|
||||
|
||||
if (process.env.NON_JS_FRAMEWORK && process.env.NON_JS_FRAMEWORK === "true") {
|
||||
router.get(
|
||||
"/createTokenType1/:fundingAddress/:fundingWif/:tokenReceiverAddress/:batonReceiverAddress/:bchChangeReceiverAddress/:decimals/:name/:symbol/:documentUri/:documentHash/:initialTokenQty",
|
||||
createTokenType1
|
||||
)
|
||||
router.get(
|
||||
"/mintTokenType1/:fundingAddress/:fundingWif/:tokenReceiverAddress/:batonReceiverAddress/:bchChangeReceiverAddress/:tokenId/:additionalTokenQty",
|
||||
mintTokenType1
|
||||
)
|
||||
router.get(
|
||||
"/sendTokenType1/:fundingAddress/:fundingWif/:tokenReceiverAddress/:bchChangeReceiverAddress/:tokenId/:amount",
|
||||
sendTokenType1
|
||||
)
|
||||
router.get(
|
||||
"/burnTokenType1/:fundingAddress/:fundingWif/:bchChangeReceiverAddress/:tokenId/:amount",
|
||||
burnTokenType1
|
||||
)
|
||||
router.get(
|
||||
"/burnAllTokenType1/:fundingAddress/:fundingWif/:bchChangeReceiverAddress/:tokenId",
|
||||
burnAllTokenType1
|
||||
)
|
||||
}
|
||||
|
||||
// Retrieve raw transactions details from the full node.
|
||||
// TODO: move this function to a separate support library.
|
||||
// TODO: Add unit tests for this function.
|
||||
async function getRawTransactionsFromNode(txids) {
|
||||
try {
|
||||
const {
|
||||
BitboxHTTP,
|
||||
username,
|
||||
password,
|
||||
requestConfig
|
||||
} = routeUtils.setEnvVars()
|
||||
|
||||
const txPromises = txids.map(async txid => {
|
||||
// Check slpTxDb
|
||||
try {
|
||||
if (slpTxDb.isOpen()) {
|
||||
const rawTx = await slpTxDb.get(txid)
|
||||
return rawTx
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
requestConfig.data.id = "getrawtransaction"
|
||||
requestConfig.data.method = "getrawtransaction"
|
||||
requestConfig.data.params = [txid, 0]
|
||||
|
||||
const response = await BitboxHTTP(requestConfig)
|
||||
const result = response.data.result
|
||||
|
||||
// Insert to slpTxDb
|
||||
try {
|
||||
if (slpTxDb.isOpen()) await slpTxDb.put(txid, result)
|
||||
} catch (err) {
|
||||
// console.log("Error inserting to slpTxDb", err)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const results = await axios.all(txPromises)
|
||||
return results
|
||||
} catch (err) {
|
||||
wlogger.error(`Error in slp.ts/getRawTransactionsFromNode().`, err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Create a validator for validating SLP transactions.
|
||||
function createValidator(network, getRawTransactions = null) {
|
||||
let tmpSLP
|
||||
|
||||
if (network === "mainnet")
|
||||
tmpSLP = new SLPSDK({ restURL: process.env.REST_URL })
|
||||
else tmpSLP = new SLPSDK({ restURL: process.env.TREST_URL })
|
||||
|
||||
const slpValidator = new slp.LocalValidator(
|
||||
tmpSLP,
|
||||
getRawTransactions
|
||||
? getRawTransactions
|
||||
: tmpSLP.RawTransactions.getRawTransaction.bind(this)
|
||||
)
|
||||
|
||||
return slpValidator
|
||||
}
|
||||
|
||||
// Instantiate the local SLP validator.
|
||||
const slpValidator = createValidator(
|
||||
process.env.NETWORK,
|
||||
getRawTransactionsFromNode
|
||||
)
|
||||
|
||||
// Instantiate the bitboxproxy class in SLPJS.
|
||||
const bitboxproxy = new slp.BitboxNetwork(SLP, slpValidator)
|
||||
|
||||
const requestConfig = {
|
||||
method: "post",
|
||||
auth: {
|
||||
@@ -198,6 +109,7 @@ function formatTokenOutput(token) {
|
||||
function root(req, res, next) {
|
||||
return res.json({ status: "slp" })
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/list List all SLP tokens.
|
||||
* @apiName List all SLP tokens.
|
||||
@@ -206,7 +118,7 @@ function root(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/list" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/list" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -255,6 +167,7 @@ async function list(req, res, next) {
|
||||
return res.json({ error: `Error in /list: ${err.message}` })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/list/{tokenId} List single SLP token by id.
|
||||
* @apiName List single SLP token by id.
|
||||
@@ -263,7 +176,7 @@ async function list(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/list/259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/list/259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -292,6 +205,7 @@ async function listSingleToken(req, res, next) {
|
||||
return res.json({ error: `Error in /list/:tokenId: ${err.message}` })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /slp/list/ List Bulk SLP token .
|
||||
* @apiName List Bulk SLP token.
|
||||
@@ -300,7 +214,7 @@ async function listSingleToken(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "http://localhost:3000/v3/slp/list" -H "accept:application/json" -H "Content-Type: application/json" -d '{"tokenIds":["7380843cd1089a1a01783f86af37734dc99667a1cdc577391b5f6ea42fc1bfb4","9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0"]}'
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/slp/list" -H "accept:application/json" -H "Content-Type: application/json" -d '{"tokenIds":["7380843cd1089a1a01783f86af37734dc99667a1cdc577391b5f6ea42fc1bfb4","9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -445,7 +359,7 @@ async function lookupToken(tokenId) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/balancesForAddress/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/balancesForAddress/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -580,7 +494,7 @@ async function balancesForAddress(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "http://localhost:3000/v3/slp/balancesForAddress" -d "{\"addresses\":[\"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57\"]}" -H "accept:application/json"
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/slp/balancesForAddress" -d "{\"addresses\":[\"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57\"]}" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -756,7 +670,7 @@ async function balancesForAddressBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/balancesForToken/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/balancesForToken/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -814,6 +728,7 @@ async function balancesForTokenSingle(req, res, next) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/balance/{address}/{TokenId} List single slp token balance for address.
|
||||
* @apiName List single slp token balance for address.
|
||||
@@ -822,7 +737,7 @@ async function balancesForTokenSingle(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/balance/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m/1cda254d0a995c713b7955298ed246822bee487458cd9747a91d9e81d9d28125" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/balance/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m/1cda254d0a995c713b7955298ed246822bee487458cd9747a91d9e81d9d28125" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -892,37 +807,6 @@ async function balancesForAddressByTokenID(req, res, next) {
|
||||
tokenId: tokenRes.data.a[0].tokenDetails.tokenIdHex,
|
||||
balance: parseFloat(tokenRes.data.a[0].token_balance)
|
||||
}
|
||||
// const query2 = {
|
||||
// v: 3,
|
||||
// q: {
|
||||
// db: ["t"],
|
||||
// find: {
|
||||
// $query: {
|
||||
// "tokenDetails.tokenIdHex": tokenId
|
||||
// }
|
||||
// },
|
||||
// project: {
|
||||
// "tokenDetails.decimals": 1,
|
||||
// "tokenDetails.tokenIdHex": 1,
|
||||
// _id: 0
|
||||
// },
|
||||
// limit: 1000
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// const s2 = JSON.stringify(query2)
|
||||
// const b642 = Buffer.from(s2).toString("base64")
|
||||
// const url2 = `${process.env.SLPDB_URL}q/${b642}`
|
||||
//
|
||||
// const tokenRes2 = await axios.get(url2)
|
||||
// console.log("hello world", tokenRes2.data.t)
|
||||
// resVal = {
|
||||
// tokenId: token.tokenDetails.tokenIdHex,
|
||||
// balance: parseFloat(token.token_balance),
|
||||
// decimalCount: tokenRes2.data.t[0].tokenDetails.decimals
|
||||
// }
|
||||
// console.log("resVal", resVal)
|
||||
// return res.json(resVal)
|
||||
} else {
|
||||
resVal = {
|
||||
tokenId: tokenId,
|
||||
@@ -953,6 +837,7 @@ async function balancesForAddressByTokenID(req, res, next) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/convert/{address} Convert address to slpAddr, cashAddr and legacy.
|
||||
* @apiName Convert address to slpAddr, cashAddr and legacy.
|
||||
@@ -961,7 +846,7 @@ async function balancesForAddressByTokenID(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/convert/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/convert/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -1002,6 +887,7 @@ async function convertAddressSingle(req, res, next) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /slp/convert/ Convert multiple addresses to cash, legacy and simpleledger format.
|
||||
* @apiName Convert multiple addresses to cash, legacy and simpleledger format.
|
||||
@@ -1010,7 +896,7 @@ async function convertAddressSingle(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "http://localhost:3000/v3/slp/convert" -H "accept:application/json" -H "Content-Type: application/json" -d '{"addresses":["simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l"]}'
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/slp/convert" -H "accept:application/json" -H "Content-Type: application/json" -d '{"addresses":["simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -1061,6 +947,7 @@ async function convertAddressBulk(req, res, next) {
|
||||
res.status(200)
|
||||
return res.json(convertedAddresses)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /slp/validateTxid/ Validate multiple SLP transactions by txid.
|
||||
* @apiName Validate multiple SLP transactions by txid.
|
||||
@@ -1069,7 +956,7 @@ async function convertAddressBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "http://localhost:3000/v3/slp/validateTxid" -H "accept:application/json" -H "Content-Type: application/json" -d '{"txids":["f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a","fb0eeaa501a6e1acb721669c62a3f70741f48ae0fd7f4b8e1d72088785c51952"]}'
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/slp/validateTxid" -H "accept:application/json" -H "Content-Type: application/json" -d '{"txids":["f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a","fb0eeaa501a6e1acb721669c62a3f70741f48ae0fd7f4b8e1d72088785c51952"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -1093,32 +980,80 @@ async function validateBulk(req, res, next) {
|
||||
|
||||
wlogger.debug(`Executing slp/validate with these txids: `, txids)
|
||||
|
||||
// Validate each txid
|
||||
const validatePromises = txids.map(async txid => {
|
||||
try {
|
||||
// Dev note: must call module.exports to allow stubs in unit tests.
|
||||
const isValid = await module.exports.testableComponents.isValidSlpTxid(
|
||||
txid
|
||||
)
|
||||
|
||||
const tmp = {
|
||||
txid: txid,
|
||||
valid: isValid ? true : false
|
||||
}
|
||||
return tmp
|
||||
} catch (err) {
|
||||
//console.log(`err obj: ${util.inspect(err)}`)
|
||||
//console.log(`err.response.data: ${util.inspect(err.response.data)}`)
|
||||
throw err
|
||||
const query = {
|
||||
v: 3,
|
||||
q: {
|
||||
db: ["c", "u"],
|
||||
find: {
|
||||
"tx.h": { $in: txids }
|
||||
},
|
||||
limit: 300,
|
||||
project: { "slp.valid": 1, "tx.h": 1, "slp.invalidReason": 1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
const s = JSON.stringify(query)
|
||||
const b64 = Buffer.from(s).toString("base64")
|
||||
const url = `${process.env.SLPDB_URL}q/${b64}`
|
||||
|
||||
// Filter array to only valid txid results
|
||||
const validateResults = await axios.all(validatePromises)
|
||||
const validTxids = validateResults.filter(result => result)
|
||||
const options = generateCredentials()
|
||||
|
||||
// Get data from SLPDB.
|
||||
const tokenRes = await axios.get(url, options)
|
||||
// console.log(`tokenRes.data: ${JSON.stringify(tokenRes.data, null, 2)}`)
|
||||
|
||||
let formattedTokens = []
|
||||
|
||||
// Combine the arrays. Why? Generally there is nothing in the u array.
|
||||
const concatArray = tokenRes.data.c.concat(tokenRes.data.u)
|
||||
|
||||
const tokenIds = []
|
||||
if (concatArray.length > 0) {
|
||||
concatArray.forEach(token => {
|
||||
tokenIds.push(token.tx.h) // txid
|
||||
|
||||
const validationResult = {
|
||||
txid: token.tx.h,
|
||||
valid: token.slp.valid
|
||||
}
|
||||
|
||||
// If the txid is invalid, add the reason it's invalid.
|
||||
if (!validationResult.valid)
|
||||
validationResult.invalidReason = token.slp.invalidReason
|
||||
|
||||
formattedTokens.push(validationResult)
|
||||
})
|
||||
|
||||
// If a user-provided txid doesn't exist in the data, add it with
|
||||
// valid:false property.
|
||||
txids.forEach(txid => {
|
||||
if (!tokenIds.includes(txid)) {
|
||||
formattedTokens.push({
|
||||
txid: txid,
|
||||
valid: false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Catch a corner case of repeated txids. SLPDB will remove redundent TXIDs,
|
||||
// which will cause the output array to be smaller than the input array.
|
||||
if (txids.length > formattedTokens.length) {
|
||||
const newOutput = []
|
||||
for (let i = 0; i < txids.length; i++) {
|
||||
const thisTxid = txids[i]
|
||||
|
||||
// Find the element that matches the current txid.
|
||||
const elem = formattedTokens.filter(x => x.txid === thisTxid)
|
||||
|
||||
newOutput.push(elem[0])
|
||||
}
|
||||
|
||||
// Replace the original output object with the new output object.
|
||||
formattedTokens = newOutput
|
||||
}
|
||||
|
||||
res.status(200)
|
||||
return res.json(validTxids)
|
||||
return res.json(formattedTokens)
|
||||
} catch (err) {
|
||||
wlogger.error(`Error in slp.ts/validateBulk().`, err)
|
||||
|
||||
@@ -1133,6 +1068,7 @@ async function validateBulk(req, res, next) {
|
||||
return res.json({ error: util.inspect(err) })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/validateTxid/{txid} Validate single SLP transaction by txid.
|
||||
* @apiName Validate single SLP transaction by txid.
|
||||
@@ -1141,7 +1077,7 @@ async function validateBulk(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/validateTxid/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/validateTxid/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -1157,13 +1093,41 @@ async function validateSingle(req, res, next) {
|
||||
|
||||
wlogger.debug(`Executing slp/validate/:txid with this txid: `, txid)
|
||||
|
||||
// Validate txid
|
||||
// Dev note: must call module.exports to allow stubs in unit tests.
|
||||
const isValid = await module.exports.testableComponents.isValidSlpTxid(txid)
|
||||
const query = {
|
||||
v: 3,
|
||||
q: {
|
||||
db: ["c", "u"],
|
||||
find: {
|
||||
"tx.h": txid
|
||||
},
|
||||
limit: 300,
|
||||
project: { "slp.valid": 1, "tx.h": 1, "slp.invalidReason": 1 }
|
||||
}
|
||||
}
|
||||
|
||||
const tmp = {
|
||||
const options = generateCredentials()
|
||||
|
||||
const s = JSON.stringify(query)
|
||||
const b64 = Buffer.from(s).toString("base64")
|
||||
const url = `${process.env.SLPDB_URL}q/${b64}`
|
||||
|
||||
// Get data from SLPDB.
|
||||
const tokenRes = await axios.get(url, options)
|
||||
|
||||
// Default return value.
|
||||
let result = {
|
||||
txid: txid,
|
||||
valid: isValid ? true : false
|
||||
valid: false
|
||||
}
|
||||
|
||||
// Build result.
|
||||
const concatArray = tokenRes.data.c.concat(tokenRes.data.u)
|
||||
if (concatArray.length > 0) {
|
||||
result = {
|
||||
txid: concatArray[0].tx.h,
|
||||
valid: concatArray[0].slp.valid
|
||||
}
|
||||
if (!result.valid) result.invalidReason = concatArray[0].slp.invalidReason
|
||||
}
|
||||
|
||||
res.status(200)
|
||||
@@ -1189,278 +1153,6 @@ async function isValidSlpTxid(txid) {
|
||||
return isValid
|
||||
}
|
||||
|
||||
// Below are functions which are enabled for teams not using our javascript SDKs which still need to create txs
|
||||
// These should never be enabled on our public REST API
|
||||
|
||||
async function createTokenType1(req, res, next) {
|
||||
const fundingAddress = req.params.fundingAddress
|
||||
if (!fundingAddress || fundingAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingAddress can not be empty" })
|
||||
}
|
||||
|
||||
const fundingWif = req.params.fundingWif
|
||||
if (!fundingWif || fundingWif === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingWif can not be empty" })
|
||||
}
|
||||
|
||||
const tokenReceiverAddress = req.params.tokenReceiverAddress
|
||||
if (!tokenReceiverAddress || tokenReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "tokenReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const batonReceiverAddress = req.params.batonReceiverAddress
|
||||
if (!batonReceiverAddress || batonReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "batonReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress
|
||||
if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "bchChangeReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const decimals = req.params.decimals
|
||||
if (!decimals || decimals === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "decimals can not be empty" })
|
||||
}
|
||||
|
||||
const name = req.params.name
|
||||
if (!name || name === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "name can not be empty" })
|
||||
}
|
||||
|
||||
const symbol = req.params.symbol
|
||||
if (!symbol || symbol === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "symbol can not be empty" })
|
||||
}
|
||||
|
||||
const documentUri = req.params.documentUri
|
||||
if (!documentUri || documentUri === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "documentUri can not be empty" })
|
||||
}
|
||||
|
||||
const documentHash = req.params.documentHash
|
||||
if (!documentHash || documentHash === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "documentHash can not be empty" })
|
||||
}
|
||||
|
||||
const initialTokenQty = req.params.initialTokenQty
|
||||
if (!initialTokenQty || initialTokenQty === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "initialTokenQty can not be empty" })
|
||||
}
|
||||
|
||||
const token = await SLP.TokenType1.create({
|
||||
fundingAddress: fundingAddress,
|
||||
fundingWif: fundingWif,
|
||||
tokenReceiverAddress: tokenReceiverAddress,
|
||||
batonReceiverAddress: batonReceiverAddress,
|
||||
bchChangeReceiverAddress: bchChangeReceiverAddress,
|
||||
decimals: decimals,
|
||||
name: name,
|
||||
symbol: symbol,
|
||||
documentUri: documentUri,
|
||||
documentHash: documentHash,
|
||||
initialTokenQty: initialTokenQty
|
||||
})
|
||||
|
||||
res.status(200)
|
||||
return res.json(token)
|
||||
}
|
||||
|
||||
async function mintTokenType1(req, res, next) {
|
||||
const fundingAddress = req.params.fundingAddress
|
||||
if (!fundingAddress || fundingAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingAddress can not be empty" })
|
||||
}
|
||||
|
||||
const fundingWif = req.params.fundingWif
|
||||
if (!fundingWif || fundingWif === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingWif can not be empty" })
|
||||
}
|
||||
|
||||
const tokenReceiverAddress = req.params.tokenReceiverAddress
|
||||
if (!tokenReceiverAddress || tokenReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "tokenReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const batonReceiverAddress = req.params.batonReceiverAddress
|
||||
if (!batonReceiverAddress || batonReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "batonReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress
|
||||
if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "bchChangeReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const tokenId = req.params.tokenId
|
||||
if (!tokenId || tokenId === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "tokenId can not be empty" })
|
||||
}
|
||||
|
||||
const additionalTokenQty = req.params.additionalTokenQty
|
||||
if (!additionalTokenQty || additionalTokenQty === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "additionalTokenQty can not be empty" })
|
||||
}
|
||||
|
||||
const mint = await SLP.TokenType1.mint({
|
||||
fundingAddress: fundingAddress,
|
||||
fundingWif: fundingWif,
|
||||
tokenReceiverAddress: tokenReceiverAddress,
|
||||
batonReceiverAddress: batonReceiverAddress,
|
||||
bchChangeReceiverAddress: bchChangeReceiverAddress,
|
||||
tokenId: tokenId,
|
||||
additionalTokenQty: additionalTokenQty
|
||||
})
|
||||
|
||||
res.status(200)
|
||||
return res.json(mint)
|
||||
}
|
||||
|
||||
async function sendTokenType1(req, res, next) {
|
||||
const fundingAddress = req.params.fundingAddress
|
||||
if (!fundingAddress || fundingAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingAddress can not be empty" })
|
||||
}
|
||||
|
||||
const fundingWif = req.params.fundingWif
|
||||
if (!fundingWif || fundingWif === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingWif can not be empty" })
|
||||
}
|
||||
|
||||
const tokenReceiverAddress = req.params.tokenReceiverAddress
|
||||
if (!tokenReceiverAddress || tokenReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "tokenReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress
|
||||
if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "bchChangeReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const tokenId = req.params.tokenId
|
||||
if (!tokenId || tokenId === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "tokenId can not be empty" })
|
||||
}
|
||||
|
||||
const amount = req.params.amount
|
||||
if (!amount || amount === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "amount can not be empty" })
|
||||
}
|
||||
const send = await SLP.TokenType1.send({
|
||||
fundingAddress: fundingAddress,
|
||||
fundingWif: fundingWif,
|
||||
tokenReceiverAddress: tokenReceiverAddress,
|
||||
bchChangeReceiverAddress: bchChangeReceiverAddress,
|
||||
tokenId: tokenId,
|
||||
amount: amount
|
||||
})
|
||||
|
||||
res.status(200)
|
||||
return res.json(send)
|
||||
}
|
||||
|
||||
async function burnTokenType1(req, res, next) {
|
||||
const fundingAddress = req.params.fundingAddress
|
||||
if (!fundingAddress || fundingAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingAddress can not be empty" })
|
||||
}
|
||||
|
||||
const fundingWif = req.params.fundingWif
|
||||
if (!fundingWif || fundingWif === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingWif can not be empty" })
|
||||
}
|
||||
|
||||
const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress
|
||||
if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "bchChangeReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const tokenId = req.params.tokenId
|
||||
if (!tokenId || tokenId === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "tokenId can not be empty" })
|
||||
}
|
||||
|
||||
const amount = req.params.amount
|
||||
if (!amount || amount === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "amount can not be empty" })
|
||||
}
|
||||
|
||||
const burn = await SLP.TokenType1.burn({
|
||||
fundingAddress: fundingAddress,
|
||||
fundingWif: fundingWif,
|
||||
tokenId: tokenId,
|
||||
amount: amount,
|
||||
bchChangeReceiverAddress: bchChangeReceiverAddress
|
||||
})
|
||||
|
||||
res.status(200)
|
||||
return res.json(burn)
|
||||
}
|
||||
|
||||
async function burnAllTokenType1(req, res, next) {
|
||||
const fundingAddress = req.params.fundingAddress
|
||||
if (!fundingAddress || fundingAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingAddress can not be empty" })
|
||||
}
|
||||
|
||||
const fundingWif = req.params.fundingWif
|
||||
if (!fundingWif || fundingWif === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "fundingWif can not be empty" })
|
||||
}
|
||||
|
||||
const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress
|
||||
if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "bchChangeReceiverAddress can not be empty" })
|
||||
}
|
||||
|
||||
const tokenId = req.params.tokenId
|
||||
if (!tokenId || tokenId === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "tokenId can not be empty" })
|
||||
}
|
||||
|
||||
const burnAll = await SLP.TokenType1.burnAll({
|
||||
fundingAddress: fundingAddress,
|
||||
fundingWif: fundingWif,
|
||||
tokenId: tokenId,
|
||||
bchChangeReceiverAddress: bchChangeReceiverAddress
|
||||
})
|
||||
|
||||
res.status(200)
|
||||
return res.json(burnAll)
|
||||
}
|
||||
/**
|
||||
* @api {get} /slp/txDetails/{txid} SLP transaction details.
|
||||
* @apiName SLP transaction details.
|
||||
@@ -1469,7 +1161,7 @@ async function burnAllTokenType1(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/txDetails/8ab4ac5dea3f9024e3954ee5b61452955d659a34561f79ef62ac44e133d0980e" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/txDetails/8ab4ac5dea3f9024e3954ee5b61452955d659a34561f79ef62ac44e133d0980e" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -1487,22 +1179,48 @@ async function txDetails(req, res, next) {
|
||||
return res.json({ error: "This is not a txid" })
|
||||
}
|
||||
|
||||
let tmpSLP
|
||||
if (process.env.NETWORK === "testnet")
|
||||
tmpSLP = new SLPSDK({ restURL: process.env.TREST_URL })
|
||||
else tmpSLP = new SLPSDK({ restURL: process.env.REST_URL })
|
||||
const query = {
|
||||
v: 3,
|
||||
db: ["g"],
|
||||
q: {
|
||||
find: {
|
||||
"tx.h": txid
|
||||
},
|
||||
limit: 300
|
||||
}
|
||||
}
|
||||
|
||||
const tmpbitboxNetwork = new slp.BitboxNetwork(tmpSLP, slpValidator)
|
||||
//console.log(
|
||||
// `tmpbitboxNetwork: ${JSON.stringify(tmpbitboxNetwork, null, 2)}`
|
||||
//)
|
||||
const s = JSON.stringify(query)
|
||||
const b64 = Buffer.from(s).toString("base64")
|
||||
const url = `${process.env.SLPDB_URL}q/${b64}`
|
||||
|
||||
// Get TX info + token info
|
||||
const result = await tmpbitboxNetwork.getTransactionDetails(txid)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
const options = generateCredentials()
|
||||
|
||||
// Get token data from SLPDB
|
||||
const tokenRes = await axios.get(url, options)
|
||||
// console.log(`tokenRes: ${util.inspect(tokenRes)}`)
|
||||
|
||||
if (tokenRes.data.c.length === 0) {
|
||||
res.status(404)
|
||||
return res.json({ error: "TXID not found" })
|
||||
}
|
||||
|
||||
// Format the returned data to an object.
|
||||
const formatted = await formatToRestObject(tokenRes)
|
||||
// console.log(`formatted: ${JSON.stringify(formatted,null,2)}`)
|
||||
|
||||
// Get information on the transaction from Insight API.
|
||||
const retData = await transactions.transactionsFromInsight(txid)
|
||||
// console.log(`retData: ${JSON.stringify(retData,null,2)}`)
|
||||
|
||||
// Return both the tx data from Insight and the formatted token information.
|
||||
const response = {
|
||||
retData,
|
||||
...formatted
|
||||
}
|
||||
|
||||
res.status(200)
|
||||
return res.json(result)
|
||||
return res.json(response)
|
||||
} catch (err) {
|
||||
wlogger.error(`Error in slp.ts/txDetails().`, err)
|
||||
|
||||
@@ -1514,7 +1232,7 @@ async function txDetails(req, res, next) {
|
||||
}
|
||||
|
||||
// Handle corner case of mis-typted txid
|
||||
if (err.error.indexOf("Not found") > -1) {
|
||||
if (err.error && err.error.indexOf("Not found") > -1) {
|
||||
res.status(400)
|
||||
return res.json({ error: "TXID not found" })
|
||||
}
|
||||
@@ -1523,6 +1241,7 @@ async function txDetails(req, res, next) {
|
||||
return res.json({ error: util.inspect(err) })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/tokenStats/{tokenId} List stats for a single slp token.
|
||||
* @apiName List stats for a single slp token.
|
||||
@@ -1531,7 +1250,7 @@ async function txDetails(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/tokenStats/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/tokenStats/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -1587,6 +1306,7 @@ async function tokenStats(req, res, next) {
|
||||
return res.json({ error: `Error in /tokenStats: ${err.message}` })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/transactions/{tokenId}/{address} SLP transactions by tokenId and address.
|
||||
* @apiName SLP transactions by tokenId and address.
|
||||
@@ -1595,7 +1315,7 @@ async function tokenStats(req, res, next) {
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/slp/transactions/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0/simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l" -H "accept:application/json"
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/slp/transactions/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0/simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -1668,6 +1388,58 @@ async function txsTokenIdAddressSingle(req, res, next) {
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a Basic Authorization header for slpserve.
|
||||
function generateCredentials() {
|
||||
// Generate the Basic Authentication header for a private instance of SLPDB.
|
||||
const username = "BITBOX"
|
||||
const password = SLPDB_PASS
|
||||
const combined = `${username}:${password}`
|
||||
var base64Credential = Buffer.from(combined).toString("base64")
|
||||
var readyCredential = `Basic ${base64Credential}`
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
authorization: readyCredential
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
// Format the response from SLPDB into an object.
|
||||
async function formatToRestObject(slpDBFormat) {
|
||||
BigNumber.set({ DECIMAL_PLACES: 8 })
|
||||
|
||||
// console.log(`slpDBFormat.data: ${JSON.stringify(slpDBFormat.data, null, 2)}`)
|
||||
|
||||
const transaction = slpDBFormat.data.u.length
|
||||
? slpDBFormat.data.u[0]
|
||||
: slpDBFormat.data.c[0]
|
||||
|
||||
const inputs = transaction.in
|
||||
|
||||
const outputs = transaction.out
|
||||
const tokenOutputs = transaction.slp.detail.outputs
|
||||
|
||||
const sendOutputs = ["0"]
|
||||
tokenOutputs.map(x => {
|
||||
const string = parseFloat(x.amount) * 100000000
|
||||
sendOutputs.push(string.toString())
|
||||
})
|
||||
|
||||
const obj = {
|
||||
tokenInfo: {
|
||||
versionType: transaction.slp.detail.versionType,
|
||||
transactionType: transaction.slp.detail.transactionType,
|
||||
tokenIdHex: transaction.slp.detail.tokenIdHex,
|
||||
sendOutputs: sendOutputs
|
||||
},
|
||||
tokenIsValid: transaction.slp.valid
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
router,
|
||||
testableComponents: {
|
||||
@@ -1682,11 +1454,6 @@ module.exports = {
|
||||
convertAddressBulk,
|
||||
validateBulk,
|
||||
isValidSlpTxid,
|
||||
createTokenType1,
|
||||
mintTokenType1,
|
||||
sendTokenType1,
|
||||
burnTokenType1,
|
||||
burnAllTokenType1,
|
||||
txDetails,
|
||||
tokenStats,
|
||||
balancesForTokenSingle,
|
||||
|
||||
+545
-140
@@ -6,13 +6,14 @@ const axios = require("axios")
|
||||
|
||||
const routeUtils = require("./route-utils")
|
||||
const wlogger = require("../../util/winston-logging")
|
||||
const blockbook = require("./blockbook")
|
||||
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const BCHJS = require("@chris.troutner/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
// Used to convert error messages to strings, to safely pass to users.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
const BCHJS_TESTNET = `https://testnet.bchjs.cash/v3/`
|
||||
|
||||
const bchjsHTTP = axios.create({
|
||||
baseURL: process.env.RPC_BASEURL
|
||||
@@ -32,164 +33,568 @@ const requestConfig = {
|
||||
}
|
||||
}
|
||||
|
||||
router.get("/", root)
|
||||
router.get("/validateAddress/:address", validateAddressSingle)
|
||||
router.post("/validateAddress", validateAddressBulk)
|
||||
let _this
|
||||
|
||||
function root(req, res, next) {
|
||||
return res.json({ status: "util" })
|
||||
}
|
||||
/**
|
||||
* @api {get} /util/validateAddress/{address} Get information about single bitcoin cash address.
|
||||
* @apiName Information about single bitcoin cash address
|
||||
* @apiGroup Util
|
||||
* @apiDescription Returns information about single bitcoin cash address.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "http://localhost:3000/v3/util/validateAddress/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
async function validateAddressSingle(req, res, next) {
|
||||
try {
|
||||
const address = req.params.address
|
||||
if (!address || address === "") {
|
||||
res.status(400)
|
||||
return res.json({ error: "address can not be empty" })
|
||||
}
|
||||
class UtilRoute {
|
||||
constructor() {
|
||||
this.bchjs = bchjs
|
||||
this.blockbook = blockbook
|
||||
|
||||
const {
|
||||
BitboxHTTP,
|
||||
username,
|
||||
password,
|
||||
requestConfig
|
||||
} = routeUtils.setEnvVars()
|
||||
|
||||
requestConfig.data.id = "validateaddress"
|
||||
requestConfig.data.method = "validateaddress"
|
||||
requestConfig.data.params = [address]
|
||||
|
||||
const response = await BitboxHTTP(requestConfig)
|
||||
|
||||
return res.json(response.data.result)
|
||||
} catch (err) {
|
||||
// Attempt to decode the error message.
|
||||
const { msg, status } = routeUtils.decodeError(err)
|
||||
if (msg) {
|
||||
res.status(status)
|
||||
return res.json({ error: msg })
|
||||
}
|
||||
|
||||
wlogger.error(`Error in util.ts/validateAddressSingle().`, err)
|
||||
|
||||
res.status(500)
|
||||
return res.json({ error: util.inspect(err) })
|
||||
_this = this
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @api {post} /util/validateAddress Get information about bulk bitcoin cash addresses..
|
||||
* @apiName Information about bulk bitcoin cash addresses.
|
||||
* @apiGroup Util
|
||||
* @apiDescription Returns information about bulk bitcoin cash addresses..
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "http://localhost:3000/v3/util/validateAddress" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c","bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"]}'
|
||||
* curl -X POST "http://localhost:3000/v3/util/validateAddress" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c","bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"],"from": 1, "to": 5}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
async function validateAddressBulk(req, res, next) {
|
||||
try {
|
||||
const addresses = req.body.addresses
|
||||
|
||||
// Reject if addresses is not an array.
|
||||
if (!Array.isArray(addresses)) {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error: "addresses needs to be an array. Use GET for single address."
|
||||
})
|
||||
}
|
||||
root(req, res, next) {
|
||||
return res.json({ status: "util" })
|
||||
}
|
||||
|
||||
// Enforce array size rate limits
|
||||
if (!routeUtils.validateArraySize(req, addresses)) {
|
||||
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
|
||||
return res.json({
|
||||
error: `Array too large.`
|
||||
})
|
||||
}
|
||||
|
||||
// Validate each element in the array.
|
||||
for (let i = 0; i < addresses.length; i++) {
|
||||
const address = addresses[i]
|
||||
|
||||
// Ensure the input is a valid BCH address.
|
||||
try {
|
||||
var legacyAddr = bchjs.Address.toLegacyAddress(address)
|
||||
} catch (err) {
|
||||
/**
|
||||
* @api {get} /util/validateAddress/{address} Get information about single bitcoin cash address.
|
||||
* @apiName Information about single bitcoin cash address
|
||||
* @apiGroup Util
|
||||
* @apiDescription Returns information about single bitcoin cash address.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://mainnet.bchjs.cash/v3/util/validateAddress/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
async validateAddressSingle(req, res, next) {
|
||||
try {
|
||||
const address = req.params.address
|
||||
if (!address || address === "") {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error: `Invalid BCH address. Double check your address is valid: ${address}`
|
||||
})
|
||||
return res.json({ error: "address can not be empty" })
|
||||
}
|
||||
|
||||
// Prevent a common user error. Ensure they are using the correct network address.
|
||||
const networkIsValid = routeUtils.validateNetwork(address)
|
||||
if (!networkIsValid) {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.`
|
||||
})
|
||||
}
|
||||
}
|
||||
const {
|
||||
BitboxHTTP,
|
||||
username,
|
||||
password,
|
||||
requestConfig
|
||||
} = routeUtils.setEnvVars()
|
||||
|
||||
wlogger.debug(`Executing util/validate with these addresses: `, addresses)
|
||||
|
||||
const {
|
||||
BitboxHTTP,
|
||||
username,
|
||||
password,
|
||||
requestConfig
|
||||
} = routeUtils.setEnvVars()
|
||||
|
||||
// Loop through each address and creates an array of requests to call in parallel
|
||||
const promises = addresses.map(async address => {
|
||||
requestConfig.data.id = "validateaddress"
|
||||
requestConfig.data.method = "validateaddress"
|
||||
requestConfig.data.params = [address]
|
||||
|
||||
return await BitboxHTTP(requestConfig)
|
||||
})
|
||||
const response = await BitboxHTTP(requestConfig)
|
||||
|
||||
// Wait for all parallel Insight requests to return.
|
||||
const axiosResult = await axios.all(promises)
|
||||
return res.json(response.data.result)
|
||||
} catch (err) {
|
||||
// Attempt to decode the error message.
|
||||
const { msg, status } = routeUtils.decodeError(err)
|
||||
if (msg) {
|
||||
res.status(status)
|
||||
return res.json({ error: msg })
|
||||
}
|
||||
|
||||
// Retrieve the data part of the result.
|
||||
const result = axiosResult.map(x => x.data.result)
|
||||
wlogger.error(`Error in util.ts/validateAddressSingle().`, err)
|
||||
|
||||
res.status(200)
|
||||
return res.json(result)
|
||||
} catch (err) {
|
||||
// Attempt to decode the error message.
|
||||
const { msg, status } = routeUtils.decodeError(err)
|
||||
if (msg) {
|
||||
res.status(status)
|
||||
return res.json({ error: msg })
|
||||
res.status(500)
|
||||
return res.json({ error: util.inspect(err) })
|
||||
}
|
||||
}
|
||||
|
||||
wlogger.error(`Error in util.ts/validateAddressSingle().`, err)
|
||||
/**
|
||||
* @api {post} /util/validateAddress Get information about bulk bitcoin cash addresses..
|
||||
* @apiName Information about bulk bitcoin cash addresses.
|
||||
* @apiGroup Util
|
||||
* @apiDescription Returns information about bulk bitcoin cash addresses..
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/util/validateAddress" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c","bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"]}'
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/util/validateAddress" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c","bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"],"from": 1, "to": 5}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
async validateAddressBulk(req, res, next) {
|
||||
try {
|
||||
const addresses = req.body.addresses
|
||||
|
||||
res.status(500)
|
||||
return res.json({ error: util.inspect(err) })
|
||||
// Reject if addresses is not an array.
|
||||
if (!Array.isArray(addresses)) {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error: "addresses needs to be an array. Use GET for single address."
|
||||
})
|
||||
}
|
||||
|
||||
// Enforce array size rate limits
|
||||
if (!routeUtils.validateArraySize(req, addresses)) {
|
||||
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
|
||||
return res.json({
|
||||
error: `Array too large.`
|
||||
})
|
||||
}
|
||||
|
||||
// Validate each element in the array.
|
||||
for (let i = 0; i < addresses.length; i++) {
|
||||
const address = addresses[i]
|
||||
|
||||
// Ensure the input is a valid BCH address.
|
||||
try {
|
||||
var legacyAddr = bchjs.Address.toLegacyAddress(address)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error: `Invalid BCH address. Double check your address is valid: ${address}`
|
||||
})
|
||||
}
|
||||
|
||||
// Prevent a common user error. Ensure they are using the correct network address.
|
||||
const networkIsValid = routeUtils.validateNetwork(address)
|
||||
if (!networkIsValid) {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
wlogger.debug(`Executing util/validate with these addresses: `, addresses)
|
||||
|
||||
const {
|
||||
BitboxHTTP,
|
||||
username,
|
||||
password,
|
||||
requestConfig
|
||||
} = routeUtils.setEnvVars()
|
||||
|
||||
// Loop through each address and creates an array of requests to call in parallel
|
||||
const promises = addresses.map(async address => {
|
||||
requestConfig.data.id = "validateaddress"
|
||||
requestConfig.data.method = "validateaddress"
|
||||
requestConfig.data.params = [address]
|
||||
|
||||
return await BitboxHTTP(requestConfig)
|
||||
})
|
||||
|
||||
// Wait for all parallel Insight requests to return.
|
||||
const axiosResult = await axios.all(promises)
|
||||
|
||||
// Retrieve the data part of the result.
|
||||
const result = axiosResult.map(x => x.data.result)
|
||||
|
||||
res.status(200)
|
||||
return res.json(result)
|
||||
} catch (err) {
|
||||
// Attempt to decode the error message.
|
||||
const { msg, status } = routeUtils.decodeError(err)
|
||||
if (msg) {
|
||||
res.status(status)
|
||||
return res.json({ error: msg })
|
||||
}
|
||||
|
||||
wlogger.error(`Error in util.ts/validateAddressSingle().`, err)
|
||||
|
||||
res.status(500)
|
||||
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://mainnet.bchjs.cash/v3/util/sweep" -H "accept: application/json" -H "Content-Type: application/json" -d '{"wif":"Kz52sdXLAiKtH82iAFRi6aTZanWtW5Eyv37KWdpY6tTv7pjoHste", "balanceOnly": true}'
|
||||
* curl -X POST "https://mainnet.bchjs.cash/v3/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.blockbook.testableComponents.balanceFromBlockbook(
|
||||
fromAddr
|
||||
)
|
||||
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
||||
|
||||
// Total balance is the sum of the confirmed and unconfirmed balance.
|
||||
const totalBalance =
|
||||
Number(balances.balance) + Number(balances.unconfirmedBalance)
|
||||
|
||||
// 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.blockbook.testableComponents.utxosFromBlockbook(
|
||||
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.satoshis
|
||||
|
||||
transactionBuilder.addInput(utxo.txid, utxo.vout)
|
||||
}
|
||||
|
||||
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.satoshis
|
||||
)
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// 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.satoshis
|
||||
|
||||
transactionBuilder.addInput(utxo.txid, utxo.vout)
|
||||
}
|
||||
|
||||
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.satoshis
|
||||
)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const utilRoute = new UtilRoute()
|
||||
|
||||
router.get("/", utilRoute.root)
|
||||
router.get("/validateAddress/:address", utilRoute.validateAddressSingle)
|
||||
router.post("/validateAddress", utilRoute.validateAddressBulk)
|
||||
router.post("/sweep", utilRoute.sweepWif)
|
||||
|
||||
module.exports = {
|
||||
router,
|
||||
testableComponents: {
|
||||
root,
|
||||
validateAddressSingle,
|
||||
validateAddressBulk
|
||||
}
|
||||
// testableComponents: {
|
||||
// root,
|
||||
// validateAddressSingle,
|
||||
// validateAddressBulk,
|
||||
// sweepWif
|
||||
// }
|
||||
UtilRoute
|
||||
}
|
||||
|
||||
+6
-5
@@ -385,8 +385,9 @@ describe("#Block", () => {
|
||||
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
console.log(`process.env.RPC_BASEURL: ${process.env.RPC_BASEURL}`)
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(500, {
|
||||
error: {
|
||||
code: -1,
|
||||
@@ -410,7 +411,7 @@ describe("#Block", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockBlockHash })
|
||||
}
|
||||
|
||||
@@ -501,7 +502,7 @@ describe("#Block", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(500, {
|
||||
error: {
|
||||
code: -1,
|
||||
@@ -546,7 +547,7 @@ describe("#Block", () => {
|
||||
// Mock the Insight URL for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockBlockHash })
|
||||
|
||||
nock(`${process.env.BITCOINCOM_BASEURL}`)
|
||||
@@ -589,7 +590,7 @@ describe("#Block", () => {
|
||||
// Mock the Insight URL for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.times(2)
|
||||
.reply(200, { result: mockData.mockBlockHash })
|
||||
|
||||
|
||||
+20
-20
@@ -119,7 +119,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockBlockHash })
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockBlockchainInfo })
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: 126769 })
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, {
|
||||
result:
|
||||
"000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c"
|
||||
@@ -288,7 +288,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockBlockHeader })
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockBlockHeaderConcise })
|
||||
}
|
||||
|
||||
@@ -431,7 +431,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockBlockHeader })
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.times(2)
|
||||
.reply(200, { result: mockData.mockBlockHeaderConcise })
|
||||
}
|
||||
@@ -511,7 +511,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockChainTips })
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: 4049809.205246544 })
|
||||
}
|
||||
|
||||
@@ -592,7 +592,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockMempoolInfo })
|
||||
}
|
||||
|
||||
@@ -638,7 +638,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockRawMempool })
|
||||
}
|
||||
|
||||
@@ -690,7 +690,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: { error: "Transaction not in mempool" } })
|
||||
}
|
||||
|
||||
@@ -836,7 +836,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockTxOut })
|
||||
}
|
||||
|
||||
@@ -902,7 +902,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockTxOutProof })
|
||||
}
|
||||
|
||||
@@ -963,7 +963,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockTxOutProof })
|
||||
}
|
||||
|
||||
@@ -982,7 +982,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.times(2)
|
||||
.reply(200, { result: mockData.mockTxOutProof })
|
||||
}
|
||||
@@ -1042,7 +1042,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: [expected] })
|
||||
}
|
||||
|
||||
@@ -1109,7 +1109,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: [expected] })
|
||||
}
|
||||
|
||||
@@ -1132,7 +1132,7 @@ describe("#BlockchainRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.times(2)
|
||||
.reply(200, { result: [expected] })
|
||||
}
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ describe("#ControlRouter", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockGetNetworkInfo })
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -114,7 +114,7 @@ describe("#Mining", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockMiningInfo })
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ describe("#Mining", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: 517604755.6648782 })
|
||||
}
|
||||
|
||||
|
||||
@@ -313,6 +313,64 @@ const mockFoobar = {
|
||||
u: []
|
||||
}
|
||||
|
||||
const mockSingleValidTxid = {
|
||||
c: [
|
||||
{
|
||||
_id: "5d965fc27f1cf2184ca2fe73",
|
||||
tx: {
|
||||
h: "77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d"
|
||||
},
|
||||
slp: {
|
||||
valid: true,
|
||||
invalidReason: null
|
||||
}
|
||||
}
|
||||
],
|
||||
u: []
|
||||
}
|
||||
|
||||
const mockTwoValidTxid = {
|
||||
c: [
|
||||
{
|
||||
_id: "5d965fc27f1cf2184ca2fe73",
|
||||
tx: {
|
||||
h: "77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d"
|
||||
},
|
||||
slp: {
|
||||
valid: true,
|
||||
invalidReason: null
|
||||
}
|
||||
},
|
||||
{
|
||||
_id: "5d71e69758380a002c492a90",
|
||||
tx: {
|
||||
h: "552112f9e458dc7d1d8b328b0a6685e8af74a64b60b6846e7c86407f27f47e42"
|
||||
},
|
||||
slp: {
|
||||
valid: true,
|
||||
invalidReason: null
|
||||
}
|
||||
}
|
||||
],
|
||||
u: []
|
||||
}
|
||||
|
||||
const mockTwoRedundentTxid = {
|
||||
c: [
|
||||
{
|
||||
_id: "5db99c72a391ae2afd604bde",
|
||||
tx: {
|
||||
h: "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56"
|
||||
},
|
||||
slp: {
|
||||
valid: true,
|
||||
invalidReason: null
|
||||
}
|
||||
}
|
||||
],
|
||||
u: []
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mockList,
|
||||
mockSingleToken,
|
||||
@@ -324,5 +382,8 @@ module.exports = {
|
||||
mockTransactions,
|
||||
mockSingleTokenError,
|
||||
mockSingleAddress,
|
||||
mockFoobar
|
||||
mockFoobar,
|
||||
mockSingleValidTxid,
|
||||
mockTwoValidTxid,
|
||||
mockTwoRedundentTxid
|
||||
}
|
||||
|
||||
+114
-2
@@ -13,6 +13,118 @@ const mockAddress = {
|
||||
isscript: false
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mockAddress
|
||||
const mockBalance = {
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
itemsOnPage: 1000,
|
||||
address: "bitcoincash:qzp7gdl52edm24xlpkyqnza9rv33u3mdxyc77j3u6k",
|
||||
balance: "2546",
|
||||
totalReceived: "2546",
|
||||
totalSent: "0",
|
||||
unconfirmedBalance: "0",
|
||||
unconfirmedTxs: 0,
|
||||
txs: 2,
|
||||
txids: [
|
||||
"e190d13b88578132608ab912a4d2be3e55aa2792d6042d481ae21d700639de56",
|
||||
"44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6"
|
||||
]
|
||||
}
|
||||
|
||||
const mockUtxos = [
|
||||
{
|
||||
txid: "e190d13b88578132608ab912a4d2be3e55aa2792d6042d481ae21d700639de56",
|
||||
vout: 0,
|
||||
value: "2000",
|
||||
height: 605873,
|
||||
confirmations: 298,
|
||||
satoshis: 2000
|
||||
},
|
||||
{
|
||||
txid: "44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6",
|
||||
vout: 1,
|
||||
value: "546",
|
||||
height: 605873,
|
||||
confirmations: 298,
|
||||
satoshis: 546
|
||||
}
|
||||
]
|
||||
|
||||
const mockThreeUtxos = [
|
||||
{
|
||||
txid: "e190d13b88578132608ab912a4d2be3e55aa2792d6042d481ae21d700639de56",
|
||||
vout: 0,
|
||||
value: "2000",
|
||||
height: 605873,
|
||||
confirmations: 298,
|
||||
satoshis: 2000
|
||||
},
|
||||
{
|
||||
txid: "44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6",
|
||||
vout: 1,
|
||||
value: "546",
|
||||
height: 605873,
|
||||
confirmations: 298,
|
||||
satoshis: 546
|
||||
},
|
||||
{
|
||||
txid: "44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6",
|
||||
vout: 1,
|
||||
value: "546",
|
||||
height: 605873,
|
||||
confirmations: 298,
|
||||
satoshis: 546
|
||||
}
|
||||
]
|
||||
|
||||
const mockIsTokenUtxos = [
|
||||
false,
|
||||
{
|
||||
txid: "44e1f48c4093fc61db1a8fa206aa402fc34e482b3f788cb38c123ca0e1a35db6",
|
||||
vout: 1,
|
||||
value: "546",
|
||||
height: 605873,
|
||||
confirmations: 298,
|
||||
satoshis: 546,
|
||||
utxoType: "token",
|
||||
transactionType: "send",
|
||||
tokenId: "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7",
|
||||
tokenTicker: "TOK-CH",
|
||||
tokenName: "TokyoCash",
|
||||
tokenDocumentUrl: "",
|
||||
tokenDocumentHash: "",
|
||||
decimals: 8,
|
||||
tokenQty: 2
|
||||
}
|
||||
]
|
||||
|
||||
const tapUtxo = {
|
||||
txid: "2e030df12390186baf817fa2760540b886511e04bc520e88f6b4c2124cc2a7d4",
|
||||
vout: 1,
|
||||
value: "546",
|
||||
height: 606564,
|
||||
confirmations: 3,
|
||||
satoshis: 546,
|
||||
utxoType: "token",
|
||||
transactionType: "send",
|
||||
tokenId: "dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d",
|
||||
tokenTicker: "TAP",
|
||||
tokenName: "Thoughts and Prayers",
|
||||
tokenDocumentUrl: "",
|
||||
tokenDocumentHash: "",
|
||||
decimals: 0,
|
||||
tokenQty: 1
|
||||
}
|
||||
|
||||
const tokensOnly = [mockIsTokenUtxos[1], tapUtxo]
|
||||
|
||||
const multipleTokens = [false, mockIsTokenUtxos[1], tapUtxo]
|
||||
|
||||
module.exports = {
|
||||
mockAddress,
|
||||
mockBalance,
|
||||
mockUtxos,
|
||||
mockThreeUtxos,
|
||||
mockIsTokenUtxos,
|
||||
tokensOnly,
|
||||
multipleTokens
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ describe("#route-ratelimits", () => {
|
||||
RPC_USERNAME: process.env.RPC_USERNAME,
|
||||
RPC_PASSWORD: process.env.RPC_PASSWORD
|
||||
}
|
||||
|
||||
if (!process.env.JWT_AUTH_SERVER)
|
||||
process.env.JWT_AUTH_SERVER = `http://fakeurl.com/`
|
||||
})
|
||||
|
||||
// Setup the mocks before each test.
|
||||
|
||||
+16
-16
@@ -134,7 +134,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockDecodeRawTransaction })
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockDecodeRawTransaction })
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.times(2)
|
||||
.reply(200, { result: mockData.mockDecodeRawTransaction })
|
||||
}
|
||||
@@ -309,7 +309,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockDecodeScript })
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockDecodeScript })
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.times(2)
|
||||
.reply(200, { result: mockData.mockDecodeScript })
|
||||
}
|
||||
@@ -453,7 +453,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(500, {
|
||||
error: { message: "parameter 1 must be of length 64 (not 6)" }
|
||||
})
|
||||
@@ -473,7 +473,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockRawTransactionConcise })
|
||||
}
|
||||
|
||||
@@ -492,7 +492,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockRawTransactionVerbose })
|
||||
}
|
||||
|
||||
@@ -541,7 +541,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(500, {
|
||||
error: { message: "parameter 1 must be of length 64 (not 6)" }
|
||||
})
|
||||
@@ -561,7 +561,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockRawTransactionConcise })
|
||||
}
|
||||
|
||||
@@ -578,7 +578,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockRawTransactionVerbose })
|
||||
}
|
||||
|
||||
@@ -647,7 +647,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(500, {
|
||||
error: { message: "TX decode failed" }
|
||||
})
|
||||
@@ -671,7 +671,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, {
|
||||
result:
|
||||
"aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118"
|
||||
@@ -759,7 +759,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(500, {
|
||||
error: { message: "TX decode failed" }
|
||||
})
|
||||
@@ -781,7 +781,7 @@ describe("#Raw-Transactions", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, {
|
||||
result:
|
||||
"aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118"
|
||||
|
||||
+51
-23
@@ -102,7 +102,7 @@ describe("#SLP", () => {
|
||||
assert.equal(result.status, "slp", "Returns static string")
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
describe("list()", () => {
|
||||
// list route handler
|
||||
const list = slpRoute.testableComponents.list
|
||||
@@ -717,7 +717,7 @@ describe("#SLP", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.SLPDB_URL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockConvert })
|
||||
}
|
||||
|
||||
@@ -806,7 +806,7 @@ describe("#SLP", () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
*/
|
||||
describe("validateBulk()", () => {
|
||||
const validateBulk = slpRoute.testableComponents.validateBulk
|
||||
|
||||
@@ -835,9 +835,9 @@ describe("#SLP", () => {
|
||||
it("should validate array with single element", async () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
sandbox
|
||||
.stub(slpRoute.testableComponents, "isValidSlpTxid")
|
||||
.resolves(true)
|
||||
nock(`${process.env.SLPDB_URL}`)
|
||||
.get(uri => uri.includes("/"))
|
||||
.reply(200, mockData.mockSingleValidTxid)
|
||||
}
|
||||
|
||||
req.body.txids = [
|
||||
@@ -854,14 +854,37 @@ describe("#SLP", () => {
|
||||
it("should validate array with two elements", async () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
sandbox
|
||||
.stub(slpRoute.testableComponents, "isValidSlpTxid")
|
||||
.resolves(true)
|
||||
nock(`${process.env.SLPDB_URL}`)
|
||||
.get(uri => uri.includes("/"))
|
||||
.reply(200, mockData.mockTwoValidTxid)
|
||||
}
|
||||
|
||||
req.body.txids = [
|
||||
"77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d",
|
||||
"77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d"
|
||||
"552112f9e458dc7d1d8b328b0a6685e8af74a64b60b6846e7c86407f27f47e42"
|
||||
]
|
||||
|
||||
const result = await validateBulk(req, res)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAllKeys(result[0], ["txid", "valid"])
|
||||
assert.equal(result.length, 2)
|
||||
})
|
||||
|
||||
// Captures a regression bug that went out to production, captured in this
|
||||
// GitHub Issue: https://github.com/Bitcoin-com/rest.bitcoin.com/issues/518
|
||||
it("should return two elements if given two elements", async () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.SLPDB_URL}`)
|
||||
.get(uri => uri.includes("/"))
|
||||
.reply(200, mockData.mockTwoRedundentTxid)
|
||||
}
|
||||
|
||||
req.body.txids = [
|
||||
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
||||
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56"
|
||||
]
|
||||
|
||||
const result = await validateBulk(req, res)
|
||||
@@ -964,7 +987,7 @@ describe("#SLP", () => {
|
||||
})
|
||||
|
||||
describe("txDetails()", () => {
|
||||
let txDetails = slpRoute.testableComponents.txDetails
|
||||
const txDetails = slpRoute.testableComponents.txDetails
|
||||
|
||||
it("should throw 400 if txid is empty", async () => {
|
||||
const result = await txDetails(req, res)
|
||||
@@ -999,21 +1022,26 @@ describe("#SLP", () => {
|
||||
}
|
||||
})
|
||||
|
||||
it("should get tx details with token info", async () => {
|
||||
if (process.env.TEST === "unit") {
|
||||
// Mock the slpjs library for unit tests.
|
||||
pathStub.BitboxNetwork = slpjsMock.BitboxNetwork
|
||||
txDetails = slpRouteStub.testableComponents.txDetails
|
||||
}
|
||||
if (process.env.TEST === "integration") {
|
||||
it("should get tx details with token info", async () => {
|
||||
// TODO: add mocking for unit testing. How do I mock reponse form SLPDB
|
||||
// since it's not an object?
|
||||
|
||||
req.params.txid =
|
||||
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
|
||||
// if (process.env.TEST === "unit") {
|
||||
// // Mock the slpjs library for unit tests.
|
||||
// pathStub.BitboxNetwork = slpjsMock.BitboxNetwork
|
||||
// txDetails = slpRouteStub.testableComponents.txDetails
|
||||
// }
|
||||
|
||||
const result = await txDetails(req, res)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
req.params.txid =
|
||||
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
|
||||
|
||||
assert.hasAnyKeys(result, ["tokenIsValid", "tokenInfo"])
|
||||
})
|
||||
const result = await txDetails(req, res)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, ["tokenIsValid", "tokenInfo"])
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe("txsTokenIdAddressSingle()", () => {
|
||||
|
||||
+254
-8
@@ -12,6 +12,7 @@ const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const utilRoute = require("../../src/routes/v3/util")
|
||||
const nock = require("nock") // HTTP mocking
|
||||
const sinon = require("sinon")
|
||||
|
||||
let originalEnvVars // Used during transition from integration to unit tests.
|
||||
|
||||
@@ -19,12 +20,15 @@ let originalEnvVars // Used during transition from integration to unit tests.
|
||||
const { mockReq, mockRes } = require("./mocks/express-mocks")
|
||||
const mockData = require("./mocks/util-mocks")
|
||||
|
||||
// Used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const UtilRoute = utilRoute.UtilRoute
|
||||
const utilRouteInst = new utilRoute.UtilRoute()
|
||||
|
||||
describe("#Util", () => {
|
||||
let req, res
|
||||
let sandbox
|
||||
|
||||
before(() => {
|
||||
// Save existing environment variables.
|
||||
@@ -58,12 +62,16 @@ describe("#Util", () => {
|
||||
|
||||
// Activate nock if it's inactive.
|
||||
if (!nock.isActive()) nock.activate()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// Clean up HTTP mocks.
|
||||
nock.cleanAll() // clear interceptor list.
|
||||
nock.restore()
|
||||
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
after(() => {
|
||||
@@ -76,7 +84,7 @@ describe("#Util", () => {
|
||||
|
||||
describe("#root", async () => {
|
||||
// root route handler.
|
||||
const root = utilRoute.testableComponents.root
|
||||
const root = utilRouteInst.root
|
||||
|
||||
it("should respond to GET for base route", async () => {
|
||||
const result = root(req, res)
|
||||
@@ -87,7 +95,7 @@ describe("#Util", () => {
|
||||
})
|
||||
|
||||
describe("#validateAddressSingle", async () => {
|
||||
const validateAddress = utilRoute.testableComponents.validateAddressSingle
|
||||
const validateAddress = utilRouteInst.validateAddressSingle
|
||||
|
||||
it("should throw an error for an empty address", async () => {
|
||||
const result = await validateAddress(req, res)
|
||||
@@ -127,14 +135,14 @@ describe("#Util", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockAddress })
|
||||
}
|
||||
|
||||
req.params.address = `bitcoincash:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5hzljcrnd`
|
||||
|
||||
const result = await validateAddress(req, res)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"isvalid",
|
||||
@@ -148,7 +156,7 @@ describe("#Util", () => {
|
||||
})
|
||||
|
||||
describe("#validateAddressBulk", async () => {
|
||||
const validateAddressBulk = utilRoute.testableComponents.validateAddressBulk
|
||||
const validateAddressBulk = utilRouteInst.validateAddressBulk
|
||||
|
||||
it("should throw an error for an empty body", async () => {
|
||||
const result = await validateAddressBulk(req, res)
|
||||
@@ -247,7 +255,7 @@ describe("#Util", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.reply(200, { result: mockData.mockAddress })
|
||||
}
|
||||
|
||||
@@ -273,7 +281,7 @@ describe("#Util", () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === "unit") {
|
||||
nock(`${process.env.RPC_BASEURL}`)
|
||||
.post(``)
|
||||
.post(uri => uri.includes("/"))
|
||||
.times(2)
|
||||
.reply(200, { result: mockData.mockAddress })
|
||||
}
|
||||
@@ -297,4 +305,242 @@ 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(
|
||||
utilRouteInst.blockbook.testableComponents,
|
||||
"balanceFromBlockbook"
|
||||
)
|
||||
.resolves(mockData.mockBalance)
|
||||
|
||||
sandbox
|
||||
.stub(
|
||||
utilRouteInst.blockbook.testableComponents,
|
||||
"utxosFromBlockbook"
|
||||
)
|
||||
.resolves(mockData.mockUtxos)
|
||||
|
||||
sandbox
|
||||
.stub(utilRouteInst.bchjs.SLP.Utils, "tokenUtxoDetails")
|
||||
.resolves(mockData.mockIsTokenUtxos)
|
||||
|
||||
// Mock sendRawTransaction() so that the hex does not actually get broadcast
|
||||
// to the network.
|
||||
sandbox
|
||||
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction")
|
||||
.resolves("test-txid")
|
||||
|
||||
req.body = {
|
||||
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt",
|
||||
toAddr: "bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p"
|
||||
}
|
||||
|
||||
const result = await utilRouteInst.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.
|
||||
if (process.env.TEST === "unit") {
|
||||
sandbox
|
||||
.stub(
|
||||
utilRouteInst.blockbook.testableComponents,
|
||||
"balanceFromBlockbook"
|
||||
)
|
||||
.resolves(mockData.mockBalance)
|
||||
}
|
||||
|
||||
// Mock sendRawTransaction() so that the hex does not actually get broadcast
|
||||
// to the network.
|
||||
sandbox
|
||||
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction")
|
||||
.resolves("test-txid")
|
||||
|
||||
req.body = {
|
||||
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt",
|
||||
balanceOnly: true
|
||||
}
|
||||
|
||||
const result = await utilRouteInst.sweepWif(req, res)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isNumber(result)
|
||||
})
|
||||
|
||||
// Unit tests only
|
||||
if (process.env.TEST === "unit") {
|
||||
it("should generate transaction for valid BCH-only sweep", async () => {
|
||||
sandbox
|
||||
.stub(
|
||||
utilRouteInst.blockbook.testableComponents,
|
||||
"balanceFromBlockbook"
|
||||
)
|
||||
.resolves(mockData.mockBalance)
|
||||
|
||||
sandbox
|
||||
.stub(
|
||||
utilRouteInst.blockbook.testableComponents,
|
||||
"utxosFromBlockbook"
|
||||
)
|
||||
.resolves(mockData.mockUtxos)
|
||||
|
||||
// Force token utxo to appear as regular BCH utxo.
|
||||
sandbox
|
||||
.stub(utilRouteInst.bchjs.SLP.Utils, "tokenUtxoDetails")
|
||||
.resolves([false, false])
|
||||
|
||||
// Mock sendRawTransaction() so that the hex does not actually get broadcast
|
||||
// to the network.
|
||||
sandbox
|
||||
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction")
|
||||
.resolves("test-txid")
|
||||
|
||||
req.body = {
|
||||
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt",
|
||||
toAddr: "bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p"
|
||||
}
|
||||
|
||||
const result = await utilRouteInst.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(
|
||||
utilRouteInst.blockbook.testableComponents,
|
||||
"balanceFromBlockbook"
|
||||
)
|
||||
.resolves(mockData.mockBalance)
|
||||
|
||||
sandbox
|
||||
.stub(
|
||||
utilRouteInst.blockbook.testableComponents,
|
||||
"utxosFromBlockbook"
|
||||
)
|
||||
.resolves(mockData.mockUtxos)
|
||||
|
||||
// Force token utxo to appear as regular BCH utxo.
|
||||
sandbox
|
||||
.stub(utilRouteInst.bchjs.SLP.Utils, "tokenUtxoDetails")
|
||||
.resolves(mockData.tokensOnly)
|
||||
|
||||
// Mock sendRawTransaction() so that the hex does not actually get broadcast
|
||||
// to the network.
|
||||
sandbox
|
||||
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction")
|
||||
.resolves("test-txid")
|
||||
|
||||
req.body = {
|
||||
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt",
|
||||
toAddr: "bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p"
|
||||
}
|
||||
|
||||
const result = await utilRouteInst.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(
|
||||
utilRouteInst.blockbook.testableComponents,
|
||||
"balanceFromBlockbook"
|
||||
)
|
||||
.resolves(mockData.mockBalance)
|
||||
|
||||
sandbox
|
||||
.stub(
|
||||
utilRouteInst.blockbook.testableComponents,
|
||||
"utxosFromBlockbook"
|
||||
)
|
||||
.resolves(mockData.mockThreeUtxos)
|
||||
|
||||
// Force token utxo to appear as regular BCH utxo.
|
||||
sandbox
|
||||
.stub(utilRouteInst.bchjs.SLP.Utils, "tokenUtxoDetails")
|
||||
.resolves(mockData.multipleTokens)
|
||||
|
||||
// Mock sendRawTransaction() so that the hex does not actually get broadcast
|
||||
// to the network.
|
||||
sandbox
|
||||
.stub(utilRouteInst.bchjs.RawTransactions, "sendRawTransaction")
|
||||
.resolves("test-txid")
|
||||
|
||||
req.body = {
|
||||
wif: "L5GEFg1tETLWBugmhSo9Zc4ms968qVmfmTroDxsJ982AiudAQGyt",
|
||||
toAddr: "bitcoincash:qz2qn6zt4qmacf4r6c0e2pdcqsgnkxaa3ql2xpee6p"
|
||||
}
|
||||
|
||||
const result = await utilRouteInst.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"
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user