Skip to content

Commit

Permalink
wallet rbf: do not violate rule 6
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Sep 22, 2023
1 parent 4d97480 commit 5f472ad
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 3 deletions.
26 changes: 24 additions & 2 deletions lib/wallet/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -1309,7 +1309,7 @@ class Wallet extends EventEmitter {

async bumpTXFee(hash, rate, sign, passphrase) {
assert((rate >>> 0) === rate, 'Rate must be a number.');
assert(rate >= this.network.minRelay, 'Provided fee rate is too low.');
assert(rate >= this.network.minRelay, 'Fee rate is below minimum.');

const wtx = await this.getTX(hash);

Expand Down Expand Up @@ -1415,14 +1415,36 @@ class Wallet extends EventEmitter {
});
}

if (!sign)
const oldRate = tx.getRate(view);

if (!sign) {
// Estimate final fee after signing for rule 6 check
const estSize =
await mtx.estimateSize(this.getAccountByAddress.bind(this));
const fee = mtx.getFee();
const estRate = policy.getRate(estSize, fee);

if (estRate <= oldRate) {
throw new Error(`Provided fee rate of ${rate} s/kvB results in ` +
`insufficient estimated total fee rate (${estRate}) ` +
`to replace original (${oldRate})`);
}

return mtx.toTX();

Check warning on line 1433 in lib/wallet/wallet.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/wallet.js#L1433

Added line #L1433 was not covered by tests
}

await this.sign(mtx, passphrase);

if (!mtx.isSigned())
throw new Error('Replacement TX could not be fully signed.');

Check warning on line 1439 in lib/wallet/wallet.js

View check run for this annotation

Codecov / codecov/patch

lib/wallet/wallet.js#L1439

Added line #L1439 was not covered by tests

const newRate = mtx.getRate();
if (newRate <= oldRate) {
throw new Error(`Provided fee rate of ${rate} s/kvB results in ` +
`insufficient total fee rate (${newRate}) ` +
`to replace original (${oldRate})`);
}

const ntx = mtx.toTX();

this.logger.debug(
Expand Down
38 changes: 37 additions & 1 deletion test/wallet-rbf-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ describe('Wallet RBF', function () {
// Try a fee rate below minRelay (1000)
await alice.bumpTXFee(tx.hash(), 999 /* satoshis per kvB */, true, null);
}, {
message: 'Provided fee rate is too low.'
message: 'Fee rate is below minimum.'
});
await node.rpc.generateToAddress([1, aliceReceive]);
});
Expand Down Expand Up @@ -217,4 +217,40 @@ describe('Wallet RBF', function () {

await node.rpc.generateToAddress([1, aliceReceive]);
});

it('should not violate rule 6 signed or unsigned', async () => {
const coins = await alice.getCoins();
let coin;
for (coin of coins) {
if (!coin.coinbase)
break;
}
const mtx = new MTX();
mtx.addCoin(coin);
mtx.addOutput(bobReceive, coin.value - 200);
mtx.inputs[0].sequence = 0xfffffffd;
await alice.sign(mtx);
const tx = mtx.toTX();
assert.strictEqual(tx.inputs.length, 1);
assert.strictEqual(tx.outputs.length, 1);
await alice.wdb.addTX(tx);
await alice.wdb.send(tx);
await forEvent(node.mempool, 'tx');

// Do not sign, estimate fee rate
await assert.rejects(async () => {
await alice.bumpTXFee(tx.hash(), 1000 /* satoshis per kvB */, false, null);
}, {
message: /^Provided fee rate of 1000 s\/kvB results in insufficient estimated total fee rate/
});

// Do sign, then check fee rate
await assert.rejects(async () => {
await alice.bumpTXFee(tx.hash(), 1000 /* satoshis per kvB */, true, null);
}, {
message: /^Provided fee rate of 1000 s\/kvB results in insufficient total fee rate/
});

await node.rpc.generateToAddress([1, aliceReceive]);
});
});

0 comments on commit 5f472ad

Please sign in to comment.