wallet: mark `bip125-replaceable` key as deprecated in transaction RPCs
What changed, and why it matters
This is a routine Bitcoin Core change that hides an old transaction status field called 'bip125-replaceable' from wallet RPC responses by default, marking it as deprecated. Users can still get it back by starting the node with -deprecatedrpc=bip125. It is not a security fix and does not introduce a vulnerability; it is a user-interface/API cleanup following earlier changes that made transactions replaceable by default.
No security action required. Developers and operators relying on the 'bip125-replaceable' field in listtransactions/listsinceblock/gettransaction should plan migration, and may use -deprecatedrpc=bip125 during the transition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit wraps the emission of the ‘bip125-replaceable’ key in WalletTxToJSON() behind chain.rpcEnableDeprecated(‘bip125’). The RPC help text is updated to mark the field optional and deprecated. Functional tests that assert on this key are updated to pass -deprecatedrpc=bip125. This aligns wallet transaction RPCs with the mempool RPC deprecation done in v29. No cryptographic, consensus, networking, or wallet-seed logic is modified.
Changed components
src/wallet/rpc/transactions.cpptest/functional/wallet_basic.pytest/functional/wallet_listtransactions.pytest/functional/wallet_migration.pytest/functional/wallet_send.pyInspect captured patch +15 / −16
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index 038e30fc..f69082e1 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -50,15 +50,17 @@ static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue
entry.pushKV("timereceived", wtx.nTimeReceived);
// Add opt-in RBF status
- std::string rbfStatus = "no";
- if (confirms <= 0) {
- RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.tx);
- if (rbfState == RBFTransactionState::UNKNOWN)
- rbfStatus = "unknown";
- else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
- rbfStatus = "yes";
+ if (chain.rpcEnableDeprecated("bip125")) {
+ std::string rbfStatus = "no";
+ if (confirms <= 0) {
+ RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.tx);
+ if (rbfState == RBFTransactionState::UNKNOWN)
+ rbfStatus = "unknown";
+ else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
+ rbfStatus = "yes";
+ }
+ entry.pushKV("bip125-replaceable", rbfStatus);
}
- entry.pushKV("bip125-replaceable", rbfStatus);
for (const std::pair<const std::string, std::string>& item : wtx.mapValue)
entry.pushKV(item.first, item.second);
@@ -405,7 +407,7 @@ static std::vector<RPCResult> TransactionDescriptionString()
{RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR, "comment", /*optional=*/true, "If a comment is associated with the transaction, only present if not empty."},
- {RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability.\n"
+ {RPCResult::Type::STR, "bip125-replaceable", /*optional=*/true, "(\"yes|no|unknown\") (DEPRECATED) Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability.\n"
"May be unknown for unconfirmed transactions not in the mempool because their unconfirmed ancestors are unknown."},
{RPCResult::Type::ARR, "parent_descs", /*optional=*/true, "Only if 'category' is 'received'. List of parent descriptors for the output script of this coin.", {
{RPCResult::Type::STR, "desc", "The descriptor string."},
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index e7a333ba..11f27ed7 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -510,7 +510,7 @@ class WalletTest(BitcoinTestFramework):
# Try with walletrejectlongchains
# Double chain limit but require combining inputs, so we pass AttemptSelection
self.stop_node(0)
- extra_args = ["-walletrejectlongchains", "-limitclustercount=" + str(2 * chainlimit), "-limitancestorcount=" + str(2*chainlimit)]
+ extra_args = ["-deprecatedrpc=bip125", "-walletrejectlongchains", "-limitclustercount=" + str(2 * chainlimit), "-limitancestorcount=" + str(2*chainlimit)]
self.start_node(0, extra_args=extra_args)
# wait until the wallet has submitted all transactions to the mempool
diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py
index 714e6594..93e8a45b 100755
--- a/test/functional/wallet_listtransactions.py
+++ b/test/functional/wallet_listtransactions.py
@@ -31,7 +31,7 @@ class ListTransactionsTest(BitcoinTestFramework):
self.num_nodes = 3
# whitelist peers to speed up tx relay / mempool sync
self.noban_tx_relay = True
- self.extra_args = [["-walletrbf=0"]] * self.num_nodes
+ self.extra_args = [["-walletrbf=0", "-deprecatedrpc=bip125"]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py
index e28cfd57..42b2a47b 100755
--- a/test/functional/wallet_migration.py
+++ b/test/functional/wallet_migration.py
@@ -44,7 +44,7 @@ class WalletMigrationTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 2
self.supports_cli = False
- self.extra_args = [[], ["-deprecatedrpc=create_bdb"]]
+ self.extra_args = [["-deprecatedrpc=bip125"], ["-deprecatedrpc=create_bdb"]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index ba986990..5011deea 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -31,10 +31,7 @@ class WalletSendTest(BitcoinTestFramework):
# whitelist peers to speed up tx relay / mempool sync
self.noban_tx_relay = True
self.supports_cli = False
- self.extra_args = [
- ["-walletrbf=1", "-datacarriersize=16"],
- ["-walletrbf=1", "-datacarriersize=16"]
- ]
+ self.extra_args = [["-walletrbf=1", "-datacarriersize=16", "-deprecatedrpc=bip125"]] * self.num_nodes
getcontext().prec = 8 # Satoshi precision for Decimal
def skip_test_if_missing_module(self):
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.