mirror of
https://github.com/mempool/mempool.git
synced 2026-08-13 12:33:11 +02:00
fix cluster mempool tx eviction bug
This commit is contained in:
parent
2d7a7d5a35
commit
cea125c121
5 changed files with 78 additions and 28 deletions
|
|
@ -244,8 +244,10 @@ class Harness {
|
|||
|
||||
const added = await this.fetchTransactions(addedTxids);
|
||||
|
||||
// Apply diff before deleting from cache — processRemovals needs the tx data
|
||||
const diff: MempoolDiff = { added, removed: removedTxids, accelerations: {} };
|
||||
const removed = removedTxids
|
||||
.map(txid => this.mempool[txid])
|
||||
.filter((tx): tx is MempoolTransactionExtended => !!tx);
|
||||
const diff: MempoolDiff = { added, removed, accelerations: {} };
|
||||
const t0 = Date.now();
|
||||
this.clusterMempool.applyMempoolChange(diff);
|
||||
const dt = Date.now() - t0;
|
||||
|
|
|
|||
|
|
@ -115,15 +115,16 @@ describe('ClusterMempool', () => {
|
|||
it('should handle removing a tx', () => {
|
||||
const parentId = txid('a1');
|
||||
const childId = txid('a2');
|
||||
const childTx = makeTx(childId, 5000, 100, [parentId]);
|
||||
const mempool = buildMempool([
|
||||
makeTx(parentId, 100, 100),
|
||||
makeTx(childId, 5000, 100, [parentId]),
|
||||
childTx,
|
||||
]);
|
||||
const cm = new ClusterMempool(mempool);
|
||||
|
||||
cm.applyMempoolChange({
|
||||
added: [],
|
||||
removed: [childId],
|
||||
removed: [childTx],
|
||||
accelerations: {},
|
||||
});
|
||||
|
||||
|
|
@ -132,13 +133,61 @@ describe('ClusterMempool', () => {
|
|||
expect(cm.getClusterInfo(parentId)).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should clean spentBy when removed tx is already missing from mempool', () => {
|
||||
const parentId = txid('a1');
|
||||
const childId = txid('a2');
|
||||
const childTx = makeTx(childId, 5000, 100, [parentId]);
|
||||
const mempool = buildMempool([
|
||||
makeTx(parentId, 100, 100),
|
||||
childTx,
|
||||
]);
|
||||
const cm = new ClusterMempool(mempool);
|
||||
const spentBy = (cm as unknown as { spentBy: Map<string, string> }).spentBy;
|
||||
|
||||
expect(spentBy.get(`${parentId}:0`)).toBe(childId);
|
||||
|
||||
delete mempool[childId];
|
||||
cm.applyMempoolChange({
|
||||
added: [],
|
||||
removed: [childTx],
|
||||
accelerations: {},
|
||||
});
|
||||
|
||||
expect(spentBy.has(`${parentId}:0`)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not delete spentBy for a replacement transaction', () => {
|
||||
const parentId = txid('a1');
|
||||
const replacedId = txid('a2');
|
||||
const replacementId = txid('a3');
|
||||
const replacedTx = makeTx(replacedId, 5000, 100, [parentId]);
|
||||
const replacementTx = makeTx(replacementId, 6000, 100, [parentId]);
|
||||
const mempool = buildMempool([
|
||||
makeTx(parentId, 100, 100),
|
||||
replacementTx,
|
||||
]);
|
||||
const cm = new ClusterMempool(mempool);
|
||||
const spentBy = (cm as unknown as { spentBy: Map<string, string> }).spentBy;
|
||||
|
||||
expect(spentBy.get(`${parentId}:0`)).toBe(replacementId);
|
||||
|
||||
cm.applyMempoolChange({
|
||||
added: [],
|
||||
removed: [replacedTx],
|
||||
accelerations: {},
|
||||
});
|
||||
|
||||
expect(spentBy.get(`${parentId}:0`)).toBe(replacementId);
|
||||
});
|
||||
|
||||
it('should split cluster when middle tx is removed', () => {
|
||||
const a = txid('a1');
|
||||
const b = txid('b1');
|
||||
const c = txid('c1');
|
||||
const bTx = makeTx(b, 200, 100, [a]);
|
||||
const mempool = buildMempool([
|
||||
makeTx(a, 100, 100),
|
||||
makeTx(b, 200, 100, [a]),
|
||||
bTx,
|
||||
makeTx(c, 300, 100, [b]),
|
||||
]);
|
||||
const cm = new ClusterMempool(mempool);
|
||||
|
|
@ -146,7 +195,7 @@ describe('ClusterMempool', () => {
|
|||
|
||||
cm.applyMempoolChange({
|
||||
added: [],
|
||||
removed: [b],
|
||||
removed: [bTx],
|
||||
accelerations: {},
|
||||
});
|
||||
|
||||
|
|
@ -277,7 +326,7 @@ describe('ClusterMempool', () => {
|
|||
const cm = new ClusterMempool(mempool);
|
||||
expect(cm.getClusterCount()).toBe(1);
|
||||
|
||||
cm.applyMempoolChange({ added: [], removed: [center], accelerations: {} });
|
||||
cm.applyMempoolChange({ added: [], removed: [centerTx], accelerations: {} });
|
||||
|
||||
expect(cm.getTxCount()).toBe(5);
|
||||
expect(cm.getClusterCount()).toBe(5);
|
||||
|
|
@ -297,7 +346,7 @@ describe('ClusterMempool', () => {
|
|||
const cm = new ClusterMempool(mempool);
|
||||
expect(cm.getClusterCount()).toBe(1);
|
||||
|
||||
cm.applyMempoolChange({ added: [], removed: [c], accelerations: {} });
|
||||
cm.applyMempoolChange({ added: [], removed: [cTx], accelerations: {} });
|
||||
|
||||
expect(cm.getClusterCount()).toBe(1);
|
||||
expect(cm.getTxCount()).toBe(2);
|
||||
|
|
@ -306,13 +355,14 @@ describe('ClusterMempool', () => {
|
|||
it('should produce singleton when tx is removed from 2-tx cluster', () => {
|
||||
const a = txid('a1');
|
||||
const b = txid('b1');
|
||||
const bTx = makeTx(b, 200, 100, [a]);
|
||||
const mempool = buildMempool([
|
||||
makeTx(a, 100, 100),
|
||||
makeTx(b, 200, 100, [a]),
|
||||
bTx,
|
||||
]);
|
||||
const cm = new ClusterMempool(mempool);
|
||||
|
||||
cm.applyMempoolChange({ added: [], removed: [b], accelerations: {} });
|
||||
cm.applyMempoolChange({ added: [], removed: [bTx], accelerations: {} });
|
||||
|
||||
expect(cm.getClusterCount()).toBe(1);
|
||||
expect(cm.getTxCount()).toBe(1);
|
||||
|
|
@ -339,7 +389,7 @@ describe('ClusterMempool', () => {
|
|||
const cm = new ClusterMempool(mempool);
|
||||
expect(cm.getClusterCount()).toBe(1);
|
||||
|
||||
cm.applyMempoolChange({ added: [], removed: [hub], accelerations: {} });
|
||||
cm.applyMempoolChange({ added: [], removed: [hubTx], accelerations: {} });
|
||||
|
||||
expect(cm.getClusterCount()).toBe(3);
|
||||
expect(cm.getTxCount()).toBe(3);
|
||||
|
|
@ -484,7 +534,7 @@ describe('ClusterMempool', () => {
|
|||
|
||||
cm.applyMempoolChange({
|
||||
added: [],
|
||||
removed: [txid('nonexistent')],
|
||||
removed: [makeTx(txid('nonexistent'), 100, 100)],
|
||||
accelerations: {},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -511,7 +511,7 @@ class Blocks {
|
|||
if (config.MEMPOOL.CLUSTER_MEMPOOL) {
|
||||
memPool.clusterMempool?.applyMempoolChange({
|
||||
added: [],
|
||||
removed: txIds,
|
||||
removed: transactions,
|
||||
accelerations: mempool.getAccelerations(),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -398,7 +398,7 @@ class Mempool {
|
|||
if (config.MEMPOOL.CLUSTER_MEMPOOL && (newTransactions.length || deletedTransactions.length || accelerationDelta.length)) {
|
||||
this.clusterMempool?.applyMempoolChange({
|
||||
added: newTransactions,
|
||||
removed: deletedTransactions.map(tx => tx.txid),
|
||||
removed: deletedTransactions,
|
||||
accelerations: this.getAccelerations(),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import logger from '../logger';
|
|||
|
||||
export interface MempoolDiff {
|
||||
added: MempoolTransactionExtended[];
|
||||
removed: string[];
|
||||
removed: MempoolTransactionExtended[];
|
||||
accelerations: { [txid: string]: { feeDelta: number } };
|
||||
}
|
||||
|
||||
|
|
@ -349,27 +349,25 @@ export class ClusterMempool {
|
|||
return relatives;
|
||||
}
|
||||
|
||||
private processRemovals(removed: string[]): void {
|
||||
for (const txid of removed) {
|
||||
const tx = this.mempool[txid];
|
||||
if (tx) {
|
||||
for (const vin of tx.vin) {
|
||||
if (!vin.is_coinbase) {
|
||||
this.spentBy.delete(`${vin.txid}:${vin.vout}`);
|
||||
private processRemovals(removed: MempoolTransactionExtended[]): void {
|
||||
for (const tx of removed) {
|
||||
for (const vin of tx.vin) {
|
||||
if (!vin.is_coinbase) {
|
||||
const spentOutpoint = `${vin.txid}:${vin.vout}`;
|
||||
if (this.spentBy.get(spentOutpoint) === tx.txid) {
|
||||
this.spentBy.delete(spentOutpoint);
|
||||
}
|
||||
}
|
||||
} else if (this.txToCluster.has(txid)) {
|
||||
logger.warn(`ClusterMempool.processRemovals: ${txid} missing from mempool, spentBy cleanup skipped`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const txid of removed) {
|
||||
const match = this.getClusterForTx(txid);
|
||||
for (const tx of removed) {
|
||||
const match = this.getClusterForTx(tx.txid);
|
||||
if (match) {
|
||||
match.cluster.depgraph.removeTransactions(new Set([match.clusterTx]));
|
||||
match.cluster.txs.delete(txid);
|
||||
match.cluster.txs.delete(tx.txid);
|
||||
match.cluster.linearization = match.cluster.linearization.filter(t => t !== match.clusterTx);
|
||||
this.txToCluster.delete(txid);
|
||||
this.txToCluster.delete(tx.txid);
|
||||
match.cluster.dirty = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue