From 388a3ee53bb607c68180d3d0ebeefee70ea6ac83 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 10 Oct 2020 09:03:14 -0700 Subject: [PATCH 1/6] fix(validate2Single): Added alternative SLP TXID validator --- config/index.js | 4 +- package-lock.json | 34 ++++++++++++++ package.json | 2 + src/routes/v3/slp.js | 93 +++++++++++++++++++++++++++++++++++--- test/v3/integration/slp.js | 6 +++ 5 files changed, 132 insertions(+), 7 deletions(-) diff --git a/config/index.js b/config/index.js index 2f47cea..542ffb1 100644 --- a/config/index.js +++ b/config/index.js @@ -3,7 +3,9 @@ */ const config = { - apiTokenSecret: process.env.TOKENSECRET ? process.env.TOKENSECRET : 'secret-jwt-token' + apiTokenSecret: process.env.TOKENSECRET + ? process.env.TOKENSECRET + : 'secret-jwt-token' } module.exports = config diff --git a/package-lock.json b/package-lock.json index 710a89e..9f858c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -810,6 +810,11 @@ "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz", "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==" }, + "@types/big.js": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/big.js/-/big.js-4.0.5.tgz", + "integrity": "sha512-D9KFrAt05FDSqLo7PU9TDHfDgkarlwdkuwFsg7Zm4xl62tTNaz+zN+Tkcdx2wGLBbSMf8BnoMhOVeUGUaJfLKg==" + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -2238,6 +2243,14 @@ "safe-buffer": "^5.0.1" } }, + "bitcoin-rpc-promise-retry": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bitcoin-rpc-promise-retry/-/bitcoin-rpc-promise-retry-1.3.0.tgz", + "integrity": "sha512-xhldBhYwiUNY4spzkXXcU9LEq+nbSKJ5tvYWEUe/XTCe1VYoe8tEb5jJq4MBA2d+5egiUOA+P1QTTYm1FqPtOg==", + "requires": { + "bitcoind-rpc": "^0.8.0" + } + }, "bitcoincash-ops": { "version": "github:christroutner/bitcoincash-ops#0041f2d945aa0e893fbbb5c2ebdf6254f6ff0555", "from": "github:christroutner/bitcoincash-ops" @@ -2268,6 +2281,11 @@ "wif": "^2.0.1" } }, + "bitcoind-rpc": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/bitcoind-rpc/-/bitcoind-rpc-0.8.1.tgz", + "integrity": "sha512-NfhykAT/x/P1SOiog8UzltvTiv6A6d2X5VWJ3UjGeAqFLXv+IYHy+E4fFCBmgQRyIb1EIcyIZK1SVpSOGRHsaw==" + }, "bitcoinjs-message": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.1.3.tgz", @@ -15279,6 +15297,22 @@ "bignumber.js": "^9.0.0" } }, + "slp-validate": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/slp-validate/-/slp-validate-1.2.2.tgz", + "integrity": "sha512-V6XV44qup2gssw4+7ETGoK5eDdvs9UwTA5/bbyk3NhQzQzMq64hDlySJi8qC1R3pkFMbqoFslHUMbZKTm7VVQA==", + "requires": { + "@types/big.js": "^4.0.5", + "big.js": "5.2.2" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + } + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", diff --git a/package.json b/package.json index ae22554..d8dff62 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@psf/bch-js": "^3.6.3", "apidoc": "^0.23.0", "axios": "^0.19.0", + "bitcoin-rpc-promise-retry": "^1.3.0", "bitcore-lib-cash": "^8.20.3", "body-parser": "^1.18.3", "cookie-parser": "~1.4.3", @@ -55,6 +56,7 @@ "pg": "^8.0.0", "pg-hstore": "^2.3.2", "rate-limiter-flexible": "^2.0.0", + "slp-validate": "^1.2.2", "winston": "^3.2.1", "winston-daily-rotate-file": "^4.0.0" }, diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 8452722..24695e2 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -10,9 +10,16 @@ const routeUtils = new RouteUtils() const Slpdb = require('./services/slpdb') +// slp-validate dependencies. Used to validate SLP TXIDs independent of SLPDB. +const slpValidate = require('slp-validate') +const ValidatorType1 = slpValidate.ValidatorType1 +const RpcClient = require('bitcoin-rpc-promise-retry') +const RPC_CONNECTION_STRING = `http://${process.env.RPC_USERNAME}:${process.env.RPC_PASSWORD}@${process.env.RPC_IP}` + // const strftime = require('strftime') const wlogger = require('../../util/winston-logging') +// Instantiate a local copy of bch-js using the local REST API server. const LOCAL_RESTURL = process.env.LOCAL_RESTURL ? process.env.LOCAL_RESTURL : 'https://api.fullstack.cash/v3/' @@ -51,6 +58,7 @@ class Slp { constructor () { _this = this + // Encapsulate external libraries. _this.axios = axios _this.routeUtils = routeUtils _this.BigNumber = BigNumber @@ -58,6 +66,16 @@ class Slp { _this.rawTransactions = rawTransactions _this.slpdb = new Slpdb() + // Instantiate and encapsulate slp-validate and dependencies. + _this.rpc = new RpcClient(RPC_CONNECTION_STRING) + _this.slpValidator = new ValidatorType1({ + getRawTransaction: async txid => { + const rawTx = await _this.rpc.getRawTransaction(txid) + // console.log(`rawTx: ${JSON.stringify(rawTx, null, 2)}`) + return rawTx + } + }) + _this.router = router _this.router.get('/', _this.root) @@ -83,6 +101,7 @@ class Slp { ) _this.router.post('/generateSendOpReturn', _this.generateSendOpReturn) _this.router.post('/hydrateUtxos', _this.hydrateUtxos) + _this.router.get('/validateTxid2/:txid', _this.validate2Single) } // DRY error handler. @@ -1062,7 +1081,7 @@ class Slp { * @api {get} /slp/validateTxid/{txid} Validate single SLP transaction by txid. * @apiName Validate single SLP transaction by txid. * @apiGroup SLP - * @apiDescription Validate single SLP transaction by txid. + * @apiDescription Validate single SLP transaction by txid, using SLPDB. * * * @apiExample Example usage: @@ -1135,11 +1154,73 @@ class Slp { } } - // Returns a Boolean if the input TXID is a valid SLP TXID. - // async function isValidSlpTxid (txid) { - // const isValid = await slpValidator.isValidSlpTxid(txid) - // return isValid - // } + /** + * @api {get} /slp/validateTxid2/{txid} Validate single SLP transaction by txid. + * @apiName Validate single SLP transaction by txid. + * @apiGroup SLP + * @apiDescription Validate single SLP transaction by txid, using slp-validate. + * Slower, less efficient method of validating an SLP TXID using the slp-validate + * npm library. This method is independent of SLPDB and can be used when + * SLPDB return 'null' values. + * + * + * @apiExample Example usage: + * curl -X GET "https://api.fullstack.cash/v3/slp/validateTxid2/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json" + * + * + */ + async validate2Single (req, res, next) { + try { + const txid = req.params.txid + + // Validate input + if (!txid || txid === '') { + res.status(400) + return res.json({ error: 'txid can not be empty' }) + } + + wlogger.debug( + 'Executing slp/validate2Single/:txid with this txid: ', + txid + ) + + // Get the raw transaction from the full node. + try { + await _this.slpValidator.getRawTransaction(txid) + } catch (err) { + wlogger.error(`err in slp.js/validate2Single() with getRawTransaction(${txid}): `, err) + return _this.errorHandler(err, res) + } + + // false by default. + let isValid = false + + // Validat the TXID. + try { + isValid = await _this.slpValidator.isValidSlpTxid({ txid }) + // console.log('isValid: ', isValid) + } catch (error) { + console.log(error) + isValid = false + } + + // Default return value. + const result = { + txid: txid, + valid: false + } + + // Build result. + result.valid = isValid + + res.status(200) + return res.json(result) + } catch (err) { + wlogger.error('Error in slp.ts/validate2Single().', err) + + return _this.errorHandler(err, res) + } + } /** * @api {get} /slp/txDetails/{txid} SLP transaction details. diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index 279d077..6425de5 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -213,4 +213,10 @@ describe('#slp', () => { assert.equal(result.slpUtxos[2].utxos.length, 2) }) }) + + // describe('#validate2Single', () => { + // it('should validate a known valid TXID', async () => { + // + // }) + // }) }) From 5619c2fbede3d5641f9f2288dccd1a9e97c8603f Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 10 Oct 2020 12:03:02 -0700 Subject: [PATCH 2/6] Added a couple integration tests around validate2Single() --- test/v3/integration/slp.js | 42 +++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index 6425de5..f91e2d4 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -214,9 +214,41 @@ describe('#slp', () => { }) }) - // describe('#validate2Single', () => { - // it('should validate a known valid TXID', async () => { - // - // }) - // }) + describe('#validate2Single', () => { + it('should invalidate a known invalid TXID', async () => { + const txid = + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a' + + req.params.txid = txid + const result = await slp.validate2Single(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result.txid, txid) + assert.equal(result.valid, false) + }) + + it('should validate a known valid TXID', async () => { + const txid = + '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488' + + req.params.txid = txid + const result = await slp.validate2Single(req, res) + console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // assert.equal(result.txid, txid) + // assert.equal(result.valid, false) + }) + + it('should cancel if validation takes too long', async () => { + const txid = + '2822f7d21e17ca8c36846613dc52caf0d03e355a13022a6cec066d197e59bb27' + + req.params.txid = txid + const result = await slp.validate2Single(req, res) + console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // assert.equal(result.txid, txid) + // assert.equal(result.valid, false) + }) + }) }) From 2d041693298272918410169b59336ceb96b4a091 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 10 Oct 2020 12:19:27 -0700 Subject: [PATCH 3/6] Working on killing promise that takes too long --- package.json | 1 + src/routes/v3/slp.js | 46 +++++++++++++++++++++++++------------- test/v3/integration/slp.js | 12 +++++----- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index d8dff62..927389f 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "mocha": "^7.1.1", "morgan": "^1.9.1", "mqtt": "^4.0.0", + "p-timeout": "^3.2.0", "passport": "^0.4.0", "passport-anonymous": "^1.0.1", "passport-http": "^0.3.0", diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 24695e2..c14dc1b 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -15,6 +15,7 @@ const slpValidate = require('slp-validate') const ValidatorType1 = slpValidate.ValidatorType1 const RpcClient = require('bitcoin-rpc-promise-retry') const RPC_CONNECTION_STRING = `http://${process.env.RPC_USERNAME}:${process.env.RPC_PASSWORD}@${process.env.RPC_IP}` +const pTimeout = require('p-timeout') // const strftime = require('strftime') const wlogger = require('../../util/winston-logging') @@ -69,12 +70,13 @@ class Slp { // Instantiate and encapsulate slp-validate and dependencies. _this.rpc = new RpcClient(RPC_CONNECTION_STRING) _this.slpValidator = new ValidatorType1({ - getRawTransaction: async txid => { + getRawTransaction: async (txid) => { const rawTx = await _this.rpc.getRawTransaction(txid) // console.log(`rawTx: ${JSON.stringify(rawTx, null, 2)}`) return rawTx } }) + _this.pTimeout = pTimeout _this.router = router @@ -1188,30 +1190,42 @@ class Slp { try { await _this.slpValidator.getRawTransaction(txid) } catch (err) { - wlogger.error(`err in slp.js/validate2Single() with getRawTransaction(${txid}): `, err) + wlogger.error( + `err in slp.js/validate2Single() with getRawTransaction(${txid}): `, + err + ) return _this.errorHandler(err, res) } - // false by default. - let isValid = false - - // Validat the TXID. - try { - isValid = await _this.slpValidator.isValidSlpTxid({ txid }) - // console.log('isValid: ', isValid) - } catch (error) { - console.log(error) - isValid = false - } - + // null by default. // Default return value. const result = { txid: txid, - valid: false + isValid: null, + msg: '' + } + + // Time in milliseconds when the promise is canceled. + const TIMEOUT = 5000 + + // Validat the TXID. + try { + result.isValid = await _this.pTimeout( + _this.slpValidator.isValidSlpTxid({ txid }), + TIMEOUT, + `Validation took longer than ${TIMEOUT} milliseconds to complete.` + ) + + // isValid = await _this.slpValidator.isValidSlpTxid({ txid }) + // console.log('isValid: ', isValid) + } catch (error) { + console.log(error) + result.isValid = null + result.msg = error.message } // Build result. - result.valid = isValid + // result.valid = isValid res.status(200) return res.json(result) diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index f91e2d4..f16c0c8 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -224,7 +224,7 @@ describe('#slp', () => { // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.equal(result.txid, txid) - assert.equal(result.valid, false) + assert.equal(result.isValid, false) }) it('should validate a known valid TXID', async () => { @@ -233,10 +233,10 @@ describe('#slp', () => { req.params.txid = txid const result = await slp.validate2Single(req, res) - console.log(`result: ${JSON.stringify(result, null, 2)}`) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) - // assert.equal(result.txid, txid) - // assert.equal(result.valid, false) + assert.equal(result.txid, txid) + assert.equal(result.isValid, true) }) it('should cancel if validation takes too long', async () => { @@ -247,8 +247,8 @@ describe('#slp', () => { const result = await slp.validate2Single(req, res) console.log(`result: ${JSON.stringify(result, null, 2)}`) - // assert.equal(result.txid, txid) - // assert.equal(result.valid, false) + assert.equal(result.txid, txid) + assert.equal(result.isValid, null) }) }) }) From e399e050220adc9e478432856a0e243306ebf277 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 11 Oct 2020 09:10:35 -0700 Subject: [PATCH 4/6] fix(validateTxid2): Final form. Updated integration tests --- package-lock.json | 13 ++++++++--- package.json | 3 ++- src/routes/v3/slp.js | 46 ++++++++++++-------------------------- test/v3/integration/slp.js | 30 ++++++++++++++++--------- 4 files changed, 45 insertions(+), 47 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f858c4..0b56232 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6476,6 +6476,13 @@ "p-cancelable": "^1.0.0", "to-readable-stream": "^1.0.0", "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + } } }, "graceful-fs": { @@ -13359,9 +13366,9 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==" }, "p-each-series": { "version": "2.1.0", diff --git a/package.json b/package.json index 927389f..4478854 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "test-v3": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 60000 test/v3/", "test:temp": "export NETWORK=mainnet && mocha --timeout 25000 test/v3/slp.js", "test:integration": "mocha test/v3/integration", - "test:integration:slpdb": "mocha --timeout 25000 test/v3/integration/slp*.js", + "test:integration:slpdb": "mocha --timeout 25000 -g '#validate2Single' test/v3/integration/slp*.js", "coverage": "nyc report --reporter=text-lcov | coveralls", "coverage:report": "export NETWORK=mainnet && nyc --reporter=html mocha --timeout 25000 test/v3/", "docs": "./node_modules/.bin/apidoc -i src/routes/v3 -o docs" @@ -50,6 +50,7 @@ "mocha": "^7.1.1", "morgan": "^1.9.1", "mqtt": "^4.0.0", + "p-cancelable": "^2.0.0", "p-timeout": "^3.2.0", "passport": "^0.4.0", "passport-anonymous": "^1.0.1", diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index c14dc1b..5c79d0b 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -16,6 +16,7 @@ const ValidatorType1 = slpValidate.ValidatorType1 const RpcClient = require('bitcoin-rpc-promise-retry') const RPC_CONNECTION_STRING = `http://${process.env.RPC_USERNAME}:${process.env.RPC_PASSWORD}@${process.env.RPC_IP}` const pTimeout = require('p-timeout') +const PCancelable = require('p-cancelable') // const strftime = require('strftime') const wlogger = require('../../util/winston-logging') @@ -1162,8 +1163,8 @@ class Slp { * @apiGroup SLP * @apiDescription Validate single SLP transaction by txid, using slp-validate. * Slower, less efficient method of validating an SLP TXID using the slp-validate - * npm library. This method is independent of SLPDB and can be used when - * SLPDB return 'null' values. + * npm library. This method is independent of SLPDB and can be used as a fall-back + * when SLPDB returns 'null' values. * * * @apiExample Example usage: @@ -1186,17 +1187,6 @@ class Slp { txid ) - // Get the raw transaction from the full node. - try { - await _this.slpValidator.getRawTransaction(txid) - } catch (err) { - wlogger.error( - `err in slp.js/validate2Single() with getRawTransaction(${txid}): `, - err - ) - return _this.errorHandler(err, res) - } - // null by default. // Default return value. const result = { @@ -1205,31 +1195,23 @@ class Slp { msg: '' } - // Time in milliseconds when the promise is canceled. - const TIMEOUT = 5000 - - // Validat the TXID. - try { - result.isValid = await _this.pTimeout( - _this.slpValidator.isValidSlpTxid({ txid }), - TIMEOUT, - `Validation took longer than ${TIMEOUT} milliseconds to complete.` - ) - - // isValid = await _this.slpValidator.isValidSlpTxid({ txid }) - // console.log('isValid: ', isValid) - } catch (error) { - console.log(error) - result.isValid = null - result.msg = error.message + // Request options + const opt = { + method: 'get', + baseURL: `${process.env.SLP_API_URL}slp/validate/${txid}`, + timeout: 10000 // Exit after 10 seconds. } + const tokenRes = await _this.axios.request(opt) + // console.log(`tokenRes.data: ${JSON.stringify(tokenRes.data, null, 2)}`) + // console.log(`tokenRes: `, tokenRes) - // Build result. - // result.valid = isValid + // Overwrite the default value with the result from slp-api. + result.isValid = tokenRes.data.isValid res.status(200) return res.json(result) } catch (err) { + console.log('validate2Single error: ', err) wlogger.error('Error in slp.ts/validate2Single().', err) return _this.errorHandler(err, res) diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index f16c0c8..4de4e88 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -239,16 +239,24 @@ describe('#slp', () => { assert.equal(result.isValid, true) }) - it('should cancel if validation takes too long', async () => { - const txid = - '2822f7d21e17ca8c36846613dc52caf0d03e355a13022a6cec066d197e59bb27' - - req.params.txid = txid - const result = await slp.validate2Single(req, res) - console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.equal(result.txid, txid) - assert.equal(result.isValid, null) - }) + // CT 10-11-2020: This test is valid, but because of the cacheing built + // into slp-validate, it will not consistently pass or fail. To manually + // Run this test, re-start slp-api or find a token txid with a long DAG. + // it('should cancel if validation takes too long', async () => { + // const txid = + // 'eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579' + // // '2822f7d21e17ca8c36846613dc52caf0d03e355a13022a6cec066d197e59bb27' + // + // req.params.txid = txid + // const result = await slp.validate2Single(req, res) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.') + // assert.include( + // result.error, + // 'Could not communicate with full node', + // 'Error message expected' + // ) + // }) }) }) From 1959b20115408aeb8bfd23db7d888b82400fbe51 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 11 Oct 2020 09:44:28 -0700 Subject: [PATCH 5/6] Removed integrated slp-validate dependencies --- package-lock.json | 39 --------------------------------------- package.json | 4 ---- src/routes/v3/slp.js | 19 ------------------- 3 files changed, 62 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0b56232..e15f7e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -810,11 +810,6 @@ "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz", "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==" }, - "@types/big.js": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/big.js/-/big.js-4.0.5.tgz", - "integrity": "sha512-D9KFrAt05FDSqLo7PU9TDHfDgkarlwdkuwFsg7Zm4xl62tTNaz+zN+Tkcdx2wGLBbSMf8BnoMhOVeUGUaJfLKg==" - }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -2243,14 +2238,6 @@ "safe-buffer": "^5.0.1" } }, - "bitcoin-rpc-promise-retry": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/bitcoin-rpc-promise-retry/-/bitcoin-rpc-promise-retry-1.3.0.tgz", - "integrity": "sha512-xhldBhYwiUNY4spzkXXcU9LEq+nbSKJ5tvYWEUe/XTCe1VYoe8tEb5jJq4MBA2d+5egiUOA+P1QTTYm1FqPtOg==", - "requires": { - "bitcoind-rpc": "^0.8.0" - } - }, "bitcoincash-ops": { "version": "github:christroutner/bitcoincash-ops#0041f2d945aa0e893fbbb5c2ebdf6254f6ff0555", "from": "github:christroutner/bitcoincash-ops" @@ -2281,11 +2268,6 @@ "wif": "^2.0.1" } }, - "bitcoind-rpc": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/bitcoind-rpc/-/bitcoind-rpc-0.8.1.tgz", - "integrity": "sha512-NfhykAT/x/P1SOiog8UzltvTiv6A6d2X5VWJ3UjGeAqFLXv+IYHy+E4fFCBmgQRyIb1EIcyIZK1SVpSOGRHsaw==" - }, "bitcoinjs-message": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.1.3.tgz", @@ -13365,11 +13347,6 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "p-cancelable": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", - "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==" - }, "p-each-series": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", @@ -15304,22 +15281,6 @@ "bignumber.js": "^9.0.0" } }, - "slp-validate": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/slp-validate/-/slp-validate-1.2.2.tgz", - "integrity": "sha512-V6XV44qup2gssw4+7ETGoK5eDdvs9UwTA5/bbyk3NhQzQzMq64hDlySJi8qC1R3pkFMbqoFslHUMbZKTm7VVQA==", - "requires": { - "@types/big.js": "^4.0.5", - "big.js": "5.2.2" - }, - "dependencies": { - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" - } - } - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", diff --git a/package.json b/package.json index 4478854..9d226ba 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "@psf/bch-js": "^3.6.3", "apidoc": "^0.23.0", "axios": "^0.19.0", - "bitcoin-rpc-promise-retry": "^1.3.0", "bitcore-lib-cash": "^8.20.3", "body-parser": "^1.18.3", "cookie-parser": "~1.4.3", @@ -50,15 +49,12 @@ "mocha": "^7.1.1", "morgan": "^1.9.1", "mqtt": "^4.0.0", - "p-cancelable": "^2.0.0", - "p-timeout": "^3.2.0", "passport": "^0.4.0", "passport-anonymous": "^1.0.1", "passport-http": "^0.3.0", "pg": "^8.0.0", "pg-hstore": "^2.3.2", "rate-limiter-flexible": "^2.0.0", - "slp-validate": "^1.2.2", "winston": "^3.2.1", "winston-daily-rotate-file": "^4.0.0" }, diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 5c79d0b..42130ed 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -10,14 +10,6 @@ const routeUtils = new RouteUtils() const Slpdb = require('./services/slpdb') -// slp-validate dependencies. Used to validate SLP TXIDs independent of SLPDB. -const slpValidate = require('slp-validate') -const ValidatorType1 = slpValidate.ValidatorType1 -const RpcClient = require('bitcoin-rpc-promise-retry') -const RPC_CONNECTION_STRING = `http://${process.env.RPC_USERNAME}:${process.env.RPC_PASSWORD}@${process.env.RPC_IP}` -const pTimeout = require('p-timeout') -const PCancelable = require('p-cancelable') - // const strftime = require('strftime') const wlogger = require('../../util/winston-logging') @@ -68,17 +60,6 @@ class Slp { _this.rawTransactions = rawTransactions _this.slpdb = new Slpdb() - // Instantiate and encapsulate slp-validate and dependencies. - _this.rpc = new RpcClient(RPC_CONNECTION_STRING) - _this.slpValidator = new ValidatorType1({ - getRawTransaction: async (txid) => { - const rawTx = await _this.rpc.getRawTransaction(txid) - // console.log(`rawTx: ${JSON.stringify(rawTx, null, 2)}`) - return rawTx - } - }) - _this.pTimeout = pTimeout - _this.router = router _this.router.get('/', _this.root) From a2c30aee6d2df5b66302766acdaf94178a52561a Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 11 Oct 2020 10:03:24 -0700 Subject: [PATCH 6/6] fix(validateTxid2): Added unit tests --- src/routes/v3/slp.js | 4 +- test/v3/mocks/slp-mocks.js | 24 ++++++++---- test/v3/slp.js | 75 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 10 deletions(-) diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 42130ed..551a694 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -73,6 +73,7 @@ class Slp { _this.router.post('/convert', _this.convertAddressBulk) _this.router.post('/validateTxid', _this.validateBulk) _this.router.get('/validateTxid/:txid', _this.validateSingle) + _this.router.get('/validateTxid2/:txid', _this.validate2Single) _this.router.get('/txDetails/:txid', _this.txDetails) _this.router.get('/tokenStats/:tokenId', _this.tokenStats) _this.router.get( @@ -85,7 +86,6 @@ class Slp { ) _this.router.post('/generateSendOpReturn', _this.generateSendOpReturn) _this.router.post('/hydrateUtxos', _this.hydrateUtxos) - _this.router.get('/validateTxid2/:txid', _this.validate2Single) } // DRY error handler. @@ -1192,7 +1192,7 @@ class Slp { res.status(200) return res.json(result) } catch (err) { - console.log('validate2Single error: ', err) + // console.log('validate2Single error: ', err) wlogger.error('Error in slp.ts/validate2Single().', err) return _this.errorHandler(err, res) diff --git a/test/v3/mocks/slp-mocks.js b/test/v3/mocks/slp-mocks.js index c90e2db..198e9b7 100644 --- a/test/v3/mocks/slp-mocks.js +++ b/test/v3/mocks/slp-mocks.js @@ -352,7 +352,8 @@ const mockTxHistory = [ e: { h: '7bdd586ebd1e5f3dfd5295ab6e896c48b25855ab77a72c035ce1e7aacc965c2d', i: 1, - s: 'RzBEAiAsu5o9EnxgZChjxKygxhGLhlfxJJbQfd4PP/vp2od3fAIgPpMqg+Sp4YpiInQZ6weJwELq8LEIrHXJpueBz7yF5gRBIQLsyL3809mRphc/+tPAcTQO5bi0kqZRFKpKIHXE3qN8Rw==', + s: + 'RzBEAiAsu5o9EnxgZChjxKygxhGLhlfxJJbQfd4PP/vp2od3fAIgPpMqg+Sp4YpiInQZ6weJwELq8LEIrHXJpueBz7yF5gRBIQLsyL3809mRphc/+tPAcTQO5bi0kqZRFKpKIHXE3qN8Rw==', a: 'simpleledger:qrrrpqmdkggpnw0czwg0jgcjd7yhu25jy5zxh2gqdq' } }, @@ -361,7 +362,8 @@ const mockTxHistory = [ e: { h: 'df49feff24dc34a10a44a0cbd7c908d964801ec7218512c84574fbce698535f0', i: 3, - s: 'SDBFAiEAhW3zbKTlPrOXD2E2oEcNof6vCMPGYIg8vOVSE9c8IakCIFLg/gxydG9eL8HMAzkScGElKWJRnfhVMpMHU3evNcrZQSEDRS7F+pSC8OxSldsT4FctJLZBU7f2+FDiE05ae1xqtN0=', + s: + 'SDBFAiEAhW3zbKTlPrOXD2E2oEcNof6vCMPGYIg8vOVSE9c8IakCIFLg/gxydG9eL8HMAzkScGElKWJRnfhVMpMHU3evNcrZQSEDRS7F+pSC8OxSldsT4FctJLZBU7f2+FDiE05ae1xqtN0=', a: 'simpleledger:qpkpeqfslejw5pptzcy25h2jxhsc9k0vts43n26up0' } } @@ -371,7 +373,8 @@ const mockTxHistory = [ e: { v: 0, i: 0, - s: 'agRTTFAAAQEEU0VORCA46XxdfTWFosvz+VgMgsozmF+csIRdTcziIMtwn5U4sAgAAAAAAJiWgAgAAAAAJHfU+g==' + s: + 'agRTTFAAAQEEU0VORCA46XxdfTWFosvz+VgMgsozmF+csIRdTcziIMtwn5U4sAgAAAAAAJiWgAgAAAAAJHfU+g==' } }, { @@ -402,7 +405,8 @@ const mockTxHistory = [ slp: { detail: { decimals: 8, - tokenIdHex: '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0', + tokenIdHex: + '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0', transactionType: 'SEND', versionType: 1, documentUri: 'psfoundation.cash', @@ -439,7 +443,8 @@ const mockTxHistory = [ e: { h: 'e4e1e1f6b502cbd42f69b919634cea3e37fc8595e6ca800e0a7d8f6dcdfb249e', i: 3, - s: 'SDBFAiEAjCGRmU28x24LMTwg5XqC+fLf3zGhTYCsSWpmF8Eshq0CIAJhxAKMbwKWC4ASmjWzplpno2ch+hGNUD6HNWOdbd4SQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=', + s: + 'SDBFAiEAjCGRmU28x24LMTwg5XqC+fLf3zGhTYCsSWpmF8Eshq0CIAJhxAKMbwKWC4ASmjWzplpno2ch+hGNUD6HNWOdbd4SQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=', a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7' } }, @@ -448,7 +453,8 @@ const mockTxHistory = [ e: { h: '438420345dd8b7fb4ea74aaf2e3090f44899bf3c71662243946a441de6dae720', i: 2, - s: 'SDBFAiEAhmLYL4QY3mvZo3/i0XG9PJAYMruw3MaGOLJ3Z4si7hwCIAaaGwRnj/cZK3L/KKFBoMtItpGRZ10GowNN//vgn6PKQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=', + s: + 'SDBFAiEAhmLYL4QY3mvZo3/i0XG9PJAYMruw3MaGOLJ3Z4si7hwCIAaaGwRnj/cZK3L/KKFBoMtItpGRZ10GowNN//vgn6PKQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=', a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7' } } @@ -458,7 +464,8 @@ const mockTxHistory = [ e: { v: 0, i: 0, - s: 'agRTTFAAAQEEU0VORCCk+1wtoaoGTiUBikP5FlBABx2emEuhkMIip/WQU6+EsggAAAAAAA9CQAgAAAAXQnHEwA==' + s: + 'agRTTFAAAQEEU0VORCCk+1wtoaoGTiUBikP5FlBABx2emEuhkMIip/WQU6+EsggAAAAAAA9CQAgAAAAXQnHEwA==' } }, { @@ -489,7 +496,8 @@ const mockTxHistory = [ slp: { detail: { decimals: 2, - tokenIdHex: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', + tokenIdHex: + 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', transactionType: 'SEND', versionType: 1, documentUri: 'troutsblog.com', diff --git a/test/v3/slp.js b/test/v3/slp.js index 47d5537..e847904 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -362,6 +362,7 @@ describe('#SLP', () => { assert.hasAllKeys(result, ['error']) assert.include(result.error, 'Array too large') }) + it('returns proper error when downstream service stalls', async () => { // Mock the timeout error. sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' }) @@ -462,6 +463,80 @@ describe('#SLP', () => { }) }) + describe('#validate2Single', () => { + it('should throw 400 if txid is empty', async () => { + req.params.txid = '' + const result = await slpRoute.validate2Single(req, res) + // console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ['error']) + assert.include(result.error, 'txid can not be empty') + }) + + it('should invalidate a known invalid TXID', async () => { + if (process.env.TEST === 'unit') { + // Mock to prevent live network connection. + sandbox + .stub(slpRoute.axios, 'request') + .resolves({ data: { isValid: false } }) + } + + const txid = + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a' + + req.params.txid = txid + const result = await slpRoute.validate2Single(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result.txid, txid) + assert.equal(result.isValid, false) + }) + + it('should validate a known valid TXID', async () => { + if (process.env.TEST === 'unit') { + // Mock to prevent live network connection. + sandbox + .stub(slpRoute.axios, 'request') + .resolves({ data: { isValid: true } }) + } + + const txid = + '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488' + + req.params.txid = txid + const result = await slpRoute.validate2Single(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result.txid, txid) + assert.equal(result.isValid, true) + }) + + // This test can only be run as a mocked unit test. It's too inconsistent + // to run as an integration test, due to the caching built into slp-validate. + if (process.env.TEST === 'unit') { + it('should cancel if validation takes too long', async () => { + // Mock the timeout error. + sandbox.stub(slpRoute.axios, 'request').throws({ + code: 'ECONNABORTED' + }) + + const txid = + 'eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579' + + req.params.txid = txid + const result = await slpRoute.validate2Single(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.') + assert.include( + result.error, + 'Could not communicate with full node', + 'Error message expected' + ) + }) + } + }) + describe('tokenStats()', () => { it('should throw 400 if tokenID is empty', async () => { req.params.tokenId = ''