Skip to content

Commit ee516e5

Browse files
authored
Merge pull request #4 from bitcoinerlab/parallelization
Optimize Fetch Calls with Parallelization and Improved Queue Utilization
2 parents ee88d03 + c12fa72 commit ee516e5

3 files changed

Lines changed: 38 additions & 24 deletions

File tree

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@bitcoinerlab/discovery",
33
"description": "A TypeScript library for retrieving Bitcoin funds from ranged descriptors, leveraging @bitcoinerlab/explorer for standardized access to multiple blockchain explorers.",
44
"homepage": "https://github.com/bitcoinerlab/discovery",
5-
"version": "1.0.2",
5+
"version": "1.0.3",
66
"author": "Jose-Luis Landabaso",
77
"license": "MIT",
88
"prettier": "@bitcoinerlab/configs/prettierConfig.json",
@@ -44,7 +44,7 @@
4444
],
4545
"dependencies": {
4646
"@bitcoinerlab/descriptors": "^2.0.1",
47-
"@bitcoinerlab/explorer": "^0.1.1",
47+
"@bitcoinerlab/explorer": "^0.1.2",
4848
"@bitcoinerlab/secp256k1": "^1.0.5",
4949
"@types/memoizee": "^0.4.8",
5050
"bitcoinjs-lib": "^6.1.3",

src/discovery.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ export function DiscoveryFactory(
239239
throw new Error(`Pass index for ranged descriptors`);
240240
const internalIndex = typeof index === 'number' ? index : 'non-ranged';
241241
const networkId = getNetworkId(network);
242+
//console.log(`Fetching ${descriptor}, ${internalIndex}`);
242243
const scriptPubKey = this.#derivers.deriveScriptPubKey(
243244
networkId,
244245
canonicalize(descriptor, network) as string,
@@ -302,31 +303,42 @@ export function DiscoveryFactory(
302303
* @returns Resolves when all the transactions have been fetched and stored in discoveryData.
303304
*/
304305
async #fetchTxs() {
305-
const txHexRecords: Record<TxId, TxHex> = {};
306306
const networkId = getNetworkId(network);
307307
const networkData = this.#discoveryData[networkId];
308+
309+
const fetchTxPromises = [];
310+
const txIdsToFetch = new Set<TxId>();
311+
308312
for (const descriptor in networkData.descriptorMap) {
309313
const range = networkData.descriptorMap[descriptor]?.range || [];
310314
for (const index in range) {
311315
const txIds = range[index]?.txIds;
312316
if (!txIds)
313317
throw new Error(
314-
`Error: cannot retrieve txs for nonexising scriptPubKey: ${networkId}, ${descriptor}, ${index}`
318+
`Error: cannot retrieve txs for nonexisting scriptPubKey: ${networkId}, ${descriptor}, ${index}`
315319
);
316-
for (const txId of txIds)
317-
if (!networkData.txMap[txId]?.txHex)
318-
txHexRecords[txId] = await explorer.fetchTx(txId);
320+
321+
for (const txId of txIds) {
322+
if (!networkData.txMap[txId]?.txHex && !txIdsToFetch.has(txId)) {
323+
txIdsToFetch.add(txId);
324+
fetchTxPromises.push(
325+
explorer.fetchTx(txId).then(txHex => ({ txId, txHex }))
326+
);
327+
}
328+
}
319329
}
320330
}
321-
if (Object.keys(txHexRecords).length) {
331+
332+
const txHexResults = await Promise.all(fetchTxPromises);
333+
334+
if (txHexResults.length) {
322335
this.#discoveryData = produce(this.#discoveryData, discoveryData => {
323-
for (const txId in txHexRecords) {
324-
const txHex = txHexRecords[txId];
336+
txHexResults.forEach(({ txId, txHex }) => {
325337
if (!txHex) throw new Error(`txHex not retrieved for ${txId}`);
326338
const txData = discoveryData[networkId].txMap[txId];
327339
if (!txData) throw new Error(`txData does not exist for ${txId}`);
328340
txData.txHex = txHex;
329-
}
341+
});
330342
});
331343
}
332344
}
@@ -412,15 +424,16 @@ export function DiscoveryFactory(
412424
throw new Error(`Don't pass index`);
413425
if (onChecking) onChecking(descriptorOrDescriptors);
414426
const canonicalInput = canonicalize(descriptorOrDescriptors, network);
415-
let nextPromise;
427+
let nextPromise: Promise<void> | undefined = undefined;
416428
let usedOutput = false;
417429
let usedOutputNotified = false;
418430

419431
const descriptorArray = Array.isArray(canonicalInput)
420432
? canonicalInput
421433
: [canonicalInput];
422434
const networkId = getNetworkId(network);
423-
for (const descriptor of descriptorArray) {
435+
436+
const descriptorFetchPromises = descriptorArray.map(async descriptor => {
424437
this.#discoveryData = produce(this.#discoveryData, discoveryData => {
425438
const descriptorInfo =
426439
discoveryData[networkId].descriptorMap[descriptor];
@@ -464,7 +477,7 @@ export function DiscoveryFactory(
464477
gap = 0;
465478
if (next && !nextPromise) nextPromise = next();
466479
if (onUsed && usedOutputNotified === false) {
467-
onUsed(descriptor);
480+
onUsed(descriptorOrDescriptors);
468481
usedOutputNotified = true;
469482
}
470483
} else {
@@ -484,7 +497,8 @@ export function DiscoveryFactory(
484497
descriptorInfo.fetching = false;
485498
descriptorInfo.timeFetched = now();
486499
});
487-
}
500+
});
501+
await Promise.all(descriptorFetchPromises);
488502

489503
const promises = [];
490504
if (usedOutput) promises.push(this.#fetchTxs());

0 commit comments

Comments
 (0)