diff options
author | t-bast <bastuc@hotmail.fr> | 2021-03-10 15:37:18 +0100 |
---|---|---|
committer | t-bast <bastuc@hotmail.fr> | 2021-04-30 18:53:47 +0200 |
commit | 11d6459b6e101f05f36e13799c400bef82d2fc21 (patch) | |
tree | e467b32e008eca8e9978766ab1d783059de3f35f /src/wallet | |
parent | 480bf01c295527bd212964efe4df3bb886db5654 (diff) |
rpc: include_unsafe option for fundrawtransaction
Allow RPC users to opt-in to unsafe inputs when funding a raw transaction.
Applications that need to manage a complex RBF flow (such as lightning
nodes using anchor outputs) are very limited if they can only use safe inputs.
Fixes #21299
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/coincontrol.h | 2 | ||||
-rw-r--r-- | src/wallet/coinselection.h | 3 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 14 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 13 |
4 files changed, 27 insertions, 5 deletions
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index 716e1922fe..85cbec76b7 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -29,6 +29,8 @@ public: std::optional<OutputType> m_change_type; //! If false, only selected inputs are used bool m_add_inputs = true; + //! If false, only safe inputs will be used + bool m_include_unsafe_inputs = false; //! If false, allows unselected inputs, but requires all selected inputs be used bool fAllowOtherInputs = false; //! Includes watch only addresses which are solvable diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h index 5c1b36be6e..5645e6db46 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -65,8 +65,7 @@ struct CoinEligibilityFilter /** Minimum number of confirmations for outputs that we sent to ourselves. * We may use unconfirmed UTXOs sent from ourselves, e.g. change outputs. */ const int conf_mine; - /** Minimum number of confirmations for outputs received from a different - * wallet. We never spend unconfirmed foreign outputs as we cannot rely on these funds yet. */ + /** Minimum number of confirmations for outputs received from a different wallet. */ const int conf_theirs; /** Maximum number of unconfirmed ancestors aggregated across all UTXOs in an OutputGroup. */ const uint64_t max_ancestors; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 67d9d56133..49505faed3 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3075,6 +3075,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, RPCTypeCheckObj(options, { {"add_inputs", UniValueType(UniValue::VBOOL)}, + {"include_unsafe", UniValueType(UniValue::VBOOL)}, {"add_to_wallet", UniValueType(UniValue::VBOOL)}, {"changeAddress", UniValueType(UniValue::VSTR)}, {"change_address", UniValueType(UniValue::VSTR)}, @@ -3135,6 +3136,10 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool(); } + if (options.exists("include_unsafe")) { + coinControl.m_include_unsafe_inputs = options["include_unsafe"].get_bool(); + } + if (options.exists("feeRate")) { if (options.exists("fee_rate")) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both fee_rate (" + CURRENCY_ATOM + "/vB) and feeRate (" + CURRENCY_UNIT + "/kvB)"); @@ -3205,6 +3210,9 @@ static RPCHelpMan fundrawtransaction() {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}", { {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{true}, "For a transaction with existing inputs, automatically include more if they are not enough."}, + {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n" + "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n" + "If that happens, you will need to fund the transaction with different inputs and republish it."}, {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"}, {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"}, {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, @@ -4030,6 +4038,9 @@ static RPCHelpMan send() {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "", { {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."}, + {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n" + "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n" + "If that happens, you will need to fund the transaction with different inputs and republish it."}, {"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns a serialized transaction which will not be added to the wallet or broadcast"}, {"change_address", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"}, {"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"}, @@ -4373,6 +4384,9 @@ static RPCHelpMan walletcreatefundedpsbt() {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "", { {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."}, + {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n" + "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n" + "If that happens, you will need to fund the transaction with different inputs and republish it."}, {"changeAddress", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"}, {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"}, {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f0aaee7e4e..d6c4f71f91 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2516,8 +2516,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) return true; // Fall back to using zero confirmation change (but with as few ancestors in the mempool as - // possible) if we cannot fund the transaction otherwise. We never spend unconfirmed - // outputs received from other wallets. + // possible) if we cannot fund the transaction otherwise. if (m_spend_zero_conf_change) { if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) return true; if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), @@ -2535,6 +2534,14 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) { return true; } + // Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs + // received from other wallets. + if (coin_control.m_include_unsafe_inputs + && SelectCoinsMinConf(value_to_select, + CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), + vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) { + return true; + } // Try with unlimited ancestors/descendants. The transaction will still need to meet // mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but // OutputGroups use heuristics that may overestimate ancestor/descendant counts. @@ -2836,7 +2843,7 @@ bool CWallet::CreateTransactionInternal( txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight()); { std::vector<COutput> vAvailableCoins; - AvailableCoins(vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0); + AvailableCoins(vAvailableCoins, !coin_control.m_include_unsafe_inputs, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0); CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends; |