From 9580a30ba592ca3f54d8582b1b4ac5e5bc464c16 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 5 Dec 2020 16:55:21 -0800 Subject: [PATCH 1/3] feat(validateBulk): Changed output. Passing true, null, and false values --- src/routes/v3/slp.js | 26 +++++++++--- test/v3/integration/slp.js | 82 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 0a84a33..5c2b0e9 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -1006,7 +1006,7 @@ class Slp { let formattedTokens = [] - // Combine the arrays. Why? Generally there is nothing in the u array. + // Combine the confirmed and unconfirmed collections. const concatArray = tokenRes.data.c.concat(tokenRes.data.u) const tokenIds = [] @@ -1028,12 +1028,15 @@ class Slp { }) // If a user-provided txid doesn't exist in the data, add it with - // valid:false property. + // valid:null property. + // 'null' indicates that SLPDB does not know about the transaction. It + // either has not seen it or has not processed it yet. A determination + // can not be made. txids.forEach((txid) => { if (!tokenIds.includes(txid)) { formattedTokens.push({ txid: txid, - valid: false + valid: null }) } }) @@ -1056,8 +1059,21 @@ class Slp { formattedTokens = newOutput } + // Put the output array in the same order as the input array. + const outAry = [] + for (let i = 0; i < txids.length; i++) { + const thisTxid = txids[i] + + // Need to use Array.find() because the returned output array is out + // of order with respect to the txid input array. + const output = formattedTokens.find((elem) => elem.txid === thisTxid) + // console.log(`output: ${JSON.stringify(output, null, 2)}`) + + outAry.push(output) + } + res.status(200) - return res.json(formattedTokens) + return res.json(outAry) } catch (err) { wlogger.error('Error in slp.ts/validateBulk().', err) @@ -1137,7 +1153,7 @@ class Slp { res.status(200) return res.json(result) } catch (err) { - wlogger.error('Error in slp.ts/validateSingle().', err) + wlogger.error('Error in slp.js/validateSingle().', err) return _this.errorHandler(err, res) } diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index cf8b9c5..aefc74b 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -258,4 +258,86 @@ describe('#slp', () => { // ) // }) }) + + describe('#validateBulk', () => { + it('should handle a mix of valid, invalid, and non-SLP txs', async () => { + const txids = [ + // Malformed SLP tx + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a', + // Normal TX (non-SLP) + '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0', + // Valid PSF SLP tx + 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd', + // Valid SLP token not in whitelist + '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488', + // Token send on BCHN network. + '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019', + // Token send on ABC network. + '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d', + // Known invalid SLP token send of PSF tokens. + '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74' + ] + + req.body.txids = txids + const result = await slp.validateBulk(req, res) + console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // assert.equal(result.txid, txid) + // assert.equal(result.isValid, false) + + // BCHN expected results + if (process.env.ISBCHN) { + assert.equal(result[0].txid, txids[0]) + assert.equal(result[0].valid, null) + + assert.equal(result[1].txid, txids[1]) + assert.equal(result[1].valid, null) + + assert.equal(result[2].txid, txids[2]) + assert.equal(result[2].valid, true) + + assert.equal(result[3].txid, txids[3]) + assert.equal(result[3].valid, true) + + // Note: This should change from null to true once SLPDB finishes indexing. + assert.equal(result[4].txid, txids[4]) + assert.equal(result[4].valid, null) + + assert.equal(result[5].txid, txids[5]) + assert.equal(result[5].valid, null) + + assert.equal(result[6].txid, txids[6]) + assert.equal(result[6].valid, false) + assert.include( + result[6].invalidReason, + 'Token outputs are greater than valid token inputs' + ) + } else { + assert.equal(result[0].txid, txids[0]) + assert.equal(result[0].valid, null) + + assert.equal(result[1].txid, txids[1]) + assert.equal(result[1].valid, null) + + assert.equal(result[2].txid, txids[2]) + assert.equal(result[2].valid, true) + + assert.equal(result[3].txid, txids[3]) + assert.equal(result[3].valid, true) + + assert.equal(result[4].txid, txids[4]) + assert.equal(result[4].valid, null) + + assert.equal(result[5].txid, txids[5]) + assert.equal(result[5].valid, true) + + assert.equal(result[6].txid, txids[6]) + assert.equal(result[6].valid, false) + assert.include( + result[6].invalidReason, + 'Token outputs are greater than valid token inputs' + ) + } + }) + }) }) From 3e09b63a55cc70beeb814e81ee8206199dffb23a Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 5 Dec 2020 17:19:14 -0800 Subject: [PATCH 2/3] Added unit test for validateBulk() --- package.json | 2 +- test/v3/integration/slp.js | 5 +-- test/v3/mocks/slp-mocks.js | 49 ++++++++++++++++++++- test/v3/slp.js | 89 +++++++++++++++++++++++++++++++++++++- 4 files changed, 137 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index b928f09..5ad88cd 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "lint": "standard --env mocha --fix", "test-v3": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 60000 test/v3/", "test:temp:integration": "export NETWORK=mainnet && export TEST=integration && mocha --timeout 25000 test/v3/integration/price.js", - "test:temp:unit": "export NETWORK=mainnet && mocha -g '#validate3Bulk' --exit test/v3/", + "test:temp:unit": "export NETWORK=mainnet && mocha -g '#validateBulk' --exit test/v3/", "test:integration": "mocha test/v3/integration", "test:integration:slpdb": "mocha --timeout 25000 -g '#validate2Single' test/v3/integration/slp*.js", "coverage": "nyc report --reporter=text-lcov | coveralls", diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index aefc74b..2156c3e 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -280,10 +280,7 @@ describe('#slp', () => { req.body.txids = txids const result = await slp.validateBulk(req, res) - console.log(`result: ${JSON.stringify(result, null, 2)}`) - - // assert.equal(result.txid, txid) - // assert.equal(result.isValid, false) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) // BCHN expected results if (process.env.ISBCHN) { diff --git a/test/v3/mocks/slp-mocks.js b/test/v3/mocks/slp-mocks.js index 736c2f2..ee42f85 100644 --- a/test/v3/mocks/slp-mocks.js +++ b/test/v3/mocks/slp-mocks.js @@ -542,6 +542,52 @@ const mockTxHistory = [ } ] +const mockValidateBulk = { + c: [ + { + _id: '5fcc1ae2aff6379ca5bbb213', + tx: { + h: '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d' + }, + slp: { + valid: true, + invalidReason: null + } + }, + { + _id: '5fc9b4db4d54eece25b52762', + tx: { + h: 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd' + }, + slp: { + valid: true, + invalidReason: null + } + }, + { + _id: '5fc989a14d54eece25af88a6', + tx: { + h: '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74' + }, + slp: { + valid: false, + invalidReason: 'Token outputs are greater than valid token inputs.' + } + }, + { + _id: '5fc974d14d54eece25ad5df2', + tx: { + h: '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488' + }, + slp: { + valid: true, + invalidReason: null + } + } + ], + u: [] +} + module.exports = { mockList, mockSingleToken, @@ -558,5 +604,6 @@ module.exports = { mockTwoValidTxid, mockTwoRedundentTxid, mockTxHistory, - mockPsfToken + mockPsfToken, + mockValidateBulk } diff --git a/test/v3/slp.js b/test/v3/slp.js index a842aeb..ed17858 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -513,7 +513,7 @@ describe('#SLP', () => { req.params.txid = txid const result = await slpRoute.validate3Single(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) @@ -562,7 +562,7 @@ describe('#SLP', () => { } }) - describe('validateBulk()', () => { + describe('#validateBulk()', () => { const validateBulk = slpRoute.validateBulk it('should throw 400 if txid array is empty', async () => { @@ -685,6 +685,91 @@ describe('#SLP', () => { assert.hasAllKeys(result[0], ['txid', 'valid']) assert.equal(result.length, 2) }) + + it('should handle a mix of valid, invalid, and non-SLP txs', async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === 'unit') { + sandbox.stub(slpRoute.axios, 'request').resolves({ + data: mockData.mockValidateBulk + }) + } + + const txids = [ + // Malformed SLP tx + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a', + // Normal TX (non-SLP) + '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0', + // Valid PSF SLP tx + 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd', + // Valid SLP token not in whitelist + '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488', + // Token send on BCHN network. + '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019', + // Token send on ABC network. + '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d', + // Known invalid SLP token send of PSF tokens. + '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74' + ] + + req.body.txids = txids + + const result = await validateBulk(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // BCHN expected results + if (process.env.ISBCHN) { + assert.equal(result[0].txid, txids[0]) + assert.equal(result[0].valid, null) + + assert.equal(result[1].txid, txids[1]) + assert.equal(result[1].valid, null) + + assert.equal(result[2].txid, txids[2]) + assert.equal(result[2].valid, true) + + assert.equal(result[3].txid, txids[3]) + assert.equal(result[3].valid, true) + + // Note: This should change from null to true once SLPDB finishes indexing. + assert.equal(result[4].txid, txids[4]) + assert.equal(result[4].valid, null) + + assert.equal(result[5].txid, txids[5]) + assert.equal(result[5].valid, null) + + assert.equal(result[6].txid, txids[6]) + assert.equal(result[6].valid, false) + assert.include( + result[6].invalidReason, + 'Token outputs are greater than valid token inputs' + ) + } else { + assert.equal(result[0].txid, txids[0]) + assert.equal(result[0].valid, null) + + assert.equal(result[1].txid, txids[1]) + assert.equal(result[1].valid, null) + + assert.equal(result[2].txid, txids[2]) + assert.equal(result[2].valid, true) + + assert.equal(result[3].txid, txids[3]) + assert.equal(result[3].valid, true) + + assert.equal(result[4].txid, txids[4]) + assert.equal(result[4].valid, null) + + assert.equal(result[5].txid, txids[5]) + assert.equal(result[5].valid, true) + + assert.equal(result[6].txid, txids[6]) + assert.equal(result[6].valid, false) + assert.include( + result[6].invalidReason, + 'Token outputs are greater than valid token inputs' + ) + } + }) }) describe('#validate3Bulk', () => { From 19b40c54e71da7a4f71a3d60b631af5b4633d614 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 5 Dec 2020 17:47:46 -0800 Subject: [PATCH 3/3] feat(validateSingle): Returning null instead of false as default return value --- src/routes/v3/slp.js | 26 ++++++++++--- test/v3/integration/slp.js | 79 ++++++++++++++++++++++++++++++++++++++ test/v3/mocks/slp-mocks.js | 29 +++++++++++++- test/v3/slp.js | 63 +++++++++++++++++++++++++++++- 4 files changed, 189 insertions(+), 8 deletions(-) diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 5c2b0e9..4a858b1 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -1135,7 +1135,7 @@ class Slp { // Default return value. let result = { txid: txid, - valid: false + valid: null } // Build result. @@ -1334,7 +1334,7 @@ class Slp { // Default return value. let result = { txid: txid, - valid: false + valid: null } // Build result. @@ -1445,12 +1445,15 @@ class Slp { }) // If a user-provided txid doesn't exist in the data, add it with - // valid:false property. + // valid:null property. + // 'null' indicates that SLPDB does not know about the transaction. It + // either has not seen it or has not processed it yet. A determination + // can not be made. txids.forEach((txid) => { if (!tokenIds.includes(txid)) { formattedTokens.push({ txid: txid, - valid: false + valid: null }) } }) @@ -1473,8 +1476,21 @@ class Slp { formattedTokens = newOutput } + // Put the output array in the same order as the input array. + const outAry = [] + for (let i = 0; i < txids.length; i++) { + const thisTxid = txids[i] + + // Need to use Array.find() because the returned output array is out + // of order with respect to the txid input array. + const output = formattedTokens.find((elem) => elem.txid === thisTxid) + // console.log(`output: ${JSON.stringify(output, null, 2)}`) + + outAry.push(output) + } + res.status(200) - return res.json(formattedTokens) + return res.json(outAry) } catch (err) { wlogger.error('Error in slp.js/validate3Bulk().', err) diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index 2156c3e..b8703f0 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -337,4 +337,83 @@ describe('#slp', () => { } }) }) + + describe('#validate3Bulk', () => { + it('should handle a mix of valid, invalid, and non-SLP txs', async () => { + const txids = [ + // Malformed SLP tx + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a', + // Normal TX (non-SLP) + '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0', + // Valid PSF SLP tx + 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd', + // Valid SLP token not in whitelist + '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488', + // Token send on BCHN network. + '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019', + // Token send on ABC network. + '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d', + // Known invalid SLP token send of PSF tokens. + '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74' + ] + + req.body.txids = txids + const result = await slp.validate3Bulk(req, res) + console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // BCHN expected results + if (process.env.ISBCHN) { + assert.equal(result[0].txid, txids[0]) + assert.equal(result[0].valid, null) + + assert.equal(result[1].txid, txids[1]) + assert.equal(result[1].valid, null) + + assert.equal(result[2].txid, txids[2]) + assert.equal(result[2].valid, true) + + assert.equal(result[3].txid, txids[3]) + assert.equal(result[3].valid, null) + + // Note: This should change from null to true once SLPDB finishes indexing. + assert.equal(result[4].txid, txids[4]) + assert.equal(result[4].valid, null) + + assert.equal(result[5].txid, txids[5]) + assert.equal(result[5].valid, null) + + assert.equal(result[6].txid, txids[6]) + assert.equal(result[6].valid, false) + assert.include( + result[6].invalidReason, + 'Token outputs are greater than valid token inputs' + ) + } else { + assert.equal(result[0].txid, txids[0]) + assert.equal(result[0].valid, null) + + assert.equal(result[1].txid, txids[1]) + assert.equal(result[1].valid, null) + + assert.equal(result[2].txid, txids[2]) + assert.equal(result[2].valid, true) + + assert.equal(result[3].txid, txids[3]) + assert.equal(result[3].valid, true) + + assert.equal(result[4].txid, txids[4]) + assert.equal(result[4].valid, null) + + assert.equal(result[5].txid, txids[5]) + assert.equal(result[5].valid, true) + + assert.equal(result[6].txid, txids[6]) + assert.equal(result[6].valid, false) + assert.include( + result[6].invalidReason, + 'Token outputs are greater than valid token inputs' + ) + } + }) + }) }) diff --git a/test/v3/mocks/slp-mocks.js b/test/v3/mocks/slp-mocks.js index ee42f85..d18ab98 100644 --- a/test/v3/mocks/slp-mocks.js +++ b/test/v3/mocks/slp-mocks.js @@ -588,6 +588,32 @@ const mockValidateBulk = { u: [] } +const mockValidate3Bulk = { + c: [ + { + _id: '5fcaf60b2898f98879029754', + tx: { + h: 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd' + }, + slp: { + valid: true, + invalidReason: null + } + }, + { + _id: '5fcaf5382898f988790275d0', + tx: { + h: '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74' + }, + slp: { + valid: false, + invalidReason: 'Token outputs are greater than valid token inputs.' + } + } + ], + u: [] +} + module.exports = { mockList, mockSingleToken, @@ -605,5 +631,6 @@ module.exports = { mockTwoRedundentTxid, mockTxHistory, mockPsfToken, - mockValidateBulk + mockValidateBulk, + mockValidate3Bulk } diff --git a/test/v3/slp.js b/test/v3/slp.js index ed17858..961dbe7 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -441,7 +441,7 @@ describe('#SLP', () => { // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.equal(result.txid, txid) - assert.equal(result.valid, false) + assert.equal(result.valid, null) }) it('should validate a known valid TXID', async () => { @@ -516,7 +516,7 @@ describe('#SLP', () => { // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.equal(result.txid, txid) - assert.equal(result.valid, false) + assert.equal(result.valid, null) }) it('should validate a known valid TXID', async () => { @@ -895,6 +895,65 @@ describe('#SLP', () => { assert.hasAllKeys(result[0], ['txid', 'valid']) assert.equal(result.length, 2) }) + + if (process.env.TEST === 'unit') { + // This is a unit-test only test, as the results change depending on if + // its tested against the BCHN or ABC networks. There are integration tests + // for this test case in the ../integration/slp.js file. + it('should handle a mix of valid, invalid, and non-SLP txs', async () => { + // Mock the RPC call for unit tests. + + sandbox.stub(slpRoute.axios, 'request').resolves({ + data: mockData.mockValidate3Bulk + }) + + const txids = [ + // Malformed SLP tx + 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a', + // Normal TX (non-SLP) + '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0', + // Valid PSF SLP tx + 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd', + // Valid SLP token not in whitelist + '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488', + // Token send on BCHN network. + '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019', + // Token send on ABC network. + '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d', + // Known invalid SLP token send of PSF tokens. + '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74' + ] + + req.body.txids = txids + const result = await validate3Bulk(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result[0].txid, txids[0]) + assert.equal(result[0].valid, null) + + assert.equal(result[1].txid, txids[1]) + assert.equal(result[1].valid, null) + + assert.equal(result[2].txid, txids[2]) + assert.equal(result[2].valid, true) + + assert.equal(result[3].txid, txids[3]) + assert.equal(result[3].valid, null) + + assert.equal(result[4].txid, txids[4]) + assert.equal(result[4].valid, null) + + assert.equal(result[5].txid, txids[5]) + assert.equal(result[5].valid, null) + + assert.equal(result[6].txid, txids[6]) + assert.equal(result[6].valid, false) + assert.include( + result[6].invalidReason, + 'Token outputs are greater than valid token inputs' + ) + }) + } }) describe('tokenStats()', () => {