- The fetchTxDataForNullUtxos() function in useBCH.js builds an array of tx IDs and then passes them to BCH.Electrumx.txData() to retrieve the corresponding transaction data.
- The problem is BCH.Electrumx.txData() is limited to 20 TXIDs per request according to the spec, and wallets with > 20 null utxos will cause this api call to throw an 'array too large' error and fetchTxDataForNullUtxos()'s catch logic ignores the entire utxo set.
- This diff batches up the array into 20 elements each before making the API call.
Details
Details
- Reviewers
bytesofman - Group Reviewers
Restricted Project - Commits
- rABC794777b766af: [Cashtab] fetchTxDataForNullUtxos array threshold fix
- npm start
- find a wallet with > 20 nullUtxos (e.g. https://explorer.be.cash/address/ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx)
- send a message transaction and ensure the balance is updated as well as the tx history in Cashtab
Diff Detail
Diff Detail
- Repository
- rABC Bitcoin ABC
- Branch
- utxoFix
- Lint
Lint Passed - Unit
No Test Coverage - Build Status
Buildable 17671 Build 35168: Build Diff cashtab-tests Build 35167: arc lint + arc unit
Event Timeline
web/cashtab/src/components/Common/Ticker.js | ||
---|---|---|
29 ↗ | (On Diff #31389) | Let's just have one item in Ticker.js and call it xecApiBatchSize Right now this is 20. it's the same for all batch functions in xec-api so no need for multiple params. |
web/cashtab/src/hooks/useBCH.js | ||
541 ↗ | (On Diff #31389) | Build an array of promises and then use Promise.all to make the API request. |
Comment Actions
Refactored fetchTxDataForNullUtxos() to build an array of promises and then executed via a subsequent promise.all call.
Comment Actions
Per our Telegram discussion, here is some code that handles some of the issues here. It includes a new function that returns a Promise, so that Promise.all can work correctly, and also some adjustments to the fetchTxDataForNullUtxos function
const fetchTxDataPromise = (BCH, txidBatch) => { return new Promise((resolve, reject) => { BCH.Electrumx.txData(txidBatch).then( result => { resolve(result); }, err => { return reject(err); }, ); }); }; const fetchTxDataForNullUtxos = async (BCH, nullUtxos) => { // Check nullUtxos. If they aren't eToken txs, count them console.log( `Null utxos found, checking OP_RETURN fields to confirm they are not eToken txs.`, ); const txids = []; for (let i = 0; i < nullUtxos.length; i += 1) { // Batch API call to get their OP_RETURN asm info txids.push(nullUtxos[i].tx_hash); } // segment the txids array into chunks under the api limit const batchedTxids = batchArray(txids, currency.xecApiBatchSize); // build an array of promises let txDataPromises = []; // loop through each batch of 20 txids for (let j = 0; j < batchedTxids.length; j += 1) { const txidsForThisFetch = batchedTxids[j]; //console.log(`txidsForThisPromise`, txidsForThisFetch); // build the promise for the api call with the 20 txids in current batch const thisTxDataPromise = fetchTxDataPromise( BCH, txidsForThisFetch, ); txDataPromises.push(thisTxDataPromise); } try { const nullUtxoTxData = await Promise.all(txDataPromises); // Scan tx data for each utxo to confirm they are not eToken txs let thisTxDataResult; let nonEtokenUtxos = []; for (let k = 0; k < nullUtxoTxData.length; k += 1) { thisTxDataResult = nullUtxoTxData[k].transactions; nonEtokenUtxos = nonEtokenUtxos.concat( checkNullUtxosForTokenStatus(thisTxDataResult), ); } return nonEtokenUtxos; } catch (err) { console.log( `Error in checkNullUtxosForTokenStatus(nullUtxos)` + err, ); console.log(`nullUtxos`, nullUtxos); // If error, ignore these utxos, will be updated next utxo set refresh return []; } };
Comment Actions
Revised promise array logic and reverted checkNullUtxosForTokenStatus() changes in cashMethods.js
web/cashtab/src/hooks/useBCH.js | ||
---|---|---|
524 ↗ | (On Diff #31453) | delete return |