diff --git a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts index f610ed883..7f4a5e53a 100644 --- a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts +++ b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts @@ -8,6 +8,7 @@ export interface AbstractBitcoinApi { $getBlockHeightTip(): Promise; $getBlockHashTip(): Promise; $getTxIdsForBlock(hash: string): Promise; + $getTxsForBlock(hash: string): Promise; $getBlockHash(height: number): Promise; $getBlockHeader(hash: string): Promise; $getBlock(hash: string): Promise; diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 3ccea01ef..a1cf767d9 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -81,6 +81,10 @@ class BitcoinApi implements AbstractBitcoinApi { .then((rpcBlock: IBitcoinApi.Block) => rpcBlock.tx); } + $getTxsForBlock(hash: string): Promise { + throw new Error('Method getTxsForBlock not supported by the Bitcoin RPC API.'); + } + $getRawBlock(hash: string): Promise { return this.bitcoindClient.getBlock(hash, 0) .then((raw: string) => Buffer.from(raw, "hex")); diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 73a44a845..ff10751e0 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -89,6 +89,10 @@ class ElectrsApi implements AbstractBitcoinApi { return this.$queryWrapper(config.ESPLORA.REST_API_URL + '/block/' + hash + '/txids'); } + $getTxsForBlock(hash: string): Promise { + return this.$queryWrapper(config.ESPLORA.REST_API_URL + '/block/' + hash + '/txs'); + } + $getBlockHash(height: number): Promise { return this.$queryWrapper(config.ESPLORA.REST_API_URL + '/block-height/' + height); } diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 7cd37f637..1f1c4ebca 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -80,40 +80,38 @@ class Blocks { quiet: boolean = false, addMempoolData: boolean = false, ): Promise { - const transactions: TransactionExtended[] = []; - if (!txIds) { - txIds = await bitcoinApi.$getTxIdsForBlock(blockHash); - } + let transactions: TransactionExtended[] = []; const mempool = memPool.getMempool(); let transactionsFound = 0; let transactionsFetched = 0; - for (let i = 0; i < txIds.length; i++) { - if (mempool[txIds[i]]) { - // We update blocks before the mempool (index.ts), therefore we can - // optimize here by directly fetching txs in the "outdated" mempool - transactions.push(mempool[txIds[i]]); - transactionsFound++; - } else if (config.MEMPOOL.BACKEND === 'esplora' || !memPool.hasPriority() || i === 0) { - // Otherwise we fetch the tx data through backend services (esplora, electrum, core rpc...) - if (!quiet && (i % (Math.round((txIds.length) / 10)) === 0 || i + 1 === txIds.length)) { // Avoid log spam - logger.debug(`Indexing tx ${i + 1} of ${txIds.length} in block #${blockHeight}`); - } - try { - const tx = await transactionUtils.$getTransactionExtended(txIds[i], false, false, false, addMempoolData); - transactions.push(tx); - transactionsFetched++; - } catch (e) { + if (config.MEMPOOL.BACKEND === 'esplora') { + const rawTransactions = await bitcoinApi.$getTxsForBlock(blockHash); + transactions = rawTransactions.map(tx => transactionUtils.extendTransaction(tx)); + + if (!quiet) { + logger.debug(`${transactions.length} fetched through backend service.`); + } + } else { + if (!txIds) { + txIds = await bitcoinApi.$getTxIdsForBlock(blockHash); + } + for (let i = 0; i < txIds.length; i++) { + if (mempool[txIds[i]]) { + // We update blocks before the mempool (index.ts), therefore we can + // optimize here by directly fetching txs in the "outdated" mempool + transactions.push(mempool[txIds[i]]); + transactionsFound++; + } else if (!memPool.hasPriority() || i === 0) { + // Otherwise we fetch the tx data through backend services (esplora, electrum, core rpc...) + if (!quiet && (i % (Math.round((txIds.length) / 10)) === 0 || i + 1 === txIds.length)) { // Avoid log spam + logger.debug(`Indexing tx ${i + 1} of ${txIds.length} in block #${blockHeight}`); + } try { - if (config.MEMPOOL.BACKEND === 'esplora') { - // Try again with core - const tx = await transactionUtils.$getTransactionExtended(txIds[i], false, false, true, addMempoolData); - transactions.push(tx); - transactionsFetched++; - } else { - throw e; - } + const tx = await transactionUtils.$getTransactionExtended(txIds[i], false, false, false, addMempoolData); + transactions.push(tx); + transactionsFetched++; } catch (e) { if (i === 0) { const msg = `Cannot fetch coinbase tx ${txIds[i]}. Reason: ` + (e instanceof Error ? e.message : e); @@ -124,17 +122,17 @@ class Blocks { } } } + + if (onlyCoinbase === true) { + break; // Fetch the first transaction and exit + } } - if (onlyCoinbase === true) { - break; // Fetch the first transaction and exit + if (!quiet) { + logger.debug(`${transactionsFound} of ${txIds.length} found in mempool. ${transactionsFetched} fetched through backend service.`); } } - if (!quiet) { - logger.debug(`${transactionsFound} of ${txIds.length} found in mempool. ${transactionsFetched} fetched through backend service.`); - } - return transactions; } diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts index a48c9f259..0b10afdfb 100644 --- a/backend/src/api/transaction-utils.ts +++ b/backend/src/api/transaction-utils.ts @@ -53,7 +53,7 @@ class TransactionUtils { return (await this.$getTransactionExtended(txId, addPrevouts, lazyPrevouts, forceCore, true)) as MempoolTransactionExtended; } - private extendTransaction(transaction: IEsploraApi.Transaction): TransactionExtended { + public extendTransaction(transaction: IEsploraApi.Transaction): TransactionExtended { // @ts-ignore if (transaction.vsize) { // @ts-ignore