diff --git a/doc/release-notes-25610.md b/doc/release-notes-25610.md new file mode 100644 index 0000000000..743a7709bf --- /dev/null +++ b/doc/release-notes-25610.md @@ -0,0 +1,12 @@ +Wallet +------ + +- The `-walletrbf` startup option will now default to `true`. The + wallet will now default to opt-in RBF on transactions that it creates. + +Updated RPCs +------------ + +- The `replaceable` option for the `createrawtransaction` and + `createpsbt` RPCs will now default to `true`. Transactions created + with these RPCs will default to having opt-in RBF enabled. diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index c62d61b7fd..8990b76a65 100755 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -212,8 +212,8 @@ static std::vector CreateTxDoc() }, }, {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"}, - {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{false}, "Marks this transaction as BIP125-replaceable.\n" -" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, + {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{true}, "Marks this transaction as BIP125-replaceable.\n" + "Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, }; } @@ -361,7 +361,7 @@ static RPCHelpMan createrawtransaction() }, true ); - bool rbf = false; + std::optional rbf; if (!request.params[3].isNull()) { rbf = request.params[3].isTrue(); } @@ -1889,7 +1889,7 @@ static RPCHelpMan createpsbt() }, true ); - bool rbf = false; + std::optional rbf; if (!request.params[3].isNull()) { rbf = request.params[3].isTrue(); } diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index a59ecf2531..4d20cbcdc7 100755 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -145,7 +145,7 @@ void CreatePegInInput(CMutableTransaction& mtx, uint32_t input_idx, Sidechain::B CreatePegInInputInner(mtx, input_idx, tx_btc, merkle_block, claim_scripts, txData, txOutProofData, active_chain_tip); } -CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf, const CBlockIndex* active_chain_tip, std::map* outputs_aux, bool allow_peg_in, bool allow_issuance) +CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional rbf, const CBlockIndex* active_chain_tip, std::map* outputs_aux, bool allow_peg_in, bool allow_issuance) { if (outputs_in.isNull()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null"); @@ -183,7 +183,8 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative"); uint32_t nSequence; - if (rbf) { + + if (rbf.value_or(true)) { nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */ } else if (rawTx.nLockTime) { nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; /* CTxIn::SEQUENCE_FINAL - 1 */ @@ -383,7 +384,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal } } - if (rbf && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) { + if (rbf.has_value() && rbf.value() && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); } diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h index 6776472b53..1635450495 100644 --- a/src/rpc/rawtransaction_util.h +++ b/src/rpc/rawtransaction_util.h @@ -6,6 +6,7 @@ #define BITCOIN_RPC_RAWTRANSACTION_UTIL_H #include +#include #include #include @@ -49,7 +50,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst /** Create a transaction from univalue parameters. If (and only if) output_pubkeys_out is null, the "nonce hack" of storing Confidential Assets output pubkeys in nonces will be used. */ -CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf, const CBlockIndex* active_chain_tip, std::map* outputs_aux = nullptr, bool allow_peg_in = true, bool allow_issuance = true); +CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional rbf, const CBlockIndex* active_chain_tip, std::map* outputs_aux = nullptr, bool allow_peg_in = true, bool allow_issuance = true); /** Create a peg-in input */ void CreatePegInInput(CMutableTransaction& mtx, uint32_t input_idx, CTransactionRef& tx_btc, CMerkleBlock& merkle_block, const std::set& claim_scripts, const std::vector& txData, const std::vector& txOutProofData, const CBlockIndex* active_chain_tip); diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py index e173f037b4..9cb85173bd 100755 --- a/test/functional/p2p_permissions.py +++ b/test/functional/p2p_permissions.py @@ -113,7 +113,8 @@ class P2PPermissionsTests(BitcoinTestFramework): ADDRESS_BCRT1_P2WSH_OP_TRUE: 5, }, { "fee": 45, - }]), + }], + replaceable=False), ) tx.wit.vtxinwit = [CTxInWitness()] tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index fc39e7c844..416c6f9262 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -624,7 +624,7 @@ class PSBTTest(BitcoinTestFramework): # Creator Tests for creator in creators: - created_tx = self.nodes[0].createpsbt(inputs=creator['inputs'], outputs=creator['outputs'], psbt_version=creator['version']) + created_tx = self.nodes[0].createpsbt(inputs=creator['inputs'], outputs=creator['outputs'], psbt_version=creator['version'], replaceable=False) assert_equal(created_tx, creator['result']) # Signer tests diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index 0c67defda1..50dc8b0dc2 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -25,7 +25,7 @@ class ListTransactionsTest(BitcoinTestFramework): self.num_nodes = 3 # This test isn't testing txn relay/timing, so set whitelist on the # peers for instant txn relay. This speeds up the test run time 2-3x. - self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes + self.extra_args = [["-whitelist=noban@127.0.0.1", "-walletrbf=0"]] * self.num_nodes def skip_test_if_missing_module(self): self.skip_if_no_wallet() @@ -146,7 +146,7 @@ class ListTransactionsTest(BitcoinTestFramework): # Create tx2 using createrawtransaction inputs = [{"txid": utxo_to_use["txid"], "vout": utxo_to_use["vout"]}] outputs = [{self.nodes[0].getnewaddress(): 0.999}, {"fee": utxo_to_use["amount"] - Decimal('0.999')}] - tx2 = self.nodes[1].createrawtransaction(inputs, outputs) + tx2 = self.nodes[1].createrawtransaction(inputs=inputs, outputs=outputs, replaceable=False) tx2_signed = self.nodes[1].signrawtransactionwithwallet(tx2)["hex"] txid_2 = self.nodes[1].sendrawtransaction(tx2_signed) @@ -178,7 +178,7 @@ class ListTransactionsTest(BitcoinTestFramework): utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_3) inputs = [{"txid": txid_3, "vout": utxo_to_use["vout"]}] outputs = [{self.nodes[0].getnewaddress(): 0.997}, {"fee": utxo_to_use["amount"] - Decimal('0.997')}] - tx4 = self.nodes[1].createrawtransaction(inputs, outputs) + tx4 = self.nodes[1].createrawtransaction(inputs=inputs, outputs=outputs, replaceable=False) tx4_signed = self.nodes[1].signrawtransactionwithwallet(tx4)["hex"] txid_4 = self.nodes[1].sendrawtransaction(tx4_signed)