Skip to content

Commit d759b5d

Browse files
author
MarcoFalke
committed
Merge #15911: Use wallet RBF default for walletcreatefundedpsbt
d6b3640 [test] walletcreatefundedpsbt: check RBF is disabled when -walletrbf=0 (Sjors Provoost) 9ed062b [doc] rpc: remove "fallback to" from RBF default help (Sjors Provoost) 4fcb698 [rpc] walletcreatefundedpsbt: use wallet default RBF (Sjors Provoost) Pull request description: The `walletcreatefundedpsbt` RPC call currently ignores `-walletrbf` and defaults to not use RBF. This PR fixes that. This PR also replaces UniValue in `ConstructTransaction` with a `bool` in preparation of moving this helper method out of the RPC codebase entirely. This may be a bit overkill, but does slightly simplify it. Fixes #15878 ACKs for top commit: achow101: Code Review ACK d6b3640 l2a5b1: re-ACK d6b3640 MarcoFalke: ACK d6b3640 Tree-SHA512: 55b9bccd1ef36b54f6b34793017dc0721103099ad3761b3b04862291ee13d6915915d4dbb1a8567924fa56e5e95dfe10eec070e06701610e70c87f8ea92b2a00
2 parents 9f54e9a + d6b3640 commit d759b5d

File tree

5 files changed

+47
-23
lines changed

5 files changed

+47
-23
lines changed

src/rpc/rawtransaction.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,11 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
406406
}, true
407407
);
408408

409-
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
409+
bool rbf = false;
410+
if (!request.params[3].isNull()) {
411+
rbf = request.params[3].isTrue();
412+
}
413+
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
410414

411415
return EncodeHexTx(CTransaction(rawTx));
412416
}
@@ -1365,7 +1369,11 @@ UniValue createpsbt(const JSONRPCRequest& request)
13651369
}, true
13661370
);
13671371

1368-
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
1372+
bool rbf = false;
1373+
if (!request.params[3].isNull()) {
1374+
rbf = request.params[3].isTrue();
1375+
}
1376+
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
13691377

13701378
// Make a blank psbt
13711379
PartiallySignedTransaction psbtx;

src/rpc/rawtransaction_util.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#include <util/rbf.h>
2020
#include <util/strencodings.h>
2121

22-
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
22+
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf)
2323
{
2424
if (inputs_in.isNull() || outputs_in.isNull())
2525
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
@@ -37,8 +37,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
3737
rawTx.nLockTime = nLockTime;
3838
}
3939

40-
bool rbfOptIn = rbf.isTrue();
41-
4240
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
4341
const UniValue& input = inputs[idx];
4442
const UniValue& o = input.get_obj();
@@ -53,7 +51,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
5351
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
5452

5553
uint32_t nSequence;
56-
if (rbfOptIn) {
54+
if (rbf) {
5755
nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
5856
} else if (rawTx.nLockTime) {
5957
nSequence = CTxIn::SEQUENCE_FINAL - 1;
@@ -125,7 +123,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
125123
}
126124
}
127125

128-
if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
126+
if (rbf && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) {
129127
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
130128
}
131129

src/rpc/rawtransaction_util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ class COutPoint;
2727
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
2828

2929
/** Create a transaction from univalue parameters */
30-
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
30+
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf);
3131

3232
#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H

