mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-12 23:18:14 +01:00
wallet: decouple outputs grouping process from each ChooseSelectionResult
Another step towards the single OutputGroups calculation goal
This commit is contained in:
@@ -242,6 +242,13 @@ struct OutputGroup
|
|||||||
CAmount GetSelectionAmount() const;
|
CAmount GetSelectionAmount() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Groups {
|
||||||
|
// Stores 'OutputGroup' containing only positive UTXOs (value > 0).
|
||||||
|
std::vector<OutputGroup> positive_group;
|
||||||
|
// Stores 'OutputGroup' which may contain both positive and negative UTXOs.
|
||||||
|
std::vector<OutputGroup> mixed_group;
|
||||||
|
};
|
||||||
|
|
||||||
/** Compute the waste for this result given the cost of change
|
/** Compute the waste for this result given the cost of change
|
||||||
* and the opportunity cost of spending these inputs now vs in the future.
|
* and the opportunity cost of spending these inputs now vs in the future.
|
||||||
* If change exists, waste = change_cost + inputs * (effective_feerate - long_term_feerate)
|
* If change exists, waste = change_cost + inputs * (effective_feerate - long_term_feerate)
|
||||||
|
|||||||
@@ -499,8 +499,11 @@ util::Result<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmo
|
|||||||
{
|
{
|
||||||
// Run coin selection on each OutputType and compute the Waste Metric
|
// Run coin selection on each OutputType and compute the Waste Metric
|
||||||
std::vector<SelectionResult> results;
|
std::vector<SelectionResult> results;
|
||||||
for (const auto& it : available_coins.coins) {
|
for (const auto& [type, coins] : available_coins.coins) {
|
||||||
auto result{ChooseSelectionResult(wallet, nTargetValue, eligibility_filter, it.second, coin_selection_params)};
|
Groups groups;
|
||||||
|
groups.positive_group = GroupOutputs(wallet, coins, coin_selection_params, eligibility_filter, true /* positive_only */);
|
||||||
|
groups.mixed_group = GroupOutputs(wallet, coins, coin_selection_params, eligibility_filter, false /* positive_only */);
|
||||||
|
auto result{ChooseSelectionResult(nTargetValue, groups, coin_selection_params)};
|
||||||
// If any specific error message appears here, then something particularly wrong happened.
|
// If any specific error message appears here, then something particularly wrong happened.
|
||||||
if (HasErrorMsg(result)) return result; // So let's return the specific error.
|
if (HasErrorMsg(result)) return result; // So let's return the specific error.
|
||||||
// Append the favorable result.
|
// Append the favorable result.
|
||||||
@@ -514,31 +517,33 @@ util::Result<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmo
|
|||||||
// over all available coins, which would allow mixing.
|
// over all available coins, which would allow mixing.
|
||||||
// If TypesCount() <= 1, there is nothing to mix.
|
// If TypesCount() <= 1, there is nothing to mix.
|
||||||
if (allow_mixed_output_types && available_coins.TypesCount() > 1) {
|
if (allow_mixed_output_types && available_coins.TypesCount() > 1) {
|
||||||
return ChooseSelectionResult(wallet, nTargetValue, eligibility_filter, available_coins.All(), coin_selection_params);
|
const auto& all = available_coins.All();
|
||||||
|
Groups groups;
|
||||||
|
groups.positive_group = GroupOutputs(wallet, all, coin_selection_params, eligibility_filter, true /* positive_only */);
|
||||||
|
groups.mixed_group = GroupOutputs(wallet, all, coin_selection_params, eligibility_filter, false /* positive_only */);
|
||||||
|
return ChooseSelectionResult(nTargetValue, groups, coin_selection_params);
|
||||||
}
|
}
|
||||||
// Either mixing is not allowed and we couldn't find a solution from any single OutputType, or mixing was allowed and we still couldn't
|
// Either mixing is not allowed and we couldn't find a solution from any single OutputType, or mixing was allowed and we still couldn't
|
||||||
// find a solution using all available coins
|
// find a solution using all available coins
|
||||||
return util::Error();
|
return util::Error();
|
||||||
};
|
};
|
||||||
|
|
||||||
util::Result<SelectionResult> ChooseSelectionResult(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, const std::vector<COutput>& available_coins, const CoinSelectionParams& coin_selection_params)
|
util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue, Groups& groups, const CoinSelectionParams& coin_selection_params)
|
||||||
{
|
{
|
||||||
// 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;
|
||||||
|
|
||||||
std::vector<OutputGroup> positive_groups = GroupOutputs(wallet, available_coins, coin_selection_params, eligibility_filter, /*positive_only=*/true);
|
if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change)}) {
|
||||||
if (auto bnb_result{SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change)}) {
|
|
||||||
results.push_back(*bnb_result);
|
results.push_back(*bnb_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here.
|
// The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here.
|
||||||
std::vector<OutputGroup> all_groups = GroupOutputs(wallet, available_coins, coin_selection_params, eligibility_filter, /*positive_only=*/false);
|
if (auto knapsack_result{KnapsackSolver(groups.mixed_group, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast)}) {
|
||||||
if (auto knapsack_result{KnapsackSolver(all_groups, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast)}) {
|
|
||||||
knapsack_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
|
knapsack_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
|
||||||
results.push_back(*knapsack_result);
|
results.push_back(*knapsack_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto srd_result{SelectCoinsSRD(positive_groups, nTargetValue, coin_selection_params.rng_fast)}) {
|
if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.rng_fast)}) {
|
||||||
srd_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
|
srd_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
|
||||||
results.push_back(*srd_result);
|
results.push_back(*srd_result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,18 +132,15 @@ util::Result<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmo
|
|||||||
* Multiple coin selection algorithms will be run and the input set that produces the least waste
|
* Multiple coin selection algorithms will be run and the input set that produces the least waste
|
||||||
* (according to the waste metric) will be chosen.
|
* (according to the waste metric) will be chosen.
|
||||||
*
|
*
|
||||||
* param@[in] wallet The wallet which provides solving data for the coins
|
|
||||||
* param@[in] nTargetValue The target value
|
* param@[in] nTargetValue The target value
|
||||||
* param@[in] eligilibity_filter A filter containing rules for which coins are allowed to be included in this selection
|
* param@[in] groups The struct containing the outputs grouped by script and divided by (1) positive only outputs and (2) all outputs (positive + negative).
|
||||||
* param@[in] available_coins The struct of coins, organized by OutputType, available for selection prior to filtering
|
|
||||||
* param@[in] coin_selection_params Parameters for the coin selection
|
* param@[in] coin_selection_params Parameters for the coin selection
|
||||||
* returns If successful, a SelectionResult containing the input set
|
* returns If successful, a SelectionResult containing the input set
|
||||||
* If failed, returns (1) an empty error message if the target was not reached (general "Insufficient funds")
|
* If failed, returns (1) an empty error message if the target was not reached (general "Insufficient funds")
|
||||||
* or (2) an specific error message if there was something particularly wrong (e.g. a selection
|
* or (2) an specific error message if there was something particularly wrong (e.g. a selection
|
||||||
* result that surpassed the tx max weight size).
|
* result that surpassed the tx max weight size).
|
||||||
*/
|
*/
|
||||||
util::Result<SelectionResult> ChooseSelectionResult(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, const std::vector<COutput>& available_coins,
|
util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue, Groups& groups, const CoinSelectionParams& coin_selection_params);
|
||||||
const CoinSelectionParams& coin_selection_params);
|
|
||||||
|
|
||||||
// User manually selected inputs that must be part of the transaction
|
// User manually selected inputs that must be part of the transaction
|
||||||
struct PreSelectedInputs
|
struct PreSelectedInputs
|
||||||
|
|||||||
Reference in New Issue
Block a user