Merge bitcoin/bitcoin#36494: wallet: reject unknown options in sendall
What changed, and why it matters
This change fixes a bug in Bitcoin Core's `sendall` wallet command where misspelled or unknown options in the options object were silently ignored. For example, typing `input` instead of `inputs` would spend all coins instead of only the chosen ones, and misspelling `add_to_wallet` or `psbt` could broadcast a transaction the user did not want broadcast. The fix makes `sendall` reject unknown or wrongly typed options, matching other wallet commands.
Upgrade to a release containing this commit. Until then, users and applications calling `sendall` should carefully verify option spelling and types, especially `inputs`, `add_to_wallet`, and `psbt`, to avoid unintended broadcasts or full-wallet spends.
Security signals we found
Silent ignore of misspelled options can lead to unintended full-wallet sweep (`input` vs `inputs`)
Silent ignore of misspelled `add_to_wallet` or `psbt` can cause unwanted transaction broadcast
Fix aligns `sendall` with existing strict option validation in `send`, `fundrawtransaction`, `walletcreatefundedpsbt`, and `bumpfee`
Validation now occurs before coin selection, preventing side effects from malformed requests
Evidence from the diff
The patch adds RPCTypeCheckObj with fStrict=true to the sendall RPC handler in src/wallet/rpc/spend.cpp, explicitly enumerating allowed option keys and their expected UniValue types. Previously, sendall only called PreventOutdatedOptions and InterpretFeeEstimationInstructions, so any extra or misspelled keys were ignored. The new strict check causes the RPC to return a JSONRPCError (-3) for unknown keys or type mismatches before coin selection or transaction creation. A functional test is added verifying rejection of add_to_wllt and string-typed add_to_wallet.
Changed components
src/wallet/rpc/spend.cppwallet RPC command `sendall`test/functional/wallet_sendall.pyInspect captured patch +37 / −1
### doc/release-notes-36494.md
@@ -0,0 +1,7 @@
+Wallet
+------
+
+- `sendall` now rejects unknown keys in its `options` object instead of
+ silently ignoring them, like `send`, `fundrawtransaction` and
+ `walletcreatefundedpsbt` already do. Option values are also type-checked
+ before any coin is selected.
### src/wallet/rpc/spend.cpp
@@ -1387,8 +1387,26 @@ RPCMethod sendall()
pwallet->BlockUntilSyncedToCurrentChain();
UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
+ RPCTypeCheckObj(options,
+ {
+ {"add_to_wallet", UniValueType(UniValue::VBOOL)},
+ {"fee_rate", UniValueType()},
+ {"include_watching", UniValueType(UniValue::VBOOL)},
+ {"inputs", UniValueType(UniValue::VARR)},
+ {"locktime", UniValueType(UniValue::VNUM)},
+ {"lock_unspents", UniValueType(UniValue::VBOOL)},
+ {"psbt", UniValueType(UniValue::VBOOL)},
+ {"send_max", UniValueType(UniValue::VBOOL)},
+ {"minconf", UniValueType(UniValue::VNUM)},
+ {"maxconf", UniValueType(UniValue::VNUM)},
+ {"version", UniValueType(UniValue::VNUM)},
+ {"conf_target", UniValueType(UniValue::VNUM)},
+ {"estimate_mode", UniValueType(UniValue::VSTR)},
+ {"replaceable", UniValueType(UniValue::VBOOL)},
+ {"solving_data", UniValueType(UniValue::VOBJ)},
+ },
+ /*fAllowNull=*/true, /*fStrict=*/true);
InterpretFeeEstimationInstructions(/*conf_target=*/request.params[1], /*estimate_mode=*/request.params[2], /*fee_rate=*/request.params[3], options);
- PreventOutdatedOptions(options);
std::set<CTxDestination> addresses_without_amount;
### test/functional/wallet_sendall.py
@@ -298,6 +298,14 @@ def sendall_fails_on_specific_inputs_with_send_max(self):
recipients=[self.remainder_target],
inputs=[utxo], send_max=True)
+ @cleanup
+ def sendall_fails_on_invalid_options(self):
+ self.log.info("Test sendall fails on unknown options or options of the wrong type")
+ assert_raises_rpc_error(-3, "Unexpected key add_to_wllt",
+ self.wallet.sendall, recipients=[self.remainder_target], options={"add_to_wllt": "false"})
+ assert_raises_rpc_error(-3, "JSON value of type string for field add_to_wallet is not of expected type bool",
+ self.wallet.sendall, recipients=[self.remainder_target], options={"add_to_wallet": "false"})
+
@cleanup
def sendall_fails_on_high_fee(self):
self.log.info("Test sendall fails if the transaction fee exceeds the maxtxfee")
@@ -591,6 +599,9 @@ def run_test(self):
# Sendall fails when using send_max while specifying inputs
self.sendall_fails_on_specific_inputs_with_send_max()
+ # Sendall fails on unknown options or options of the wrong type
+ self.sendall_fails_on_invalid_options()
+
# Sendall fails when providing a fee that is too high
self.sendall_fails_on_high_fee()
Why this scored 64/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.