- 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 17670 Build 35166: Build Diff cashtab-tests Build 35165: 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 | delete return | |