From d1847d2ecadf0bf61c563e99d77fabab38be0dc8 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 6 Dec 2020 19:18:47 -0800 Subject: [PATCH] feat(tokenUtxoDetails()): Using fallback SLP validators --- src/slp/utils.js | 49 +++++++++------ test/unit/fixtures/slp/mock-utils.js | 30 ++++++++- test/unit/slp-utils.js | 91 +++++++++++++++++++++++----- 3 files changed, 137 insertions(+), 33 deletions(-) diff --git a/src/slp/utils.js b/src/slp/utils.js index a6aea56..9f55d32 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -1,3 +1,4 @@ +// Public npm libraries const axios = require("axios") const slpParser = require("slp-parser") @@ -1111,9 +1112,9 @@ class Utils { let slpData = false try { slpData = await this.decodeOpReturn(utxo.txid, decodeOpReturnCache) - console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`) + // console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`) } catch (err) { - console.log(`error from decodeOpReturn(${utxo.txid}): `, err) + // console.log(`error from decodeOpReturn(${utxo.txid}): `, err) // An error will be thrown if the txid is not SLP. // If error is for some other reason, like a 429 error, mark utxo as 'null' @@ -1124,10 +1125,10 @@ class Utils { err.message.indexOf("lokad id") === -1 && err.message.indexOf("trailing data") === -1) ) { - console.log( - `unknown error from decodeOpReturn(). Marking as 'null'`, - err - ) + // console.log( + // `unknown error from decodeOpReturn(). Marking as 'null'`, + // err + // ) utxo.isValid = null outAry.push(utxo) @@ -1136,10 +1137,9 @@ class Utils { // an SLP UTXO. // Mark as false and continue the loop. } else { - console.log("marking as invalid") + // console.log("marking as invalid") utxo.isValid = false outAry.push(utxo) - console.log(`outAry: ${JSON.stringify(outAry, null, 2)}`) } // Halt the execution of the loop and increase to the next index. @@ -1285,7 +1285,8 @@ class Utils { // validate the TXID with SLPDB. if (outAry[i].tokenType) { // Only execute this block if the current UTXO has a 'tokenType' - // property. i.e. it has been hydrated with SLP information. + // property. i.e. it has been successfully hydrated with SLP + // information. // If the value has been cached, use the cached version first. let isValid = cachedTxValidation[utxo.txid] @@ -1302,7 +1303,13 @@ class Utils { // If still null, check the whitelist SLPDB if (isValid === null) { - console.log(`outAry[${i}]: ${JSON.stringify(outAry[i], null, 2)}`) + // console.log( + // `checking against whitelist SLPDB. outAry[${i}]: ${JSON.stringify( + // outAry[i], + // null, + // 2 + // )}` + // ) // Figure out if the token UTXO is in the whitelist. let utxoInWhitelist = false @@ -1318,6 +1325,15 @@ class Utils { // if the general SLPDB is not. if (utxoInWhitelist) { isValid = await this.validateTxid3(utxo.txid) + + // console.log( + // `whitelist-SLPDB for ${utxo.txid}: ${JSON.stringify( + // isValid, + // null, + // 2 + // )}` + // ) + isValid = isValid[0].valid // Save the result to the local cache. @@ -1328,22 +1344,21 @@ class Utils { // If still null, as a last resort, check it against slp-validate if (isValid === null) { isValid = await this.validateTxid2(utxo.txid) - console.log( - `slp-validate isValid: ${JSON.stringify(isValid, null, 2)}` - ) - isValid = isValid.valid + // console.log( + // `slp-validate isValid: ${JSON.stringify(isValid, null, 2)}` + // ) + + isValid = isValid.isValid // Save the result to the local cache. cachedTxValidation[utxo.txid] = isValid } - console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`) + // console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`) outAry[i].isValid = isValid } } - console.log(`pt2 outAry: ${JSON.stringify(outAry, null, 2)}`) - return outAry } catch (error) { if (error.response && error.response.data) throw error.response.data diff --git a/test/unit/fixtures/slp/mock-utils.js b/test/unit/fixtures/slp/mock-utils.js index b0d0034..af499b0 100644 --- a/test/unit/fixtures/slp/mock-utils.js +++ b/test/unit/fixtures/slp/mock-utils.js @@ -1163,6 +1163,33 @@ const mockValidateTxidArray = [ } ] +const whitelist = [ + { + name: "USDH", + tokenId: "c4b0d62156b3fa5c8f3436079b5394f7edc1bef5dc1cd2f9d0c4d46f82cca479" + }, + { + name: "SPICE", + tokenId: "4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf" + }, + { + name: "PSF", + tokenId: "38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0" + }, + { + name: "TROUT", + tokenId: "a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2" + }, + { + name: "PSFTEST", + tokenId: "d0ef4de95b78222bfee2326ab11382f4439aa0855936e2fe6ac129a8d778baa0" + }, + { + name: "TOK-CH", + tokenId: "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" + } +] + module.exports = { mockList, mockToken, @@ -1190,5 +1217,6 @@ module.exports = { mockInvalidSlpSend, mockWhitelist, mockValidateTxid3Valid, - mockValidateTxid3Invalid + mockValidateTxid3Invalid, + whitelist } diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index 7f32c10..c562fd7 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -1695,21 +1695,82 @@ describe("#SLP Utils", () => { assert.equal(data[0].isValid, false) }) - // it("should validate against the whitelist SLPDB when regular SLPDB returns null", async () => { - // const utxos = [ - // // Malformed SLP tx - // { - // note: "Malformed SLP tx", - // tx_hash: - // "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", - // tx_pos: 1, - // value: 546 - // } - // ] - // - // const data = await uut.Utils.tokenUtxoDetails(utxos) - // console.log(`data: ${JSON.stringify(data, null, 2)}`) - // }) + it("should use backup validators when regular SLPDB returns null", async () => { + // Mock the call to REST API + // Stub the calls to decodeOpReturn. + sandbox + .stub(uut.Utils, "decodeOpReturn") + .onCall(0) + .resolves({ + tokenType: 1, + txType: "SEND", + tokenId: + "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7", + amounts: ["200000000", "99887500000000"] + }) + .onCall(1) + .resolves({ + tokenType: 1, + txType: "GENESIS", + ticker: "TOK-CH", + name: "TokyoCash", + tokenId: + "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7", + documentUri: "", + documentHash: "", + decimals: 8, + mintBatonVout: 0, + qty: "2100000000000000" + }) + + // Force default SLPDB to return 'null', which should trigger usage of the + // backup whitelist-SLPDB. + sandbox.stub(uut.Utils, "validateTxid").resolves([ + { + txid: + "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", + valid: null + } + ]) + + // Force the token UTXO to be in the whitelist. + uut.Utils.whitelist = mockData.whitelist + + // Mock the response from the whitelist-SLPDB. + sandbox.stub(uut.Utils, "validateTxid3").resolves([ + { + txid: + "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", + valid: null + } + ]) + + // Mock the response from slp-api + sandbox.stub(uut.Utils, "validateTxid2").resolves({ + txid: + "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", + isValid: true, + msg: "" + }) + + const utxos = [ + { + txid: + "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", + vout: 1, + amount: 0.00000546, + satoshis: 546, + height: 596089, + confirmations: 748 + } + ] + + const data = await uut.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + // The UTXO should have been validated by the third validation source. + assert.equal(data[0].isValid, true) + }) }) describe("#txDetails", () => {