Skip to content

Commit 9d9b101

Browse files
committed
Use SelectionResult in AttemptSelection
Replace setCoinsRet and nValueRet with a SelectionResult in AttemptSelection
1 parent bb50850 commit 9d9b101

File tree

3 files changed

+53
-54
lines changed

3 files changed

+53
-54
lines changed

src/bench/coin_selection.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,10 @@ static void CoinSelection(benchmark::Bench& bench)
5454
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
5555
/* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
5656
bench.run([&] {
57-
std::set<CInputCoin> setCoinsRet;
58-
CAmount nValueRet;
59-
bool success = AttemptSelection(wallet, 1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
60-
assert(success);
61-
assert(nValueRet == 1003 * COIN);
62-
assert(setCoinsRet.size() == 2);
57+
auto result = AttemptSelection(wallet, 1003 * COIN, filter_standard, coins, coin_selection_params);
58+
assert(result);
59+
assert(result->GetSelectedValue() == 1003 * COIN);
60+
assert(result->GetInputSet().size() == 2);
6361
});
6462
}
6563

src/wallet/spend.cpp

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,9 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
373373
return groups_out;
374374
}
375375

376-
bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
377-
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params)
376+
std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
377+
const CoinSelectionParams& coin_selection_params)
378378
{
379-
setCoinsRet.clear();
380-
nValueRet = 0;
381379
// Vector of results. We will choose the best one based on waste.
382380
std::vector<SelectionResult> results;
383381

@@ -407,15 +405,13 @@ bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const
407405

408406
if (results.size() == 0) {
409407
// No solution found
410-
return false;
408+
return std::nullopt;
411409
}
412410

413411
// Choose the result with the least waste
414412
// If the waste is the same, choose the one which spends more inputs.
415413
auto& best_result = *std::min_element(results.begin(), results.end());
416-
setCoinsRet = best_result.GetInputSet();
417-
nValueRet = best_result.GetSelectedValue();
418-
return true;
414+
return best_result;
419415
}
420416

421417
bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params)
@@ -438,7 +434,6 @@ bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCo
438434

439435
// calculate value from preset inputs and store them
440436
std::set<CInputCoin> setPresetCoins;
441-
CAmount nValueFromPresetInputs = 0;
442437
OutputGroup preset_inputs(coin_selection_params);
443438

444439
std::vector<COutPoint> vPresetInputs;
@@ -466,7 +461,6 @@ bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCo
466461
}
467462

468463
CInputCoin coin(outpoint, txout, input_bytes);
469-
nValueFromPresetInputs += coin.txout.nValue;
470464
if (coin.m_input_bytes == -1) {
471465
return false; // Not solvable, can't estimate size for fee
472466
}
@@ -511,62 +505,67 @@ bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCo
511505
// Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
512506
// transaction at a target feerate. If an attempt fails, more attempts may be made using a more
513507
// permissive CoinEligibilityFilter.
514-
const bool res = [&] {
508+
std::optional<SelectionResult> res = [&] {
515509
// Pre-selected inputs already cover the target amount.
516-
if (value_to_select <= 0) return true;
510+
if (value_to_select <= 0) return std::make_optional(SelectionResult(nTargetValue));
517511

518512
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
519513
// confirmations on outputs received from other wallets and only spend confirmed change.
520-
if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
521-
if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
514+
if (auto r1{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, coin_selection_params)}) return r1;
515+
if (auto r2{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, coin_selection_params)}) return r2;
522516

523517
// Fall back to using zero confirmation change (but with as few ancestors in the mempool as
524518
// possible) if we cannot fund the transaction otherwise.
525519
if (wallet.m_spend_zero_conf_change) {
526-
if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
527-
if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
528-
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
529-
return true;
520+
if (auto r3{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, coin_selection_params)}) return r3;
521+
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)),
522+
vCoins, coin_selection_params)}) {
523+
return r4;
530524
}
531-
if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
532-
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
533-
return true;
525+
if (auto r5{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
526+
vCoins, coin_selection_params)}) {
527+
return r5;
534528
}
535529
// If partial groups are allowed, relax the requirement of spending OutputGroups (groups
536530
// of UTXOs sent to the same address, which are obviously controlled by a single wallet)
537531
// in their entirety.
538-
if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
539-
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
540-
return true;
532+
if (auto r6{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
533+
vCoins, coin_selection_params)}) {
534+
return r6;
541535
}
542536
// Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs
543537
// received from other wallets.
544-
if (coin_control.m_include_unsafe_inputs
545-
&& AttemptSelection(wallet, value_to_select,
538+
if (coin_control.m_include_unsafe_inputs) {
539+
if (auto r7{AttemptSelection(wallet, value_to_select,
546540
CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
547-
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
548-
return true;
541+
vCoins, coin_selection_params)}) {
542+
return r7;
543+
}
549544
}
550545
// Try with unlimited ancestors/descendants. The transaction will still need to meet
551546
// mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but
552547
// OutputGroups use heuristics that may overestimate ancestor/descendant counts.
553-
if (!fRejectLongChains && AttemptSelection(wallet, value_to_select,
548+
if (!fRejectLongChains) {
549+
if (auto r8{AttemptSelection(wallet, value_to_select,
554550
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */),
555-
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
556-
return true;
551+
vCoins, coin_selection_params)}) {
552+
return r8;
553+
}
557554
}
558555
}
559556
// Coin Selection failed.
560-
return false;
557+
return std::optional<SelectionResult>();
561558
}();
562559

563-
// AttemptSelection clears setCoinsRet, so add the preset inputs from coin_control to the coinset
564-
util::insert(setCoinsRet, setPresetCoins);
560+
if (!res) return false;
565561

566-
// add preset inputs to the total value selected
567-
nValueRet += nValueFromPresetInputs;
562+
// Add preset inputs to result
563+
res->AddInput(preset_inputs);
568564

569-
return res;
565+
setCoinsRet = res->GetInputSet();
566+
nValueRet = res->GetSelectedValue();
567+
568+
return true;
570569
}
571570

572571
static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256& block_hash)

src/wallet/spend.h

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,20 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
101101
std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only);
102102

103103
/**
104-
* Shuffle and select coins until nTargetValue is reached while avoiding
105-
* small change; This method is stochastic for some inputs and upon
106-
* completion the coin set and corresponding actual target value is
107-
* assembled
108-
* param@[in] coins Set of UTXOs to consider. These will be categorized into
109-
* OutputGroups and filtered using eligibility_filter before
110-
* selecting coins.
111-
* param@[out] setCoinsRet Populated with the coins selected if successful.
112-
* param@[out] nValueRet Used to return the total value of selected coins.
104+
* Attempt to find a valid input set that meets the provided eligibility filter and target.
105+
* Multiple coin selection algorithms will be run and the input set that produces the least waste
106+
* (according to the waste metric) will be chosen.
107+
*
108+
* param@[in] wallet The wallet which provides solving data for the coins
109+
* param@[in] nTargetValue The target value
110+
* param@[in] eligilibity_filter A filter containing rules for which coins are allowed to be included in this selection
111+
* param@[in] coins The vector of coins available for selection prior to filtering
112+
* param@[in] coin_selection_params Parameters for the coin selection
113+
* returns If successful, a SelectionResult containing the input set
114+
* If failed, a nullopt
113115
*/
114-
bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
115-
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params);
116+
std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
117+
const CoinSelectionParams& coin_selection_params);
116118

117119
/**
118120
* Select a set of coins such that nValueRet >= nTargetValue and at least

0 commit comments

Comments
 (0)