Skip to content

Commit

Permalink
mempool: evict expired transactions regardless of mempool size
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Jan 12, 2021
1 parent 5836f8d commit 633c8f4
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 13 deletions.
32 changes: 19 additions & 13 deletions lib/mempool/mempool.js
Original file line number Diff line number Diff line change
Expand Up @@ -583,14 +583,29 @@ class Mempool extends EventEmitter {

limitSize(added) {
const maxSize = this.options.maxSize;
const expiryTime = this.options.expiryTime;
const now = util.now();

// Clear out expired transactions first
for (const entry of this.map.values()) {
// If this tx has ancestors in the mempool, we will find and
// evict the oldest and its entire chain of descendants
if (this.hasDepends(entry.tx))
continue;

if (now >= entry.time + expiryTime) {
this.logger.debug(
'Removing package %x from mempool (too old).',
entry.hash());

this.evictEntry(entry);
}
}

if (this.size <= maxSize)
return false;

const threshold = maxSize - (maxSize / 10);
const expiryTime = this.options.expiryTime;

const now = util.now();
const queue = new Heap(cmpRate);

let start = util.bench();
Expand All @@ -599,16 +614,7 @@ class Mempool extends EventEmitter {
if (this.hasDepends(entry.tx))
continue;

if (now < entry.time + expiryTime) {
queue.insert(entry);
continue;
}

this.logger.debug(
'Removing package %x from mempool (too old).',
entry.hash());

this.evictEntry(entry);
queue.insert(entry);
}

if (this.size <= threshold) {
Expand Down
58 changes: 58 additions & 0 deletions test/mempool-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1243,5 +1243,63 @@ describe('Mempool', function() {
}
}
});

it('should evict old transactions', async () => {
// Clear mempool. Note that TXs in last test were not
// added to the wallet: we can re-spend those coins.
await mempool.reset();

let now = 0;
const original = util.now;
try {
util.now = () => {
return now;
};

// After we cross the expiry threshold, one TX at a time
// will start to expire, starting with the oldest.
const sent = [];
let evicted = 0;
mempool.on('remove entry', (entry) => {
const expected = sent.shift();
assert.bufferEqual(entry.tx.hash(), expected);
evicted++;
});

for (let i = 0; i < N; i++) {
// Spend a different coin each time to avoid exceeding max ancestors.
const coin = chaincoins.getCoins()[i];
const addr = wallet.createReceive().getAddress();

const mtx = new MTX();
mtx.addCoin(coin);
mtx.addOutput(addr, 90000);
chaincoins.sign(mtx);
const tx = mtx.toTX();

sent.push(tx.hash());

// mempool size is not a factor
assert(mempool.size + (txMemUsage * 2) < maxSize);

await mempool.addTX(tx);

// Time travel forward ten minutes
now += 60 * 10;

// The first 6 TXs are added without incident.
// After that, a virtual hour will have passed, and
// each new TX will trigger the eviction of one old TX.
if (i < 6) {
assert(mempool.map.size === i + 1);
} else {
assert(mempool.map.size === 6);
assert.strictEqual(evicted, (i + 1) - 6);
}
}
} finally {
util.now = original;
}
});
});
});

0 comments on commit 633c8f4

Please sign in to comment.