From 3b873b6064a6fe720c779a789567b863b93107f0 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 8 Feb 2021 17:10:32 -0800 Subject: [PATCH] feat(waterfallValidateTxid): SLP.Utils.waterfallValidateTxid(txid) --- package.json | 2 +- src/slp/utils.js | 57 ++++++++++++++++++++-------- test/unit/slp-utils.js | 86 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 3bd5509..ff7b565 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "test:integration:local:bchn": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/", "test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/testnet", "test:temp": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#waterfallValidateTxid' test/integration/", - "test:temp2": "mocha --timeout=30000 -g '#getTxData' test/unit/", + "test:temp2": "mocha --timeout=30000 -g '#waterfallValidateTxid' test/unit/", "coverage": "nyc report --reporter=text-lcov | coveralls", "coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/", "docs": "./node_modules/.bin/apidoc -i src/ -o docs", diff --git a/src/slp/utils.js b/src/slp/utils.js index 37090ff..f62575e 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -1426,18 +1426,43 @@ class Utils { } } - // This function aggregates all the available SLP token validation sources. - // It starts with the fastest, most-efficient source first, and continues - // to other validation sources until the txid is validated (true or false). - // If the txid goes through all sources and can't be validated, it will - // return null. - // - // Validation sources from most efficient to least efficient: - // - SLPDB whitelist - // - SLPDB general - // - slp-api - // - // Currently only supports a single txid at a time. + /** + * @api SLP.Utils.waterfallValidateTxid() waterfallValidateTxid() + * @apiName waterfallValidateTxid + * @apiGroup SLP Utils + * @apiDescription Use multiple validators to validate an SLP TXID. + * + * This function aggregates all the available SLP token validation sources. + * It starts with the fastest, most-efficient source first, and continues + * to other validation sources until the txid is validated (true or false). + * If the txid goes through all sources and can't be validated, it will + * return null. + * + * Validation sources from most efficient to least efficient: + * - SLPDB with whitelist filter + * - SLPDB general purpose + * - slp-api + * + * Currently only supports a single txid at a time. + * + * @apiExample Example usage: + * + * // validate single SLP txid + * (async () => { + * try { + * let validated = await bchjs.SLP.Utils.waterfallValidateTxid( + * "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb" + * ); + * console.log(validated); + * } catch (error) { + * console.error(error); + * } + * })(); + * + * // returns + * true + */ + async waterfallValidateTxid (txid) { try { const cachedTxValidation = {} @@ -1471,7 +1496,7 @@ class Utils { const whitelistResult = await this.validateTxid3(txid) // console.log( // `whitelist-SLPDB for ${txid}: ${JSON.stringify( - // isValid, + // whitelistResult, // null, // 2 // )}` @@ -1491,11 +1516,11 @@ class Utils { // Try the general SLPDB, if the whitelist returned null. const generalResult = await this.validateTxid(txid) // console.log( - // `validateTxid() isValid: ${JSON.stringify(isValid, null, 2)}` + // `validateTxid() isValid: ${JSON.stringify(generalResult, null, 2)}` // ) // Safely retrieve the returned value. - if (generalResult[0] === null) isValid = generalResult[0].valid + if (generalResult[0] !== null) isValid = generalResult[0].valid // Exit if isValid is not null. if (isValid !== null) { @@ -1513,7 +1538,7 @@ class Utils { /* exit quietly */ } // console.log( - // `slp-validate isValid: ${JSON.stringify(isValid, null, 2)}` + // `slpValidateResult: ${JSON.stringify(slpValidateResult, null, 2)}` // ) // Exit if isValid is not null. diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index 1ac210c..8e78438 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -1855,7 +1855,8 @@ describe('#SLP Utils', () => { .throws({ error: 'TXID not found' }) } - const txid = 'd284e71227ec89f714b964d8eda595be6392bebd2fac46082bc5a9ce6fb7b33e' + const txid = + 'd284e71227ec89f714b964d8eda595be6392bebd2fac46082bc5a9ce6fb7b33e' await uut.Utils.txDetails(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) @@ -1875,7 +1876,8 @@ describe('#SLP Utils', () => { .resolves({ data: mockData.mockTxDetails }) } - const txid = '9dbaaafc48c49a21beabada8de632009288a2cd52eecefd0c00edcffca9955d0' + const txid = + '9dbaaafc48c49a21beabada8de632009288a2cd52eecefd0c00edcffca9955d0' const result = await uut.Utils.txDetails(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) @@ -2055,6 +2057,86 @@ describe('#SLP Utils', () => { */ }) + describe('#waterfallValidateTxid', () => { + // This test ensures that the whitelist SLPDB is called before the + // general purpose SLPDB. + it('should call validateTxid3() first', async () => { + const txid = 'fakeTxid' + + const retVal = [{ txid, valid: true }] + + // Use stubs to force the desired code path. + sandbox.stub(uut.Utils, 'validateTxid3').resolves(retVal) + sandbox + .stub(uut.Utils, 'validateTxid') + .rejects(new Error('Unexpected code path')) + sandbox + .stub(uut.Utils, 'validateTxid2') + .rejects(new Error('Unexpected code path')) + + const result = await uut.Utils.waterfallValidateTxid(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result, true) + }) + + // This test ensures that the general purpose SLPDB is called after the + // whitelist SLPDB, but before slp-api. + it('should call validateTxid() second', async () => { + const txid = 'fakeTxid' + + const retVal1 = [{ txid, valid: null }] + const retVal2 = [{ txid, valid: true }] + + // Use stubs to force the desired code path. + sandbox.stub(uut.Utils, 'validateTxid3').resolves(retVal1) + sandbox.stub(uut.Utils, 'validateTxid').resolves(retVal2) + sandbox + .stub(uut.Utils, 'validateTxid2') + .rejects(new Error('Unexpected code path')) + + const result = await uut.Utils.waterfallValidateTxid(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result, true) + }) + + // This test ensures that slp-api is called if both SLPDBs return null. + it('should call slp-api last', async () => { + const txid = 'fakeTxid' + + const retVal1 = [{ txid, valid: null }] + + // Use stubs to force the desired code path. + sandbox.stub(uut.Utils, 'validateTxid3').resolves(retVal1) + sandbox.stub(uut.Utils, 'validateTxid').resolves(retVal1) + sandbox.stub(uut.Utils, 'validateTxid2').resolves({ isValid: true }) + + const result = await uut.Utils.waterfallValidateTxid(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result, true) + }) + + it('catches and throws an error', async () => { + try { + const txid = 'fakeTxid' + + // Force an error + sandbox + .stub(uut.Utils, 'validateTxid3') + .rejects(new Error('fake error')) + + await await uut.Utils.waterfallValidateTxid(txid) + + assert.fail('Unexpected result') + } catch (err) { + // console.log(`err.message: ${err.message}`) + assert.include(err.message, 'fake error') + } + }) + }) + describe('#getWhitelist', () => { it('should return the list', async () => { sandbox