Skip to content

Commit 6ba8921

Browse files
glozowmurchandamus
andcommitted
refactor + document coin selection strategy
Co-authored-by: Xekyo <[email protected]>
1 parent 58ea324 commit 6ba8921

File tree

1 file changed

+50
-13
lines changed

1 file changed

+50
-13
lines changed

src/wallet/wallet.cpp

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,7 +2478,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
24782478
}
24792479
}
24802480

2481-
// remove preset inputs from vCoins
2481+
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
24822482
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();)
24832483
{
24842484
if (setPresetCoins.count(it->GetInputCoin()))
@@ -2490,9 +2490,9 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
24902490
unsigned int limit_ancestor_count = 0;
24912491
unsigned int limit_descendant_count = 0;
24922492
chain().getPackageLimits(limit_ancestor_count, limit_descendant_count);
2493-
size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count);
2494-
size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count);
2495-
bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
2493+
const size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count);
2494+
const size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count);
2495+
const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
24962496

24972497
// form groups from remaining coins; note that preset coins will not
24982498
// automatically have their associated (same address) coins included
@@ -2502,16 +2502,53 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
25022502
// explicitly shuffling the outputs before processing
25032503
Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext());
25042504
}
2505-
bool res = value_to_select <= 0 ||
2506-
SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
2507-
SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
2508-
(m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
2509-
(m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
2510-
(m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
2511-
(m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
2512-
(m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
25132505

2514-
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
2506+
// Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
2507+
// transaction at a target feerate. If an attempt fails, more attempts may be made using a more
2508+
// permissive CoinEligibilityFilter.
2509+
const bool res = [&] {
2510+
// Pre-selected inputs already cover the target amount.
2511+
if (value_to_select <= 0) return true;
2512+
2513+
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
2514+
// confirmations on outputs received from other wallets and only spend confirmed change.
2515+
if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) return true;
2516+
if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) return true;
2517+
2518+
// Fall back to using zero confirmation change (but with as few ancestors in the mempool as
2519+
// possible) if we cannot fund the transaction otherwise. We never spend unconfirmed
2520+
// outputs received from other wallets.
2521+
if (m_spend_zero_conf_change) {
2522+
if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) return true;
2523+
if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
2524+
vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) {
2525+
return true;
2526+
}
2527+
if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
2528+
vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) {
2529+
return true;
2530+
}
2531+
// If partial groups are allowed, relax the requirement of spending OutputGroups (groups
2532+
// of UTXOs sent to the same address, which are obviously controlled by a single wallet)
2533+
// in their entirety.
2534+
if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
2535+
vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) {
2536+
return true;
2537+
}
2538+
// Try with unlimited ancestors/descendants. The transaction will still need to meet
2539+
// mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but
2540+
// OutputGroups use heuristics that may overestimate ancestor/descendant counts.
2541+
if (!fRejectLongChains && SelectCoinsMinConf(value_to_select,
2542+
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */),
2543+
vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) {
2544+
return true;
2545+
}
2546+
}
2547+
// Coin Selection failed.
2548+
return false;
2549+
}();
2550+
2551+
// SelectCoinsMinConf clears setCoinsRet, so add the preset inputs from coin_control to the coinset
25152552
util::insert(setCoinsRet, setPresetCoins);
25162553

25172554
// add preset inputs to the total value selected

0 commit comments

Comments
 (0)