use bulk /txs endpoint to check cached rbf tx status
This commit is contained in:
parent
2339a0771e
commit
38909cfc42
@ -3,6 +3,7 @@ import { IEsploraApi } from './esplora-api.interface';
|
|||||||
export interface AbstractBitcoinApi {
|
export interface AbstractBitcoinApi {
|
||||||
$getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]>;
|
$getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]>;
|
||||||
$getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, lazyPrevouts?: boolean): Promise<IEsploraApi.Transaction>;
|
$getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, lazyPrevouts?: boolean): Promise<IEsploraApi.Transaction>;
|
||||||
|
$getRawTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]>;
|
||||||
$getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]>;
|
$getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]>;
|
||||||
$getAllMempoolTransactions(lastTxid: string);
|
$getAllMempoolTransactions(lastTxid: string);
|
||||||
$getTransactionHex(txId: string): Promise<string>;
|
$getTransactionHex(txId: string): Promise<string>;
|
||||||
|
@ -60,6 +60,10 @@ class BitcoinApi implements AbstractBitcoinApi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$getRawTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
|
||||||
|
throw new Error('Method getRawTransactions not supported by the Bitcoin RPC API.');
|
||||||
|
}
|
||||||
|
|
||||||
$getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
|
$getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
|
||||||
throw new Error('Method getMempoolTransactions not supported by the Bitcoin RPC API.');
|
throw new Error('Method getMempoolTransactions not supported by the Bitcoin RPC API.');
|
||||||
}
|
}
|
||||||
|
@ -213,6 +213,10 @@ class ElectrsApi implements AbstractBitcoinApi {
|
|||||||
return this.failoverRouter.$get<IEsploraApi.Transaction>('/tx/' + txId);
|
return this.failoverRouter.$get<IEsploraApi.Transaction>('/tx/' + txId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async $getRawTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
|
||||||
|
return this.$postWrapper<IEsploraApi.Transaction[]>(config.ESPLORA.REST_API_URL + '/txs', txids, 'json');
|
||||||
|
}
|
||||||
|
|
||||||
async $getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
|
async $getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
|
||||||
return this.failoverRouter.$post<IEsploraApi.Transaction[]>('/internal/mempool/txs', txids, 'json');
|
return this.failoverRouter.$post<IEsploraApi.Transaction[]>('/internal/mempool/txs', txids, 'json');
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import config from "../config";
|
|||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import { MempoolTransactionExtended, TransactionStripped } from "../mempool.interfaces";
|
import { MempoolTransactionExtended, TransactionStripped } from "../mempool.interfaces";
|
||||||
import bitcoinApi from './bitcoin/bitcoin-api-factory';
|
import bitcoinApi from './bitcoin/bitcoin-api-factory';
|
||||||
|
import { IEsploraApi } from "./bitcoin/esplora-api.interface";
|
||||||
import { Common } from "./common";
|
import { Common } from "./common";
|
||||||
import redisCache from "./redis-cache";
|
import redisCache from "./redis-cache";
|
||||||
|
|
||||||
@ -383,6 +384,7 @@ class RbfCache {
|
|||||||
});
|
});
|
||||||
logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`);
|
logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`);
|
||||||
this.staleCount = 0;
|
this.staleCount = 0;
|
||||||
|
await this.checkTrees();
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('failed to restore RBF cache: ' + (e instanceof Error ? e.message : e));
|
logger.err('failed to restore RBF cache: ' + (e instanceof Error ? e.message : e));
|
||||||
@ -481,6 +483,57 @@ class RbfCache {
|
|||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async checkTrees(): Promise<void> {
|
||||||
|
const found: { [txid: string]: boolean } = {};
|
||||||
|
const txids = Array.from(this.txs.values()).map(tx => tx.txid).filter(txid => {
|
||||||
|
return !this.expiring.has(txid) && !this.getRbfTree(txid)?.mined;
|
||||||
|
});
|
||||||
|
|
||||||
|
const processTxs = (txs: IEsploraApi.Transaction[]): void => {
|
||||||
|
for (const tx of txs) {
|
||||||
|
found[tx.txid] = true;
|
||||||
|
if (tx.status?.confirmed) {
|
||||||
|
const tree = this.getRbfTree(tx.txid);
|
||||||
|
if (tree) {
|
||||||
|
this.setTreeMined(tree, tx.txid);
|
||||||
|
tree.mined = true;
|
||||||
|
this.evict(tx.txid, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (config.MEMPOOL.BACKEND === 'esplora') {
|
||||||
|
const sliceLength = 10000;
|
||||||
|
for (let i = 0; i < Math.ceil(txids.length / sliceLength); i++) {
|
||||||
|
const slice = txids.slice(i * sliceLength, (i + 1) * sliceLength);
|
||||||
|
try {
|
||||||
|
const txs = await bitcoinApi.$getRawTransactions(slice);
|
||||||
|
processTxs(txs);
|
||||||
|
} catch (err) {
|
||||||
|
logger.err('failed to fetch some cached rbf transactions');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const txs: IEsploraApi.Transaction[] = [];
|
||||||
|
for (const txid of txids) {
|
||||||
|
try {
|
||||||
|
const tx = await bitcoinApi.$getRawTransaction(txid, true, false);
|
||||||
|
txs.push(tx);
|
||||||
|
} catch (err) {
|
||||||
|
// some 404s are expected, so continue quietly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processTxs(txs);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const txid of txids) {
|
||||||
|
if (!found[txid]) {
|
||||||
|
this.evict(txid, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getLatestRbfSummary(): ReplacementInfo[] {
|
public getLatestRbfSummary(): ReplacementInfo[] {
|
||||||
const rbfList = this.getRbfTrees(false);
|
const rbfList = this.getRbfTrees(false);
|
||||||
return rbfList.slice(0, 6).map(rbfTree => {
|
return rbfList.slice(0, 6).map(rbfTree => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user