Skip to content

Commit 4638224

Browse files
committed
Add psbtbumpfee RPC
1 parent f32f7e9 commit 4638224

File tree

3 files changed

+33
-9
lines changed

3 files changed

+33
-9
lines changed

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
151151
{ "getmempoolancestors", 1, "verbose" },
152152
{ "getmempooldescendants", 1, "verbose" },
153153
{ "bumpfee", 1, "options" },
154+
{ "psbtbumpfee", 1, "options" },
154155
{ "logging", 0, "include" },
155156
{ "logging", 1, "exclude" },
156157
{ "disconnectnode", 1, "nodeid" },

src/wallet/rpcwallet.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3245,8 +3245,11 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
32453245

32463246
static UniValue bumpfee(const JSONRPCRequest& request)
32473247
{
3248-
RPCHelpMan{"bumpfee",
3248+
bool want_psbt = request.strMethod == "psbtbumpfee";
3249+
3250+
RPCHelpMan{request.strMethod,
32493251
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
3252+
+ std::string(want_psbt ? "Returns a PSBT instead of creating and signing a new transaction.\n" : "") +
32503253
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
32513254
"The command will pay the additional fee by reducing change outputs or adding inputs when necessary. It may add a new change output if one does not already exist.\n"
32523255
"All inputs in the original transaction will be included in the replacement transaction.\n"
@@ -3277,27 +3280,35 @@ static UniValue bumpfee(const JSONRPCRequest& request)
32773280
"options"},
32783281
},
32793282
RPCResult{
3280-
RPCResult::Type::OBJ, "", "", {
3281-
{RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled."},
3282-
{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."},
3283+
RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
3284+
{
3285+
{RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction." + std::string(want_psbt ? "" : " Only returned when wallet private keys are disabled. (DEPRECATED)")},
3286+
},
3287+
want_psbt ? std::vector<RPCResult>{} : std::vector<RPCResult>{{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."}}
3288+
),
3289+
{
32833290
{RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
32843291
{RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
32853292
{RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
32863293
{
32873294
{RPCResult::Type::STR, "", ""},
32883295
}},
3289-
}
3296+
})
32903297
},
32913298
RPCExamples{
3292-
"\nBump the fee, get the new transaction\'s txid\n" +
3293-
HelpExampleCli("bumpfee", "<txid>")
3299+
"\nBump the fee, get the new transaction\'s" + std::string(want_psbt ? "psbt" : "txid") + "\n" +
3300+
HelpExampleCli(request.strMethod, "<txid>")
32943301
},
32953302
}.Check(request);
32963303

32973304
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
32983305
if (!wallet) return NullUniValue;
32993306
CWallet* const pwallet = wallet.get();
33003307

3308+
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !want_psbt) {
3309+
want_psbt = true;
3310+
}
3311+
33013312
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
33023313
uint256 hash(ParseHashV(request.params[0], "txid"));
33033314

@@ -3382,7 +3393,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
33823393

33833394
// If wallet private keys are enabled, return the new transaction id,
33843395
// otherwise return the base64-encoded unsigned PSBT of the new transaction.
3385-
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
3396+
if (!want_psbt) {
33863397
if (!feebumper::SignTransaction(*pwallet, mtx)) {
33873398
throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
33883399
}
@@ -3415,6 +3426,11 @@ static UniValue bumpfee(const JSONRPCRequest& request)
34153426
return result;
34163427
}
34173428

3429+
static UniValue psbtbumpfee(const JSONRPCRequest& request)
3430+
{
3431+
return bumpfee(request);
3432+
}
3433+
34183434
UniValue rescanblockchain(const JSONRPCRequest& request)
34193435
{
34203436
RPCHelpMan{"rescanblockchain",
@@ -4160,6 +4176,7 @@ static const CRPCCommand commands[] =
41604176
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} },
41614177
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
41624178
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
4179+
{ "wallet", "psbtbumpfee", &psbtbumpfee, {"txid", "options"} },
41634180
{ "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors"} },
41644181
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
41654182
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },

test/functional/wallet_bumpfee.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,19 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
123123
self.sync_mempools((rbf_node, peer_node))
124124
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
125125
if mode == "fee_rate":
126+
bumped_psbt = rbf_node.psbtbumpfee(rbfid, {"fee_rate": NORMAL})
126127
bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": NORMAL})
127128
else:
129+
bumped_psbt = rbf_node.psbtbumpfee(rbfid)
128130
bumped_tx = rbf_node.bumpfee(rbfid)
129131
assert_equal(bumped_tx["errors"], [])
130132
assert bumped_tx["fee"] > -rbftx["fee"]
131133
assert_equal(bumped_tx["origfee"], -rbftx["fee"])
132134
assert "psbt" not in bumped_tx
135+
assert_equal(bumped_psbt["errors"], [])
136+
assert bumped_psbt["fee"] > -rbftx["fee"]
137+
assert_equal(bumped_psbt["origfee"], -rbftx["fee"])
138+
assert "psbt" in bumped_psbt
133139
# check that bumped_tx propagates, original tx was evicted and has a wallet conflict
134140
self.sync_mempools((rbf_node, peer_node))
135141
assert bumped_tx["txid"] in rbf_node.getrawmempool()
@@ -391,7 +397,7 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):
391397
assert_equal(len(watcher.decodepsbt(psbt)["tx"]["vin"]), 1)
392398

393399
# Bump fee, obnoxiously high to add additional watchonly input
394-
bumped_psbt = watcher.bumpfee(original_txid, {"fee_rate": HIGH})
400+
bumped_psbt = watcher.psbtbumpfee(original_txid, {"fee_rate": HIGH})
395401
assert_greater_than(len(watcher.decodepsbt(bumped_psbt['psbt'])["tx"]["vin"]), 1)
396402
assert "txid" not in bumped_psbt
397403
assert_equal(bumped_psbt["origfee"], -watcher.gettransaction(original_txid)["fee"])

0 commit comments

Comments
 (0)