mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-09-05 13:06:08 +02:00
wallet: refactor eight consecutive 'AttemptSelection' calls into a loop
and remove 'CoinEligibilityFilter' default constructor to prevent mistakes.
This commit is contained in:
@@ -186,6 +186,7 @@ struct CoinEligibilityFilter
|
|||||||
/** When avoid_reuse=true and there are full groups (OUTPUT_GROUP_MAX_ENTRIES), whether or not to use any partial groups.*/
|
/** When avoid_reuse=true and there are full groups (OUTPUT_GROUP_MAX_ENTRIES), whether or not to use any partial groups.*/
|
||||||
const bool m_include_partial_groups{false};
|
const bool m_include_partial_groups{false};
|
||||||
|
|
||||||
|
CoinEligibilityFilter() = delete;
|
||||||
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_ancestors) {}
|
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_ancestors) {}
|
||||||
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants) {}
|
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants) {}
|
||||||
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants, bool include_partial) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants), m_include_partial_groups(include_partial) {}
|
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants, bool include_partial) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants), m_include_partial_groups(include_partial) {}
|
||||||
|
@@ -622,6 +622,11 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
|
|||||||
return op_selection_result;
|
return op_selection_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SelectionFilter {
|
||||||
|
CoinEligibilityFilter filter;
|
||||||
|
bool allow_mixed_output_types{true};
|
||||||
|
};
|
||||||
|
|
||||||
std::optional<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& value_to_select, const CCoinControl& coin_control, const CoinSelectionParams& coin_selection_params)
|
std::optional<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& value_to_select, const CCoinControl& coin_control, const CoinSelectionParams& coin_selection_params)
|
||||||
{
|
{
|
||||||
unsigned int limit_ancestor_count = 0;
|
unsigned int limit_ancestor_count = 0;
|
||||||
@@ -644,50 +649,42 @@ std::optional<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coi
|
|||||||
// transaction at a target feerate. If an attempt fails, more attempts may be made using a more
|
// transaction at a target feerate. If an attempt fails, more attempts may be made using a more
|
||||||
// permissive CoinEligibilityFilter.
|
// permissive CoinEligibilityFilter.
|
||||||
std::optional<SelectionResult> res = [&] {
|
std::optional<SelectionResult> res = [&] {
|
||||||
|
// Place coins eligibility filters on a scope increasing order.
|
||||||
|
std::vector<SelectionFilter> ordered_filters{
|
||||||
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
|
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
|
||||||
// confirmations on outputs received from other wallets and only spend confirmed change.
|
// confirmations on outputs received from other wallets and only spend confirmed change.
|
||||||
if (auto r1{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 6, 0), available_coins, coin_selection_params, /*allow_mixed_output_types=*/false)}) return r1;
|
{CoinEligibilityFilter(1, 6, 0), /*allow_mixed_output_types=*/false},
|
||||||
// Allow mixing only if no solution from any single output type can be found
|
{CoinEligibilityFilter(1, 1, 0)},
|
||||||
if (auto r2{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 1, 0), available_coins, coin_selection_params, /*allow_mixed_output_types=*/true)}) return r2;
|
};
|
||||||
|
|
||||||
// Fall back to using zero confirmation change (but with as few ancestors in the mempool as
|
// Fall back to using zero confirmation change (but with as few ancestors in the mempool as
|
||||||
// possible) if we cannot fund the transaction otherwise.
|
// possible) if we cannot fund the transaction otherwise.
|
||||||
if (wallet.m_spend_zero_conf_change) {
|
if (wallet.m_spend_zero_conf_change) {
|
||||||
if (auto r3{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, 2), available_coins, coin_selection_params, /*allow_mixed_output_types=*/true)}) return r3;
|
ordered_filters.push_back({CoinEligibilityFilter(0, 1, 2)});
|
||||||
if (auto r4{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
|
ordered_filters.push_back({CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3))});
|
||||||
available_coins, coin_selection_params, /*allow_mixed_output_types=*/true)}) {
|
ordered_filters.push_back({CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2)});
|
||||||
return r4;
|
|
||||||
}
|
|
||||||
if (auto r5{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
|
|
||||||
available_coins, coin_selection_params, /*allow_mixed_output_types=*/true)}) {
|
|
||||||
return r5;
|
|
||||||
}
|
|
||||||
// If partial groups are allowed, relax the requirement of spending OutputGroups (groups
|
// If partial groups are allowed, relax the requirement of spending OutputGroups (groups
|
||||||
// of UTXOs sent to the same address, which are obviously controlled by a single wallet)
|
// of UTXOs sent to the same address, which are obviously controlled by a single wallet)
|
||||||
// in their entirety.
|
// in their entirety.
|
||||||
if (auto r6{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, /*include_partial=*/true),
|
ordered_filters.push_back({CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, /*include_partial=*/true)});
|
||||||
available_coins, coin_selection_params, /*allow_mixed_output_types=*/true)}) {
|
|
||||||
return r6;
|
|
||||||
}
|
|
||||||
// Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs
|
// Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs
|
||||||
// received from other wallets.
|
// received from other wallets.
|
||||||
if (coin_control.m_include_unsafe_inputs) {
|
if (coin_control.m_include_unsafe_inputs) {
|
||||||
if (auto r7{AttemptSelection(wallet, value_to_select,
|
ordered_filters.push_back({CoinEligibilityFilter(/*conf_mine=*/0, /*conf_theirs*/0, max_ancestors-1, max_descendants-1, /*include_partial=*/true)});
|
||||||
CoinEligibilityFilter(/*conf_mine=*/0, /*conf_theirs=*/0, max_ancestors-1, max_descendants-1, /*include_partial=*/true),
|
|
||||||
available_coins, coin_selection_params, /*allow_mixed_output_types=*/true)}) {
|
|
||||||
return r7;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Try with unlimited ancestors/descendants. The transaction will still need to meet
|
// Try with unlimited ancestors/descendants. The transaction will still need to meet
|
||||||
// mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but
|
// mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but
|
||||||
// OutputGroups use heuristics that may overestimate ancestor/descendant counts.
|
// OutputGroups use heuristics that may overestimate ancestor/descendant counts.
|
||||||
if (!fRejectLongChains) {
|
if (!fRejectLongChains) {
|
||||||
if (auto r8{AttemptSelection(wallet, value_to_select,
|
ordered_filters.push_back({CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(),
|
||||||
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), /*include_partial=*/true),
|
std::numeric_limits<uint64_t>::max(),
|
||||||
available_coins, coin_selection_params, /*allow_mixed_output_types=*/true)}) {
|
/*include_partial=*/true)});
|
||||||
return r8;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Walk-through the filters until the solution gets found
|
||||||
|
for (const auto& select_filter : ordered_filters) {
|
||||||
|
if (auto res{AttemptSelection(wallet, value_to_select, select_filter.filter, available_coins,
|
||||||
|
coin_selection_params, select_filter.allow_mixed_output_types)}) return res;
|
||||||
}
|
}
|
||||||
// Coin Selection failed.
|
// Coin Selection failed.
|
||||||
return std::optional<SelectionResult>();
|
return std::optional<SelectionResult>();
|
||||||
|
Reference in New Issue
Block a user