From f729918385a3261230e14bc28683acffa81c7a9e Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 1 Mar 2021 06:55:35 -0800 Subject: [PATCH 1/3] fix(_hydrateUtxo): Adding user-configurable artifical delay --- src/slp/utils.js | 30 +++++++++++++++++------ test/unit/slp-utils.js | 55 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/slp/utils.js b/src/slp/utils.js index 4b37f6a..0e07265 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -5,10 +5,8 @@ const axios = require('axios') const slpParser = require('slp-parser') const BigNumber = require('bignumber.js') -// const Script = require("../script") -// const scriptLib = new Script() - -// const BigNumber = require("bignumber.js") +// Local libraries +const Util = require('../util') let _this @@ -39,6 +37,8 @@ class Utils { _this = this this.whitelist = [] + + this.util = new Util(config) } /** @@ -1049,14 +1049,18 @@ class Utils { * that txid and validity has not been confirmed, or a 429 rate-limit error * was enountered during the processing of the request. * + * An optional second input object, `usrObj`, allows the user to inject an + * artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to + * a number, the call will delay by that number of milliseconds between + * processing UTXOs. + * * This is an API-heavy call. If you get a lot of `null` values, then slow down - * the calls by adding artifical delays, or request info on fewer UTXOs at a + * the calls by using the usrObj.utxoDelay property, or request info on fewer + * UTXOs at a * time. `null` indicates that the UTXO can *not* be safely spent, because * a judgement as to weather it is a token UTXO has not been made. Spending it * could burn tokens. It's safest to ignore UTXOs with a value of `null`. * - * A second, optional input, `usrObj`, is used by bch-api for managing rate - * limits. It can be safely ignored when writing apps using this call. * * @apiExample Example usage: * @@ -1064,7 +1068,8 @@ class Utils { * try { * const utxos = await bchjs.Electrumx.utxo(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`) * - * const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) + * // Delay 100mS between processing UTXOs, to prevent rate-limit errors. + * const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos, { utxoDelay: 100 }) * * console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`) * } catch (error) { @@ -1266,6 +1271,9 @@ class Utils { // transaction. However, that is a rare occurence since the cache of // decodeOpReturn() will minimize API calls for this case. This behavior // could be changed, but right now it's a corner case of a corner case. + // + // If the usrObj has a utxoDelay property, then it will delay the loop for + // each UTXO by that many milliseconds. async _hydrateUtxo (utxos, usrObj = null) { try { const decodeOpReturnCache = {} @@ -1279,6 +1287,12 @@ class Utils { for (let i = 0; i < utxos.length; i++) { const utxo = utxos[i] + // If the user passes in a delay, then wait. + if (usrObj && usrObj.utxoDelay && !isNaN(Number(usrObj.utxoDelay))) { + const delayMs = Number(usrObj.utxoDelay) + await this.util.sleep(delayMs) + } + // Get raw transaction data from the full node and attempt to decode // the OP_RETURN data. // If there is no OP_RETURN, mark the UTXO as false. diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index 2ea2aae..b651da5 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -775,6 +775,61 @@ describe('#SLP Utils', () => { assert.include(err.response.data.error, 'Too many requests') } }) + + it('should add delay if delay is specified', 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' + }) + + // sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true) + + const utxos = [ + { + txid: + 'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb', + vout: 1, + amount: 0.00000546, + satoshis: 546, + height: 596089, + confirmations: 748 + } + ] + + const usrObj = { + utxoDelay: 100 + } + + await uut.Utils._hydrateUtxo(utxos, usrObj) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + // TODO: This test should realy assert that the test took at least 100mS + // to complete. However, as-is, it exercises the code path, so not + // throwing an error can be considered a pass. + assert.equal(true, true) + }) }) describe('#tokenUtxoDetails', () => { From 24d629ebe1ab7dad815ec7bcb0815f32fd8e14e9 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 1 Mar 2021 07:48:08 -0800 Subject: [PATCH 2/3] fix(hydrateUtxos): Adding usrObj input for delaying UTXO processing --- src/slp/utils.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/slp/utils.js b/src/slp/utils.js index 0e07265..3d8e214 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -1659,11 +1659,17 @@ class Utils { * or a 429 rate-limit error was enountered during the processing of the * request. * + * An optional second input object, `usrObj`, allows the user to inject an + * artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to + * a number, the call will delay by that number of milliseconds between + * processing UTXOs. + * * This is an API-heavy call. If you get a lot of `null` values, then slow down - * the calls or request info on fewer UTXOs at a time. - * `null` indicates that the UTXO can not be safely spent, because a judgement - * as to weather it is a token UTXO has not been made. Spending it could burn - * tokens. It's safest to ignore UTXOs with a value of `null`. + * the calls by using the usrObj.utxoDelay property, or request info on fewer + * UTXOs at a + * time. `null` indicates that the UTXO can *not* be safely spent, because + * a judgement as to weather it is a token UTXO has not been made. Spending it + * could burn tokens. It's safest to ignore UTXOs with a value of `null`. * * @apiExample Example usage: * @@ -1675,7 +1681,8 @@ class Utils { * "bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk" * ]) * - * const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos) + * // Wait 100mS between processing UTXOs, to prevent rate limit errors. + * const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos, { utxoDelay: 100 }) * * console.log(`${JSON.stringify(utxoInfo, null, 2)}`) * } catch (error) { @@ -1809,7 +1816,7 @@ class Utils { */ // Same as tokenUtxoDetails(), but reduces API calls by having bch-api server // do the heavy lifting. - async hydrateUtxos (utxos) { + async hydrateUtxos (utxos, usrObj) { try { // Throw error if input is not an array. if (!Array.isArray(utxos)) throw new Error('Input must be an array.') @@ -1817,7 +1824,8 @@ class Utils { const response = await _this.axios.post( `${this.restURL}slp/hydrateUtxos`, { - utxos: utxos + utxos: utxos, + usrObj }, _this.axiosOptions ) From c839f0b8917babf706c12fc6d58adaba1209c436 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 1 Mar 2021 08:19:16 -0800 Subject: [PATCH 3/3] fix(integration tests): Fixing broken ABC integration test --- test/integration/chains/bchn/slp.js | 25 +++++++++++++++++++++++++ test/integration/slp.js | 23 ----------------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/test/integration/chains/bchn/slp.js b/test/integration/chains/bchn/slp.js index 14e270c..6c9760e 100644 --- a/test/integration/chains/bchn/slp.js +++ b/test/integration/chains/bchn/slp.js @@ -31,6 +31,31 @@ describe('#SLP', () => { }) describe('#util', () => { + describe('#decodeOpReturn', () => { + it('should decode a NFT Child transaction', async () => { + const txid = + 'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b' + + const data = await bchjs.SLP.Utils.decodeOpReturn(txid) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + assert.property(data, 'tokenType') + assert.property(data, 'txType') + assert.property(data, 'ticker') + assert.property(data, 'name') + assert.property(data, 'tokenId') + assert.property(data, 'documentUri') + assert.property(data, 'documentHash') + assert.property(data, 'decimals') + assert.property(data, 'mintBatonVout') + assert.property(data, 'qty') + + assert.equal(data.tokenType, 65) + assert.equal(data.mintBatonVout, 0) + assert.equal(data.qty, '1') + }) + }) + describe('#tokenUtxoDetails', () => { it('should handle a range of UTXO types', async () => { const utxos = [ diff --git a/test/integration/slp.js b/test/integration/slp.js index 1d8e8d3..1d4570b 100644 --- a/test/integration/slp.js +++ b/test/integration/slp.js @@ -228,29 +228,6 @@ describe('#SLP', () => { assert.include(err.message, 'amount string size not 8 bytes') } }) - - it('should decode a NFT Child transaction', async () => { - const txid = - 'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b' - - const data = await bchjs.SLP.Utils.decodeOpReturn(txid) - // console.log(`data: ${JSON.stringify(data, null, 2)}`) - - assert.property(data, 'tokenType') - assert.property(data, 'txType') - assert.property(data, 'ticker') - assert.property(data, 'name') - assert.property(data, 'tokenId') - assert.property(data, 'documentUri') - assert.property(data, 'documentHash') - assert.property(data, 'decimals') - assert.property(data, 'mintBatonVout') - assert.property(data, 'qty') - - assert.equal(data.tokenType, 65) - assert.equal(data.mintBatonVout, 0) - assert.equal(data.qty, '1') - }) }) describe('#tokenUtxoDetails', () => {