From 86b081b3547c8ecf0db5d12342477c3e9e3fd01a Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Fri, 11 Aug 2023 16:23:05 -0400 Subject: [PATCH] cli/http: add bwallet-cli bump --- bin/bwallet-cli | 15 +++++++++++++++ lib/client/wallet.js | 28 ++++++++++++++++++++++++++++ lib/wallet/http.js | 17 +++++++++++++++++ lib/wallet/wallet.js | 4 ++-- 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/bin/bwallet-cli b/bin/bwallet-cli index cc853e2ef..409975f77 100755 --- a/bin/bwallet-cli +++ b/bin/bwallet-cli @@ -364,6 +364,17 @@ class CLI { this.log('Abandoned tx: ' + hash); } + async bumpTX() { + const hash = this.config.str([0, 'hash']); + const rate = this.config.uint([1, 'rate']); + const sign = this.config.bool([2, 'sign']); + const passphrase = this.config.str([3, 'passphrase']); + + const tx = await this.wallet.bumpTX(hash, rate, sign, passphrase); + + this.log(tx); + } + async getDetails() { const hash = this.config.str(0); const details = await this.wallet.getTX(hash); @@ -605,6 +616,9 @@ class CLI { case 'rescan': await this.rescan(); break; + case 'bump': + await this.bumpTX(); + break; default: this.log('Unrecognized command.'); this.log('Commands:'); @@ -617,6 +631,7 @@ class CLI { this.log(' $ balance: Get wallet balance.'); this.log(' $ block [height]: View wallet block.'); this.log(' $ blocks: List wallet blocks.'); + this.log(' $ bump [hash]: Bump TX fee with replacement.'); this.log(' $ change: Derive new change address.'); this.log(' $ coins: View wallet coins.'); this.log(' $ dump [address]: Get wallet key WIF by address.'); diff --git a/lib/client/wallet.js b/lib/client/wallet.js index 812386e26..020de66e8 100644 --- a/lib/client/wallet.js +++ b/lib/client/wallet.js @@ -356,6 +356,21 @@ class WalletClient extends Client { return this.del(`/wallet/${id}/tx/${hash}`); } + /** + * @param {Number} id + * @param {Hash} hash + * @param {Number?} rate + * @param {Bool?} sign + * @param {String?} passphrase + * @returns {Promise} + */ + + bumpTX(id, hash, rate, sign, passphrase) { + return this.post( + `/wallet/${id}/bump/${hash}`, + {hash, rate, sign, passphrase}); + } + /** * Create a transaction, fill. * @param {Number} id @@ -832,6 +847,19 @@ class Wallet extends EventEmitter { return this.client.abandon(this.id, hash); } + /** + * Send an RBF replacement to bump fee + * @param {Hash} hash + * @param {Number?} rate + * @param {Bool?} sign + * @param {String?} passphrase + * @returns {Promise} + */ + + bumpTX(hash, rate, sign, passphrase) { + return this.client.bumpTX(this.id, hash); + } + /** * Create a transaction, fill. * @param {Object} options diff --git a/lib/wallet/http.js b/lib/wallet/http.js index ce194aa8f..6a3749a22 100644 --- a/lib/wallet/http.js +++ b/lib/wallet/http.js @@ -548,6 +548,23 @@ class HTTP extends Server { res.json(200, tx.getJSON(this.network)); }); + // Create replacement fee-bump TX + this.post('/wallet/:id/bump/:hash', async (req, res) => { + const valid = Validator.fromRequest(req); + const hash = valid.brhash('hash'); + enforce(hash, 'txid is required.'); + let rate = valid.u64('rate'); + if (!rate) + rate = this.network.minRelay; + const sign = valid.bool('sign', true); + const passphrase = valid.str('passphrase'); + + // Bump fee by reducing change output value. + const tx = await req.wallet.bumpTXFee(hash, rate, sign, passphrase); + + res.json(200, tx.getJSON(this.network)); + }); + // Zap Wallet TXs this.post('/wallet/:id/zap', async (req, res) => { const valid = Validator.fromRequest(req); diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index 0e7a4d680..66ed6bb90 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -1301,12 +1301,12 @@ class Wallet extends EventEmitter { * Complies with BIP-125 replace-by-fee * @param {Hash} hash * @param {Rate} rate - * @param {(String|Buffer)?} passphrase * @param {Boolean?} sign - sign with wallet + * @param {(String|Buffer)?} passphrase * @returns {Promise} - Returns {@link TX}. */ - async bumpTXFee(hash, rate, passphrase, sign) { + async bumpTXFee(hash, rate, sign, passphrase) { assert((rate >>> 0) === rate, 'Rate must be a number.'); const wtx = await this.getTX(hash);