aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/spend.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/spend.cpp')
-rw-r--r--src/wallet/spend.cpp51
1 files changed, 24 insertions, 27 deletions
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 4548d5f813..c3303419d4 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -356,12 +356,6 @@ CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl*
return AvailableCoins(wallet, coinControl, /*feerate=*/ std::nullopt, params);
}
-CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl)
-{
- LOCK(wallet.cs_wallet);
- return AvailableCoins(wallet, coinControl).GetTotalAmount();
-}
-
const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const COutPoint& outpoint)
{
AssertLockHeld(wallet.cs_wallet);
@@ -565,42 +559,45 @@ util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue,
{
// Vector of results. We will choose the best one based on waste.
std::vector<SelectionResult> results;
+ std::vector<util::Result<SelectionResult>> errors;
+ auto append_error = [&] (const util::Result<SelectionResult>& result) {
+ // If any specific error message appears here, then something different from a simple "no selection found" happened.
+ // Let's save it, so it can be retrieved to the user if no other selection algorithm succeeded.
+ if (HasErrorMsg(result)) {
+ errors.emplace_back(result);
+ }
+ };
+
+ // Maximum allowed weight
+ int max_inputs_weight = MAX_STANDARD_TX_WEIGHT - (coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR);
- if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change)}) {
+ if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change, max_inputs_weight)}) {
results.push_back(*bnb_result);
- }
+ } else append_error(bnb_result);
+
+ // As Knapsack and SRD can create change, also deduce change weight.
+ max_inputs_weight -= (coin_selection_params.change_output_size * WITNESS_SCALE_FACTOR);
// 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.
- 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(groups.mixed_group, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast, max_inputs_weight)}) {
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);
- }
+ } else append_error(knapsack_result);
- if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.rng_fast)}) {
+ if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.rng_fast, max_inputs_weight)}) {
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);
- }
+ } else append_error(srd_result);
if (results.empty()) {
- // No solution found
- return util::Error();
- }
-
- std::vector<SelectionResult> eligible_results;
- std::copy_if(results.begin(), results.end(), std::back_inserter(eligible_results), [coin_selection_params](const SelectionResult& result) {
- const auto initWeight{coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR};
- return initWeight + result.GetWeight() <= static_cast<int>(MAX_STANDARD_TX_WEIGHT);
- });
-
- if (eligible_results.empty()) {
- return util::Error{_("The inputs size exceeds the maximum weight. "
- "Please try sending a smaller amount or manually consolidating your wallet's UTXOs")};
+ // No solution found, retrieve the first explicit error (if any).
+ // future: add 'severity level' to errors so the worst one can be retrieved instead of the first one.
+ return errors.empty() ? util::Error() : errors.front();
}
// Choose the result with the least waste
// If the waste is the same, choose the one which spends more inputs.
- auto& best_result = *std::min_element(eligible_results.begin(), eligible_results.end());
- return best_result;
+ return *std::min_element(results.begin(), results.end());
}
util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& available_coins, const PreSelectedInputs& pre_set_inputs,