From 9090d1b9913fa7d7a80f94872f87a8398d239744 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 30 Oct 2019 12:33:29 -0700 Subject: [PATCH] fix(validateTxid): Fixed bug in SLP validateTxid() --- src/routes/v3/slp.js | 35 ++++++++++++-- test/v3/mocks/slp-mocks.js | 63 +++++++++++++++++++++++++- test/v3/slp.js | 93 +++++++++++++++++++++++--------------- 3 files changed, 149 insertions(+), 42 deletions(-) diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index fec56d4..d86c6dc 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -999,34 +999,59 @@ async function validateBulk(req, res, next) { // Get data from SLPDB. const tokenRes = await axios.get(url, options) + // console.log(`tokenRes.data: ${JSON.stringify(tokenRes.data, null, 2)}`) - const formattedTokens = [] + 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) + 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) }) - txids.forEach(tokenId => { - if (!tokenIds.includes(tokenId)) { + // 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: tokenId, + 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(formattedTokens) } catch (err) { diff --git a/test/v3/mocks/slp-mocks.js b/test/v3/mocks/slp-mocks.js index fca6693..da15193 100644 --- a/test/v3/mocks/slp-mocks.js +++ b/test/v3/mocks/slp-mocks.js @@ -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 } diff --git a/test/v3/slp.js b/test/v3/slp.js index 81ac91a..3592ae9 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -102,7 +102,7 @@ describe("#SLP", () => { assert.equal(result.status, "slp", "Returns static string") }) }) - + /* describe("list()", () => { // list route handler const list = slpRoute.testableComponents.list @@ -806,7 +806,7 @@ describe("#SLP", () => { ]) }) }) - +*/ describe("validateBulk()", () => { const validateBulk = slpRoute.testableComponents.validateBulk @@ -832,47 +832,68 @@ describe("#SLP", () => { assert.include(result.error, "Array too large") }) - if (process.env.TEST === "integration") { - 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) - // } + it("should validate array with single element", 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.mockSingleValidTxid) + } - req.body.txids = [ - "77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d" - ] + req.body.txids = [ + "77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d" + ] - const result = await validateBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) + const result = await validateBulk(req, res) + // console.log(`result: ${util.inspect(result)}`) - assert.isArray(result) - assert.hasAllKeys(result[0], ["txid", "valid"]) - }) + assert.isArray(result) + assert.hasAllKeys(result[0], ["txid", "valid"]) + }) - 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) - // } + it("should validate array with 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.mockTwoValidTxid) + } - req.body.txids = [ - "77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d", - "552112f9e458dc7d1d8b328b0a6685e8af74a64b60b6846e7c86407f27f47e42" - ] + req.body.txids = [ + "77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d", + "552112f9e458dc7d1d8b328b0a6685e8af74a64b60b6846e7c86407f27f47e42" + ] - const result = await validateBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) + 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) - }) - } + 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) + // console.log(`result: ${util.inspect(result)}`) + + assert.isArray(result) + assert.hasAllKeys(result[0], ["txid", "valid"]) + assert.equal(result.length, 2) + }) }) describe("tokenStatsSingle()", () => {