Merge pull request #3719 from mempool/mononaut/fix-rbf-cache-eviction

fix rbf cache eviction logic
This commit is contained in:
softsimon 2023-05-05 14:27:56 +04:00 committed by GitHub
commit a7dff0effe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,3 +1,4 @@
import logger from "../logger";
import { TransactionExtended, TransactionStripped } from "../mempool.interfaces"; import { TransactionExtended, TransactionStripped } from "../mempool.interfaces";
import bitcoinApi from './bitcoin/bitcoin-api-factory'; import bitcoinApi from './bitcoin/bitcoin-api-factory';
import { Common } from "./common"; import { Common } from "./common";
@ -23,10 +24,10 @@ class RbfCache {
private dirtyTrees: Set<string> = new Set(); private dirtyTrees: Set<string> = new Set();
private treeMap: Map<string, string> = new Map(); // map of txids to sequence ids private treeMap: Map<string, string> = new Map(); // map of txids to sequence ids
private txs: Map<string, TransactionExtended> = new Map(); private txs: Map<string, TransactionExtended> = new Map();
private expiring: Map<string, Date> = new Map(); private expiring: Map<string, number> = new Map();
constructor() { constructor() {
setInterval(this.cleanup.bind(this), 1000 * 60 * 60); setInterval(this.cleanup.bind(this), 1000 * 60 * 10);
} }
public add(replaced: TransactionExtended[], newTxExtended: TransactionExtended): void { public add(replaced: TransactionExtended[], newTxExtended: TransactionExtended): void {
@ -146,6 +147,9 @@ class RbfCache {
} }
public mined(txid): void { public mined(txid): void {
if (!this.txs.has(txid)) {
return;
}
const treeId = this.treeMap.get(txid); const treeId = this.treeMap.get(txid);
if (treeId && this.rbfTrees.has(treeId)) { if (treeId && this.rbfTrees.has(treeId)) {
const tree = this.rbfTrees.get(treeId); const tree = this.rbfTrees.get(treeId);
@ -159,18 +163,21 @@ class RbfCache {
} }
// flag a transaction as removed from the mempool // flag a transaction as removed from the mempool
public evict(txid): void { public evict(txid, fast: boolean = false): void {
this.expiring.set(txid, new Date(Date.now() + 1000 * 86400)); // 24 hours if (this.txs.has(txid) && (fast || !this.expiring.has(txid))) {
this.expiring.set(txid, fast ? Date.now() + (1000 * 60 * 10) : Date.now() + (1000 * 86400)); // 24 hours
}
} }
private cleanup(): void { private cleanup(): void {
const currentDate = new Date(); const now = Date.now();
for (const txid in this.expiring) { for (const txid of this.expiring.keys()) {
if ((this.expiring.get(txid) || 0) < currentDate) { if ((this.expiring.get(txid) || 0) < now) {
this.expiring.delete(txid); this.expiring.delete(txid);
this.remove(txid); this.remove(txid);
} }
} }
logger.debug(`rbf cache contains ${this.txs.size} txs, ${this.expiring.size} due to expire`);
} }
// remove a transaction & all previous versions from the cache // remove a transaction & all previous versions from the cache
@ -237,7 +244,9 @@ class RbfCache {
await this.importTree(deflatedTree.root, deflatedTree.root, deflatedTree, this.txs); await this.importTree(deflatedTree.root, deflatedTree.root, deflatedTree, this.txs);
} }
expiring.forEach(expiringEntry => { expiring.forEach(expiringEntry => {
this.expiring.set(expiringEntry[0], expiringEntry[1]); if (this.txs.has(expiringEntry[0])) {
this.expiring.set(expiringEntry[0], new Date(expiringEntry[1]).getTime());
}
}); });
this.cleanup(); this.cleanup();
} }
@ -269,18 +278,29 @@ class RbfCache {
// check if any transactions in this tree have already been confirmed // check if any transactions in this tree have already been confirmed
mined = mined || treeInfo.mined; mined = mined || treeInfo.mined;
let exists = mined;
if (!mined) { if (!mined) {
try { try {
const apiTx = await bitcoinApi.$getRawTransaction(txid); const apiTx = await bitcoinApi.$getRawTransaction(txid);
if (apiTx) {
exists = true;
}
if (apiTx?.status?.confirmed) { if (apiTx?.status?.confirmed) {
mined = true; mined = true;
this.evict(txid); treeInfo.txMined = true;
this.evict(txid, true);
} }
} catch (e) { } catch (e) {
// most transactions do not exist // most transactions do not exist
} }
} }
// if the root tx is not in the mempool or the blockchain
// evict this tree as soon as possible
if (root === txid && !exists) {
this.evict(txid, true);
}
// recursively reconstruct child trees // recursively reconstruct child trees
for (const childId of treeInfo.replaces) { for (const childId of treeInfo.replaces) {
const replaced = await this.importTree(root, childId, deflated, txs, mined); const replaced = await this.importTree(root, childId, deflated, txs, mined);