Merge pull request #4050 from mempool/mononaut/retry-block-txs
Handle failures while fetching block transactions
This commit is contained in:
commit
1b248c24f1
@ -105,11 +105,16 @@ class Blocks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip expensive lookups while mempool has priority
|
|
||||||
if (onlyCoinbase) {
|
if (onlyCoinbase) {
|
||||||
try {
|
try {
|
||||||
const coinbase = await transactionUtils.$getTransactionExtended(txIds[0], false, false, false, addMempoolData);
|
const coinbase = await transactionUtils.$getTransactionExtendedRetry(txIds[0], false, false, false, addMempoolData);
|
||||||
|
if (coinbase && coinbase.vin[0].is_coinbase) {
|
||||||
return [coinbase];
|
return [coinbase];
|
||||||
|
} else {
|
||||||
|
const msg = `Expected a coinbase tx, but the backend API returned something else`;
|
||||||
|
logger.err(msg);
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = `Cannot fetch coinbase tx ${txIds[0]}. Reason: ` + (e instanceof Error ? e.message : e);
|
const msg = `Cannot fetch coinbase tx ${txIds[0]}. Reason: ` + (e instanceof Error ? e.message : e);
|
||||||
logger.err(msg);
|
logger.err(msg);
|
||||||
@ -134,17 +139,17 @@ class Blocks {
|
|||||||
|
|
||||||
// Fetch remaining txs individually
|
// Fetch remaining txs individually
|
||||||
for (const txid of txIds.filter(txid => !transactionMap[txid])) {
|
for (const txid of txIds.filter(txid => !transactionMap[txid])) {
|
||||||
if (!transactionMap[txid]) {
|
|
||||||
if (!quiet && (totalFound % (Math.round((txIds.length) / 10)) === 0 || totalFound + 1 === txIds.length)) { // Avoid log spam
|
if (!quiet && (totalFound % (Math.round((txIds.length) / 10)) === 0 || totalFound + 1 === txIds.length)) { // Avoid log spam
|
||||||
logger.debug(`Indexing tx ${totalFound + 1} of ${txIds.length} in block #${blockHeight}`);
|
logger.debug(`Indexing tx ${totalFound + 1} of ${txIds.length} in block #${blockHeight}`);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const tx = await transactionUtils.$getTransactionExtended(txid, false, false, false, addMempoolData);
|
const tx = await transactionUtils.$getTransactionExtendedRetry(txid, false, false, false, addMempoolData);
|
||||||
transactionMap[txid] = tx;
|
transactionMap[txid] = tx;
|
||||||
totalFound++;
|
totalFound++;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err(`Cannot fetch tx ${txid}. Reason: ` + (e instanceof Error ? e.message : e));
|
const msg = `Cannot fetch tx ${txid}. Reason: ` + (e instanceof Error ? e.message : e);
|
||||||
}
|
logger.err(msg);
|
||||||
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,8 +157,24 @@ class Blocks {
|
|||||||
logger.debug(`${foundInMempool} of ${txIds.length} found in mempool. ${totalFound - foundInMempool} fetched through backend service.`);
|
logger.debug(`${foundInMempool} of ${txIds.length} found in mempool. ${totalFound - foundInMempool} fetched through backend service.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Require the first transaction to be a coinbase
|
||||||
|
const coinbase = transactionMap[txIds[0]];
|
||||||
|
if (!coinbase || !coinbase.vin[0].is_coinbase) {
|
||||||
|
const msg = `Expected first tx in a block to be a coinbase, but found something else`;
|
||||||
|
logger.err(msg);
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require all transactions to be present
|
||||||
|
// (we should have thrown an error already if a tx request failed)
|
||||||
|
if (txIds.some(txid => !transactionMap[txid])) {
|
||||||
|
const msg = `Failed to fetch ${txIds.length - totalFound} transactions from block`;
|
||||||
|
logger.err(msg);
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
// Return list of transactions, preserving block order
|
// Return list of transactions, preserving block order
|
||||||
return txIds.map(txid => transactionMap[txid]).filter(tx => tx != null);
|
return txIds.map(txid => transactionMap[txid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -667,14 +688,14 @@ class Blocks {
|
|||||||
const block = BitcoinApi.convertBlock(verboseBlock);
|
const block = BitcoinApi.convertBlock(verboseBlock);
|
||||||
const txIds: string[] = verboseBlock.tx.map(tx => tx.txid);
|
const txIds: string[] = verboseBlock.tx.map(tx => tx.txid);
|
||||||
const transactions = await this.$getTransactionsExtended(blockHash, block.height, false, txIds, false, true) as MempoolTransactionExtended[];
|
const transactions = await this.$getTransactionsExtended(blockHash, block.height, false, txIds, false, true) as MempoolTransactionExtended[];
|
||||||
if (config.MEMPOOL.BACKEND !== 'esplora') {
|
|
||||||
// fill in missing transaction fee data from verboseBlock
|
// fill in missing transaction fee data from verboseBlock
|
||||||
for (let i = 0; i < transactions.length; i++) {
|
for (let i = 0; i < transactions.length; i++) {
|
||||||
if (!transactions[i].fee && transactions[i].txid === verboseBlock.tx[i].txid) {
|
if (!transactions[i].fee && transactions[i].txid === verboseBlock.tx[i].txid) {
|
||||||
transactions[i].fee = verboseBlock.tx[i].fee * 100_000_000;
|
transactions[i].fee = verboseBlock.tx[i].fee * 100_000_000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const cpfpSummary: CpfpSummary = Common.calculateCpfp(block.height, transactions);
|
const cpfpSummary: CpfpSummary = Common.calculateCpfp(block.height, transactions);
|
||||||
const blockExtended: BlockExtended = await this.$getBlockExtended(block, cpfpSummary.transactions);
|
const blockExtended: BlockExtended = await this.$getBlockExtended(block, cpfpSummary.transactions);
|
||||||
const blockSummary: BlockSummary = this.summarizeBlockTransactions(block.id, cpfpSummary.transactions);
|
const blockSummary: BlockSummary = this.summarizeBlockTransactions(block.id, cpfpSummary.transactions);
|
||||||
|
@ -3,6 +3,7 @@ import { IEsploraApi } from './bitcoin/esplora-api.interface';
|
|||||||
import { Common } from './common';
|
import { Common } from './common';
|
||||||
import bitcoinApi, { bitcoinCoreApi } from './bitcoin/bitcoin-api-factory';
|
import bitcoinApi, { bitcoinCoreApi } from './bitcoin/bitcoin-api-factory';
|
||||||
import * as bitcoinjs from 'bitcoinjs-lib';
|
import * as bitcoinjs from 'bitcoinjs-lib';
|
||||||
|
import logger from '../logger';
|
||||||
|
|
||||||
class TransactionUtils {
|
class TransactionUtils {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
@ -22,6 +23,23 @@ class TransactionUtils {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrapper for $getTransactionExtended with an automatic retry direct to Core if the first API request fails.
|
||||||
|
// Propagates any error from the retry request.
|
||||||
|
public async $getTransactionExtendedRetry(txid: string, addPrevouts = false, lazyPrevouts = false, forceCore = false, addMempoolData = false): Promise<TransactionExtended> {
|
||||||
|
try {
|
||||||
|
const result = await this.$getTransactionExtended(txid, addPrevouts, lazyPrevouts, forceCore, addMempoolData);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
logger.err(`Cannot fetch tx ${txid}. Reason: backend returned null data`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.err(`Cannot fetch tx ${txid}. Reason: ` + (e instanceof Error ? e.message : e));
|
||||||
|
}
|
||||||
|
// retry direct from Core if first request failed
|
||||||
|
return this.$getTransactionExtended(txid, addPrevouts, lazyPrevouts, true, addMempoolData);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param txId
|
* @param txId
|
||||||
* @param addPrevouts
|
* @param addPrevouts
|
||||||
@ -31,7 +49,7 @@ class TransactionUtils {
|
|||||||
public async $getTransactionExtended(txId: string, addPrevouts = false, lazyPrevouts = false, forceCore = false, addMempoolData = false): Promise<TransactionExtended> {
|
public async $getTransactionExtended(txId: string, addPrevouts = false, lazyPrevouts = false, forceCore = false, addMempoolData = false): Promise<TransactionExtended> {
|
||||||
let transaction: IEsploraApi.Transaction;
|
let transaction: IEsploraApi.Transaction;
|
||||||
if (forceCore === true) {
|
if (forceCore === true) {
|
||||||
transaction = await bitcoinCoreApi.$getRawTransaction(txId, true);
|
transaction = await bitcoinCoreApi.$getRawTransaction(txId, false, addPrevouts, lazyPrevouts);
|
||||||
} else {
|
} else {
|
||||||
transaction = await bitcoinApi.$getRawTransaction(txId, false, addPrevouts, lazyPrevouts);
|
transaction = await bitcoinApi.$getRawTransaction(txId, false, addPrevouts, lazyPrevouts);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user