Skip to content

Commit 3b1dc6b

Browse files
committed
cli/http: add bwallet-cli bump <txid> <rate> <sign> <passphrase>
1 parent 161089f commit 3b1dc6b

File tree

5 files changed

+67
-7
lines changed

5 files changed

+67
-7
lines changed

bin/bwallet-cli

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,17 @@ class CLI {
364364
this.log('Abandoned tx: ' + hash);
365365
}
366366

367+
async bumpTX() {
368+
const hash = this.config.str([0, 'hash']);
369+
const rate = this.config.uint([1, 'rate']);
370+
const sign = this.config.bool([2, 'sign']);
371+
const passphrase = this.config.str([3, 'passphrase']);
372+
373+
const tx = await this.wallet.bumpTX(hash, rate, sign, passphrase);
374+
375+
this.log(tx);
376+
}
377+
367378
async getDetails() {
368379
const hash = this.config.str(0);
369380
const details = await this.wallet.getTX(hash);
@@ -605,6 +616,9 @@ class CLI {
605616
case 'rescan':
606617
await this.rescan();
607618
break;
619+
case 'bump':
620+
await this.bumpTX();
621+
break;
608622
default:
609623
this.log('Unrecognized command.');
610624
this.log('Commands:');
@@ -617,6 +631,7 @@ class CLI {
617631
this.log(' $ balance: Get wallet balance.');
618632
this.log(' $ block [height]: View wallet block.');
619633
this.log(' $ blocks: List wallet blocks.');
634+
this.log(' $ bump [hash]: Bump TX fee with replacement.');
620635
this.log(' $ change: Derive new change address.');
621636
this.log(' $ coins: View wallet coins.');
622637
this.log(' $ dump [address]: Get wallet key WIF by address.');

lib/client/wallet.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,21 @@ class WalletClient extends Client {
356356
return this.del(`/wallet/${id}/tx/${hash}`);
357357
}
358358

359+
/**
360+
* @param {Number} id
361+
* @param {Hash} hash
362+
* @param {Number?} rate
363+
* @param {Bool?} sign
364+
* @param {String?} passphrase
365+
* @returns {Promise}
366+
*/
367+
368+
bumpTX(id, hash, rate, sign, passphrase) {
369+
return this.post(
370+
`/wallet/${id}/bump/${hash}`,
371+
{hash, rate, sign, passphrase});
372+
}
373+
359374
/**
360375
* Create a transaction, fill.
361376
* @param {Number} id
@@ -832,6 +847,19 @@ class Wallet extends EventEmitter {
832847
return this.client.abandon(this.id, hash);
833848
}
834849

850+
/**
851+
* Send an RBF replacement to bump fee
852+
* @param {Hash} hash
853+
* @param {Number?} rate
854+
* @param {Bool?} sign
855+
* @param {String?} passphrase
856+
* @returns {Promise}
857+
*/
858+
859+
bumpTX(hash, rate, sign, passphrase) {
860+
return this.client.bumpTX(this.id, hash);
861+
}
862+
835863
/**
836864
* Create a transaction, fill.
837865
* @param {Object} options

lib/wallet/http.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,23 @@ class HTTP extends Server {
548548
res.json(200, tx.getJSON(this.network));
549549
});
550550

551+
// Create replacement fee-bump TX
552+
this.post('/wallet/:id/bump/:hash', async (req, res) => {
553+
const valid = Validator.fromRequest(req);
554+
const hash = valid.brhash('hash');
555+
enforce(hash, 'txid is required.');
556+
let rate = valid.u64('rate');
557+
if (!rate)
558+
rate = this.network.minRelay;
559+
const sign = valid.bool('sign', true);
560+
const passphrase = valid.str('passphrase');
561+
562+
// Bump fee by reducing change output value.
563+
const tx = await req.wallet.bumpTXFee(hash, rate, sign, passphrase);
564+
565+
res.json(200, tx.getJSON(this.network));
566+
});
567+
551568
// Zap Wallet TXs
552569
this.post('/wallet/:id/zap', async (req, res) => {
553570
const valid = Validator.fromRequest(req);

lib/wallet/wallet.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,12 +1301,12 @@ class Wallet extends EventEmitter {
13011301
* Complies with BIP-125 replace-by-fee
13021302
* @param {Hash} hash
13031303
* @param {Rate} rate
1304-
* @param {(String|Buffer)?} passphrase
13051304
* @param {Boolean?} sign - sign with wallet
1305+
* @param {(String|Buffer)?} passphrase
13061306
* @returns {Promise} - Returns {@link TX}.
13071307
*/
13081308

1309-
async bumpTXFee(hash, rate, passphrase, sign) {
1309+
async bumpTXFee(hash, rate, sign, passphrase) {
13101310
assert((rate >>> 0) === rate, 'Rate must be a number.');
13111311

13121312
const wtx = await this.getTX(hash);

test/wallet-rbf-test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe('Wallet RBF', function () {
4747
it('should not replace missing tx', async () => {
4848
const dummyHash = Buffer.alloc(32, 0x10);
4949
assert.rejects(async () => {
50-
await alice.bumpTXFee(dummyHash, 1000 /* satoshis per kvB */, null, true);
50+
await alice.bumpTXFee(dummyHash, 1000 /* satoshis per kvB */, true, null);
5151
}, {
5252
message: 'Transaction not found.'
5353
});
@@ -57,7 +57,7 @@ describe('Wallet RBF', function () {
5757
const txs = await alice.getHistory();
5858
const cb = txs[0];
5959
assert.rejects(async () => {
60-
await alice.bumpTXFee(cb.hash, 1000 /* satoshis per kvB */, null, true);
60+
await alice.bumpTXFee(cb.hash, 1000 /* satoshis per kvB */, true, null);
6161
}, {
6262
message: 'Transaction is confirmed.'
6363
});
@@ -78,7 +78,7 @@ describe('Wallet RBF', function () {
7878
assert(node.mempool.hasEntry(tx.hash()));
7979

8080
assert.rejects(async () => {
81-
await alice.bumpTXFee(tx.hash(), 1000 /* satoshis per kvB */, null, true);
81+
await alice.bumpTXFee(tx.hash(), 1000 /* satoshis per kvB */, true, null);
8282
}, {
8383
message: 'Transaction does not signal opt-in replace-by-fee.'
8484
});
@@ -112,7 +112,7 @@ describe('Wallet RBF', function () {
112112
assert(node.mempool.hasEntry(tx2.hash()));
113113

114114
assert.rejects(async () => {
115-
await alice.bumpTXFee(tx1.hash(), 1000 /* satoshis per kvB */, null, true);
115+
await alice.bumpTXFee(tx1.hash(), 1000 /* satoshis per kvB */, true, null);
116116
}, {
117117
message: 'Transaction has descendants in the wallet.'
118118
});
@@ -132,7 +132,7 @@ describe('Wallet RBF', function () {
132132
await forEvent(node.mempool, 'tx');
133133
assert(node.mempool.has(tx.hash()));
134134

135-
const rtx = await alice.bumpTXFee(tx.hash(), 1000 /* satoshis per kvB */, null, true);
135+
const rtx = await alice.bumpTXFee(tx.hash(), 1000 /* satoshis per kvB */, true, null);
136136

137137
await forEvent(node.mempool, 'tx');
138138
assert(!node.mempool.hasEntry(tx.hash()));

0 commit comments

Comments
 (0)