src/wallet/rpcwallet.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
359359
" transaction, just kept in your wallet."},
360360
{"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
361361
" The recipient will receive less bitcoins than you enter in the amount field."},
362-
{"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
363-
{"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
362+
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
363+
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
364364
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
365365
" \"UNSET\"\n"
366366
" \"ECONOMICAL\"\n"
@@ -815,8 +815,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
815815
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
816816
},
817817
},
818-
{"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
819-
{"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
818+
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
819+
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
820820
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
821821
" \"UNSET\"\n"
822822
" \"ECONOMICAL\"\n"
@@ -3121,9 +3121,9 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
31213121
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
31223122
},
31233123
},
3124-
{"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Marks this transaction as BIP125 replaceable.\n"
3124+
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
31253125
" Allows this transaction to be replaced by a transaction with higher fees"},
3126-
{"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
3126+
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
31273127
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
31283128
" \"UNSET\"\n"
31293129
" \"ECONOMICAL\"\n"
@@ -3286,7 +3286,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
32863286
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
32873287
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
32883288
{
3289-
{"confTarget", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
3289+
{"confTarget", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
32903290
{"totalFee", RPCArg::Type::NUM, /* default */ "fallback to 'confTarget'", "Total fee (NOT feerate) to pay, in satoshis. (DEPRECATED)\n"
32913291
" In rare cases, the actual fee paid might be slightly higher than the specified\n"
32923292
" totalFee if the tx change output has to be removed because it is too close to\n"
@@ -4066,7 +4066,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
40664066
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
40674067
},
40684068
},
4069-
{"replaceable", RPCArg::Type::BOOL, /* default */ "false", "Marks this transaction as BIP125 replaceable.\n"
4069+
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
40704070
" Allows this transaction to be replaced by a transaction with higher fees"},
40714071
{"conf_target", RPCArg::Type::NUM, /* default */ "Fallback to wallet's confirmation target", "Confirmation target (in blocks)"},
40724072
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
@@ -4101,7 +4101,13 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
41014101

41024102
CAmount fee;
41034103
int change_position;
4104-
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]["replaceable"]);
4104+
bool rbf = pwallet->m_signal_rbf;
4105+
const UniValue &replaceable_arg = request.params[3]["replaceable"];
4106+
if (!replaceable_arg.isNull()) {
4107+
RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
4108+
rbf = replaceable_arg.isTrue();
4109+
}
4110+
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
41054111
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
41064112

41074113
// Make a blank psbt

test/functional/rpc_psbt.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ class PSBTTest(BitcoinTestFramework):
2727
def set_test_params(self):
2828
self.setup_clean_chain = False
2929
self.num_nodes = 3
30+
self.extra_args = [
31+
["-walletrbf=1"],
32+
["-walletrbf=0"],
33+
[]
34+
]
3035

3136
def skip_test_if_missing_module(self):
3237
self.skip_if_no_wallet()
@@ -207,28 +212,35 @@ def run_test(self):
207212
# replaceable arg
208213
block_height = self.nodes[0].getblockcount()
209214
unspent = self.nodes[0].listunspent()[0]
210-
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable":True}, False)
215+
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable": False}, False)
211216
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
212217
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
213-
assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
218+
assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
214219
assert "bip32_derivs" not in psbt_in
215220
assert_equal(decoded_psbt["tx"]["locktime"], block_height+2)
216221

217-
# Same construction with only locktime set
218-
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {}, True)
222+
# Same construction with only locktime set and RBF explicitly enabled
223+
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {"replaceable": True}, True)
219224
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
220225
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
221-
assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
226+
assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
222227
assert "bip32_derivs" in psbt_in
223228
assert_equal(decoded_psbt["tx"]["locktime"], block_height)
224229

225230
# Same construction without optional arguments
226231
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])
227232
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
228233
for tx_in in decoded_psbt["tx"]["vin"]:
229-
assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
234+
assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
230235
assert_equal(decoded_psbt["tx"]["locktime"], 0)
231236

237+
# Same construction without optional arguments, for a node with -walletrbf=0
238+
unspent1 = self.nodes[1].listunspent()[0]
239+
psbtx_info = self.nodes[1].walletcreatefundedpsbt([{"txid":unspent1["txid"], "vout":unspent1["vout"]}], [{self.nodes[2].getnewaddress():unspent1["amount"]+1}], block_height)
240+
decoded_psbt = self.nodes[1].decodepsbt(psbtx_info["psbt"])
241+
for tx_in in decoded_psbt["tx"]["vin"]:
242+
assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
243+
232244
# Make sure change address wallet does not have P2SH innerscript access to results in success
233245
# when attempting BnB coin selection
234246
self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)

0 commit comments

Comments
 (0)