diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h index dbe01a08fe8..5a7b748be15 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -153,6 +153,11 @@ struct CoinSelectionParams { * associated with the same address. This helps reduce privacy leaks resulting from address * reuse. Dust outputs are not eligible to be added to output groups and thus not considered. */ bool m_avoid_partial_spends = false; + /** + * When true, allow unsafe coins to be selected during Coin Selection. This may spend unconfirmed outputs: + * 1) Received from other wallets, 2) replacing other txs, 3) that have been replaced. + */ + bool m_include_unsafe_inputs = false; CoinSelectionParams(FastRandomContext& rng_fast, size_t change_output_size, size_t change_spend_size, CAmount min_change_target, CFeeRate effective_feerate, diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index 359fc78df17..5771d33b7a8 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -617,7 +617,7 @@ util::Result SelectCoins(const CWallet& wallet, CoinsResult& av } // Start wallet Coin Selection procedure - auto op_selection_result = AutomaticCoinSelection(wallet, available_coins, selection_target, coin_control, coin_selection_params); + auto op_selection_result = AutomaticCoinSelection(wallet, available_coins, selection_target, coin_selection_params); if (!op_selection_result) return op_selection_result; // If needed, add preset inputs to the automatic coin selection result @@ -632,7 +632,7 @@ util::Result SelectCoins(const CWallet& wallet, CoinsResult& av return op_selection_result; } -util::Result AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& value_to_select, const CCoinControl& coin_control, const CoinSelectionParams& coin_selection_params) +util::Result AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& value_to_select, const CoinSelectionParams& coin_selection_params) { unsigned int limit_ancestor_count = 0; unsigned int limit_descendant_count = 0; @@ -641,12 +641,10 @@ util::Result AutomaticCoinSelection(const CWallet& wallet, Coin const size_t max_descendants = (size_t)std::max(1, limit_descendant_count); const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); - // form groups from remaining coins; note that preset coins will not - // automatically have their associated (same address) coins included - if (coin_control.m_avoid_partial_spends && available_coins.Size() > OUTPUT_GROUP_MAX_ENTRIES) { - // Cases where we have 101+ outputs all pointing to the same destination may result in - // privacy leaks as they will potentially be deterministically sorted. We solve that by - // explicitly shuffling the outputs before processing + // Cases where we have 101+ outputs all pointing to the same destination may result in + // privacy leaks as they will potentially be deterministically sorted. We solve that by + // explicitly shuffling the outputs before processing + if (coin_selection_params.m_avoid_partial_spends && available_coins.Size() > OUTPUT_GROUP_MAX_ENTRIES) { available_coins.Shuffle(coin_selection_params.rng_fast); } @@ -673,7 +671,7 @@ util::Result AutomaticCoinSelection(const CWallet& wallet, Coin ordered_filters.push_back({CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, /*include_partial=*/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) { + if (coin_selection_params.m_include_unsafe_inputs) { ordered_filters.push_back({CoinEligibilityFilter(/*conf_mine=*/0, /*conf_theirs*/0, max_ancestors-1, max_descendants-1, /*include_partial=*/true)}); } // Try with unlimited ancestors/descendants. The transaction will still need to meet @@ -804,6 +802,7 @@ static util::Result CreateTransactionInternal( CoinSelectionParams coin_selection_params{rng_fast}; // Parameters for coin selection, init with dummy coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends; + coin_selection_params.m_include_unsafe_inputs = coin_control.m_include_unsafe_inputs; // Set the long term feerate estimate to the wallet's consolidate feerate coin_selection_params.m_long_term_feerate = wallet.m_consolidate_feerate; diff --git a/src/wallet/spend.h b/src/wallet/spend.h index b8bc82db7aa..78c2c5f22b9 100644 --- a/src/wallet/spend.h +++ b/src/wallet/spend.h @@ -191,7 +191,7 @@ util::Result FetchSelectedInputs(const CWallet& wallet, const * or (2) an specific error message if there was something particularly wrong (e.g. a selection * result that surpassed the tx max weight size). */ -util::Result AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& nTargetValue, const CCoinControl& coin_control, +util::Result AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& nTargetValue, const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); /**