Skip to content

Commit 1284223

Browse files
committed
wallet: refactor coin selection algos to return util::Result
so the selection processes can retrieve different errors and not uninformative std::nullopt
1 parent 86bacd7 commit 1284223

File tree

3 files changed

+25
-15
lines changed

3 files changed

+25
-15
lines changed

src/wallet/coinselection.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ struct {
6363

6464
static const size_t TOTAL_TRIES = 100000;
6565

66-
std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change)
66+
util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change)
6767
{
6868
SelectionResult result(selection_target, SelectionAlgorithm::BNB);
6969
CAmount curr_value = 0;
@@ -78,7 +78,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
7878
curr_available_value += utxo.GetSelectionAmount();
7979
}
8080
if (curr_available_value < selection_target) {
81-
return std::nullopt;
81+
return util::Error();
8282
}
8383

8484
// Sort the utxo_pool
@@ -152,7 +152,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
152152

153153
// Check for solution
154154
if (best_selection.empty()) {
155-
return std::nullopt;
155+
return util::Error();
156156
}
157157

158158
// Set output set
@@ -165,7 +165,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
165165
return result;
166166
}
167167

168-
std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng)
168+
util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng)
169169
{
170170
SelectionResult result(target_value, SelectionAlgorithm::SRD);
171171

@@ -190,7 +190,7 @@ std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& ut
190190
return result;
191191
}
192192
}
193-
return std::nullopt;
193+
return util::Error();
194194
}
195195

196196
/** Find a subset of the OutputGroups that is at least as large as, but as close as possible to, the
@@ -252,7 +252,7 @@ static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::v
252252
}
253253
}
254254

255-
std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
255+
util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
256256
CAmount change_target, FastRandomContext& rng)
257257
{
258258
SelectionResult result(nTargetValue, SelectionAlgorithm::KNAPSACK);
@@ -286,7 +286,7 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
286286
}
287287

288288
if (nTotalLower < nTargetValue) {
289-
if (!lowest_larger) return std::nullopt;
289+
if (!lowest_larger) return util::Error();
290290
result.AddInput(*lowest_larger);
291291
return result;
292292
}

src/wallet/coinselection.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <random.h>
1414
#include <util/system.h>
1515
#include <util/check.h>
16+
#include <util/result.h>
1617

1718
#include <optional>
1819

@@ -408,7 +409,7 @@ struct SelectionResult
408409
int GetWeight() const { return m_weight; }
409410
};
410411

411-
std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change);
412+
util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change);
412413

413414
/** Select coins by Single Random Draw. OutputGroups are selected randomly from the eligible
414415
* outputs until the target is satisfied
@@ -417,10 +418,10 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
417418
* @param[in] target_value The target value to select for
418419
* @returns If successful, a SelectionResult, otherwise, std::nullopt
419420
*/
420-
std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng);
421+
util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng);
421422

422423
// Original coin selection algorithm as a fallback
423-
std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
424+
util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
424425
CAmount change_target, FastRandomContext& rng);
425426
} // namespace wallet
426427

src/wallet/spend.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -554,25 +554,34 @@ util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue,
554554
{
555555
// Vector of results. We will choose the best one based on waste.
556556
std::vector<SelectionResult> results;
557+
std::vector<util::Result<SelectionResult>> errors;
558+
auto append_error = [&] (const util::Result<SelectionResult>& result) {
559+
// If any specific error message appears here, then something different from a simple "no selection found" happened.
560+
// Let's save it, so it can be retrieved to the user if no other selection algorithm succeeded.
561+
if (HasErrorMsg(result)) {
562+
errors.emplace_back(result);
563+
}
564+
};
557565

558566
if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change)}) {
559567
results.push_back(*bnb_result);
560-
}
568+
} else append_error(bnb_result);
561569

562570
// 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.
563571
if (auto knapsack_result{KnapsackSolver(groups.mixed_group, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast)}) {
564572
knapsack_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
565573
results.push_back(*knapsack_result);
566-
}
574+
} else append_error(knapsack_result);
567575

568576
if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.rng_fast)}) {
569577
srd_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
570578
results.push_back(*srd_result);
571-
}
579+
} else append_error(srd_result);
572580

573581
if (results.empty()) {
574-
// No solution found
575-
return util::Error();
582+
// No solution found, retrieve the first explicit error (if any).
583+
// future: add 'severity level' to errors so the worst one can be retrieved instead of the first one.
584+
return errors.empty() ? util::Error() : errors.front();
576585
}
577586

578587
std::vector<SelectionResult> eligible_results;

0 commit comments

Comments
 (0)