Skip to content

Commit

Permalink
mempool: check input index when comparing replacement to conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Aug 21, 2023
1 parent 6f04c0d commit 28392c4
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
2 changes: 1 addition & 1 deletion lib/mempool/mempool.js
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,7 @@ class Mempool extends EventEmitter {
// must also be spent by the conflict.
for (const {prevout: conflictPrevout} of conflict.tx.inputs) {
if (conflictPrevout.hash.equals(prevout.hash)
&& conflictPrevout.input === prevout.input) {
&& conflictPrevout.index === prevout.index) {
// Once we find a match we don't need to check any more
// of the conflict's inputs. Continue by testing the
// next input in the potential replacement tx.
Expand Down
58 changes: 58 additions & 0 deletions test/mempool-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1638,5 +1638,63 @@ describe('Mempool', function() {
assert(mempool.has(tx1.hash()));
assert(!mempool.has(tx2.hash()));
});

it('should not accept replacement that does not evict its own inputs', async () => {
// ...because it is still adding a new unconfirmed input.
// {confirmed coin 1}
// |
// tx 0 {output 0} {output 1}
// | | |
// tx 1 +-------+ |
// | |
// tx 2

mempool.options.replaceByFee = true;

const addr1 = chaincoins.createReceive().getAddress();
const coin0 = chaincoins.getCoins()[0];

// Generate tx 0 which spends a confirmed coin and creates two outputs
const mtx0 = new MTX();
mtx0.addCoin(coin0);
mtx0.addOutput(addr1, parseInt(coin0.value / 2) - 200);
mtx0.addOutput(addr1, parseInt(coin0.value / 2) - 200);
chaincoins.sign(mtx0);
assert(mtx0.verify());
const tx0 = mtx0.toTX();
await mempool.addTX(tx0);

// Generate tx 1 which spends output 0 of tx 0
const mtx1 = new MTX();
const coin1 = Coin.fromTX(tx0, 0, -1);
mtx1.addCoin(coin1);
mtx1.inputs[0].sequence = 0xfffffffd;
mtx1.addOutput(addr1, coin1.value - 200);
chaincoins.sign(mtx1);
assert(mtx1.verify());
const tx1 = mtx1.toTX();
await mempool.addTX(tx1);

// Send tx 2 which spends outputs 0 & 1 of tx 0, replacing tx 1
const mtx2 = new MTX();
mtx2.addCoin(coin1);
const coin2 = Coin.fromTX(tx0, 1, -1);
mtx2.addCoin(coin2);
mtx2.addOutput(addr1, coin2.value + coin1.value - 1000);
chaincoins.sign(mtx2);
assert(mtx2.verify());
const tx2 = mtx2.toTX();

await assert.rejects(async () => {
await mempool.addTX(tx2);
}, {
type: 'VerifyError',
reason: 'replacement-adds-unconfirmed'
});

assert(mempool.has(tx0.hash()));
assert(mempool.has(tx1.hash()));
assert(!mempool.has(tx2.hash()));
});
});
});

0 comments on commit 28392c4

Please sign in to comment.