mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 15:04:44 +01:00
Use SelectionResult in AttemptSelection
Replace setCoinsRet and nValueRet with a SelectionResult in AttemptSelection
This commit is contained in:
parent
bb50850a44
commit
9d9b101d20
3 changed files with 53 additions and 54 deletions
|
@ -54,12 +54,10 @@ static void CoinSelection(benchmark::Bench& bench)
|
||||||
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
|
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
|
||||||
/* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
|
/* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
|
||||||
bench.run([&] {
|
bench.run([&] {
|
||||||
std::set<CInputCoin> setCoinsRet;
|
auto result = AttemptSelection(wallet, 1003 * COIN, filter_standard, coins, coin_selection_params);
|
||||||
CAmount nValueRet;
|
assert(result);
|
||||||
bool success = AttemptSelection(wallet, 1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
|
assert(result->GetSelectedValue() == 1003 * COIN);
|
||||||
assert(success);
|
assert(result->GetInputSet().size() == 2);
|
||||||
assert(nValueRet == 1003 * COIN);
|
|
||||||
assert(setCoinsRet.size() == 2);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -373,11 +373,9 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
|
||||||
return groups_out;
|
return groups_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
|
std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
|
||||||
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params)
|
const CoinSelectionParams& coin_selection_params)
|
||||||
{
|
{
|
||||||
setCoinsRet.clear();
|
|
||||||
nValueRet = 0;
|
|
||||||
// Vector of results. We will choose the best one based on waste.
|
// Vector of results. We will choose the best one based on waste.
|
||||||
std::vector<SelectionResult> results;
|
std::vector<SelectionResult> results;
|
||||||
|
|
||||||
|
@ -407,15 +405,13 @@ bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const
|
||||||
|
|
||||||
if (results.size() == 0) {
|
if (results.size() == 0) {
|
||||||
// No solution found
|
// No solution found
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose the result with the least waste
|
// Choose the result with the least waste
|
||||||
// If the waste is the same, choose the one which spends more inputs.
|
// If the waste is the same, choose the one which spends more inputs.
|
||||||
auto& best_result = *std::min_element(results.begin(), results.end());
|
auto& best_result = *std::min_element(results.begin(), results.end());
|
||||||
setCoinsRet = best_result.GetInputSet();
|
return best_result;
|
||||||
nValueRet = best_result.GetSelectedValue();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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
|
||||||
|
|
||||||
// calculate value from preset inputs and store them
|
// calculate value from preset inputs and store them
|
||||||
std::set<CInputCoin> setPresetCoins;
|
std::set<CInputCoin> setPresetCoins;
|
||||||
CAmount nValueFromPresetInputs = 0;
|
|
||||||
OutputGroup preset_inputs(coin_selection_params);
|
OutputGroup preset_inputs(coin_selection_params);
|
||||||
|
|
||||||
std::vector<COutPoint> vPresetInputs;
|
std::vector<COutPoint> vPresetInputs;
|
||||||
|
@ -466,7 +461,6 @@ bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCo
|
||||||
}
|
}
|
||||||
|
|
||||||
CInputCoin coin(outpoint, txout, input_bytes);
|
CInputCoin coin(outpoint, txout, input_bytes);
|
||||||
nValueFromPresetInputs += coin.txout.nValue;
|
|
||||||
if (coin.m_input_bytes == -1) {
|
if (coin.m_input_bytes == -1) {
|
||||||
return false; // Not solvable, can't estimate size for fee
|
return false; // Not solvable, can't estimate size for fee
|
||||||
}
|
}
|
||||||
|
@ -511,62 +505,67 @@ bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCo
|
||||||
// Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
|
// Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
|
||||||
// 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.
|
||||||
const bool res = [&] {
|
std::optional<SelectionResult> res = [&] {
|
||||||
// Pre-selected inputs already cover the target amount.
|
// Pre-selected inputs already cover the target amount.
|
||||||
if (value_to_select <= 0) return true;
|
if (value_to_select <= 0) return std::make_optional(SelectionResult(nTargetValue));
|
||||||
|
|
||||||
// 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 (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
|
if (auto r1{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, coin_selection_params)}) return r1;
|
||||||
if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
|
if (auto r2{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, coin_selection_params)}) 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 (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
|
if (auto r3{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, coin_selection_params)}) return r3;
|
||||||
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)),
|
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)),
|
||||||
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
|
vCoins, coin_selection_params)}) {
|
||||||
return true;
|
return r4;
|
||||||
}
|
}
|
||||||
if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
|
if (auto r5{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
|
||||||
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
|
vCoins, coin_selection_params)}) {
|
||||||
return 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 (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
|
if (auto r6{AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
|
||||||
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
|
vCoins, coin_selection_params)}) {
|
||||||
return 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) {
|
||||||
&& AttemptSelection(wallet, value_to_select,
|
if (auto r7{AttemptSelection(wallet, value_to_select,
|
||||||
CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
|
CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
|
||||||
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
|
vCoins, coin_selection_params)}) {
|
||||||
return 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 && AttemptSelection(wallet, value_to_select,
|
if (!fRejectLongChains) {
|
||||||
|
if (auto r8{AttemptSelection(wallet, value_to_select,
|
||||||
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */),
|
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)) {
|
vCoins, coin_selection_params)}) {
|
||||||
return true;
|
return r8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Coin Selection failed.
|
// Coin Selection failed.
|
||||||
return false;
|
return std::optional<SelectionResult>();
|
||||||
}();
|
}();
|
||||||
|
|
||||||
// AttemptSelection clears setCoinsRet, so add the preset inputs from coin_control to the coinset
|
if (!res) return false;
|
||||||
util::insert(setCoinsRet, setPresetCoins);
|
|
||||||
|
|
||||||
// add preset inputs to the total value selected
|
// Add preset inputs to result
|
||||||
nValueRet += nValueFromPresetInputs;
|
res->AddInput(preset_inputs);
|
||||||
|
|
||||||
return res;
|
setCoinsRet = res->GetInputSet();
|
||||||
|
nValueRet = res->GetSelectedValue();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256& block_hash)
|
static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256& block_hash)
|
||||||
|
|
|
@ -101,18 +101,20 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
|
||||||
std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only);
|
std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuffle and select coins until nTargetValue is reached while avoiding
|
* Attempt to find a valid input set that meets the provided eligibility filter and target.
|
||||||
* small change; This method is stochastic for some inputs and upon
|
* Multiple coin selection algorithms will be run and the input set that produces the least waste
|
||||||
* completion the coin set and corresponding actual target value is
|
* (according to the waste metric) will be chosen.
|
||||||
* assembled
|
*
|
||||||
* param@[in] coins Set of UTXOs to consider. These will be categorized into
|
* param@[in] wallet The wallet which provides solving data for the coins
|
||||||
* OutputGroups and filtered using eligibility_filter before
|
* param@[in] nTargetValue The target value
|
||||||
* selecting coins.
|
* param@[in] eligilibity_filter A filter containing rules for which coins are allowed to be included in this selection
|
||||||
* param@[out] setCoinsRet Populated with the coins selected if successful.
|
* param@[in] coins The vector of coins available for selection prior to filtering
|
||||||
* param@[out] nValueRet Used to return the total value of selected coins.
|
* param@[in] coin_selection_params Parameters for the coin selection
|
||||||
|
* returns If successful, a SelectionResult containing the input set
|
||||||
|
* If failed, a nullopt
|
||||||
*/
|
*/
|
||||||
bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
|
std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
|
||||||
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params);
|
const CoinSelectionParams& coin_selection_params);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a set of coins such that nValueRet >= nTargetValue and at least
|
* Select a set of coins such that nValueRet >= nTargetValue and at least
|
||||||
|
|
Loading…
Add table
Reference in a new issue