Skip to content

Commit 6a8b2be

Browse files
committed
refactor: Avoid copying util::Result values
Copying util::Result values is less efficient than moving them because they allocate memory and contain strings. Also this is needed to avoid compile errors in bitcoin/bitcoin#25722 which adds a std::unique_ptr member to util::Result which implicity disables copying.
1 parent 834f65e commit 6a8b2be

File tree

3 files changed

+21
-18
lines changed

3 files changed

+21
-18
lines changed

src/util/result.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ class Result
3939

4040
std::variant<bilingual_str, T> m_variant;
4141

42+
//! Disallow copy constructor, require Result to be moved for efficiency.
43+
Result(const Result&) = delete;
44+
4245
//! Disallow operator= to avoid confusion in the future when the Result
4346
//! class gains support for richer error reporting, and callers should have
4447
//! ability to set a new result value without clearing existing error
@@ -53,7 +56,6 @@ class Result
5356
Result() : m_variant{std::in_place_index_t<1>{}, std::monostate{}} {} // constructor for void
5457
Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {}
5558
Result(Error error) : m_variant{std::in_place_index_t<0>{}, std::move(error.message)} {}
56-
Result(const Result&) = default;
5759
Result(Result&&) = default;
5860
~Result() = default;
5961

src/wallet/spend.cpp

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -683,11 +683,11 @@ util::Result<SelectionResult> ChooseSelectionResult(interfaces::Chain& chain, co
683683
// Vector of results. We will choose the best one based on waste.
684684
std::vector<SelectionResult> results;
685685
std::vector<util::Result<SelectionResult>> errors;
686-
auto append_error = [&] (const util::Result<SelectionResult>& result) {
686+
auto append_error = [&] (util::Result<SelectionResult>&& result) {
687687
// If any specific error message appears here, then something different from a simple "no selection found" happened.
688688
// Let's save it, so it can be retrieved to the user if no other selection algorithm succeeded.
689689
if (HasErrorMsg(result)) {
690-
errors.emplace_back(result);
690+
errors.emplace_back(std::move(result));
691691
}
692692
};
693693

@@ -698,7 +698,7 @@ util::Result<SelectionResult> ChooseSelectionResult(interfaces::Chain& chain, co
698698
if (!coin_selection_params.m_subtract_fee_outputs) {
699699
if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change, max_inputs_weight)}) {
700700
results.push_back(*bnb_result);
701-
} else append_error(bnb_result);
701+
} else append_error(std::move(bnb_result));
702702
}
703703

704704
// As Knapsack and SRD can create change, also deduce change weight.
@@ -707,25 +707,25 @@ util::Result<SelectionResult> ChooseSelectionResult(interfaces::Chain& chain, co
707707
// 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.
708708
if (auto knapsack_result{KnapsackSolver(groups.mixed_group, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast, max_inputs_weight)}) {
709709
results.push_back(*knapsack_result);
710-
} else append_error(knapsack_result);
710+
} else append_error(std::move(knapsack_result));
711711

712712
if (coin_selection_params.m_effective_feerate > CFeeRate{3 * coin_selection_params.m_long_term_feerate}) { // Minimize input set for feerates of at least 3×LTFRE (default: 30 ṩ/vB+)
713713
if (auto cg_result{CoinGrinder(groups.positive_group, nTargetValue, coin_selection_params.m_min_change_target, max_inputs_weight)}) {
714714
cg_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
715715
results.push_back(*cg_result);
716716
} else {
717-
append_error(cg_result);
717+
append_error(std::move(cg_result));
718718
}
719719
}
720720

721721
if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.m_change_fee, coin_selection_params.rng_fast, max_inputs_weight)}) {
722722
results.push_back(*srd_result);
723-
} else append_error(srd_result);
723+
} else append_error(std::move(srd_result));
724724

725725
if (results.empty()) {
726726
// No solution found, retrieve the first explicit error (if any).
727727
// future: add 'severity level' to errors so the worst one can be retrieved instead of the first one.
728-
return errors.empty() ? util::Error() : errors.front();
728+
return errors.empty() ? util::Error() : std::move(errors.front());
729729
}
730730

731731
// If the chosen input set has unconfirmed inputs, check for synergies from overlapping ancestry
@@ -818,7 +818,7 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin
818818
// Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
819819
// transaction at a target feerate. If an attempt fails, more attempts may be made using a more
820820
// permissive CoinEligibilityFilter.
821-
util::Result<SelectionResult> res = [&] {
821+
{
822822
// Place coins eligibility filters on a scope increasing order.
823823
std::vector<SelectionFilter> ordered_filters{
824824
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
@@ -866,9 +866,9 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin
866866
if (CAmount total_amount = available_coins.GetTotalAmount() - total_discarded < value_to_select) {
867867
// Special case, too-long-mempool cluster.
868868
if (total_amount + total_unconf_long_chain > value_to_select) {
869-
return util::Result<SelectionResult>({_("Unconfirmed UTXOs are available, but spending them creates a chain of transactions that will be rejected by the mempool")});
869+
return util::Error{_("Unconfirmed UTXOs are available, but spending them creates a chain of transactions that will be rejected by the mempool")};
870870
}
871-
return util::Result<SelectionResult>(util::Error()); // General "Insufficient Funds"
871+
return util::Error{}; // General "Insufficient Funds"
872872
}
873873

874874
// Walk-through the filters until the solution gets found.
@@ -885,19 +885,17 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin
885885
// If any specific error message appears here, then something particularly wrong might have happened.
886886
// Save the error and continue the selection process. So if no solutions gets found, we can return
887887
// the detailed error to the upper layers.
888-
if (HasErrorMsg(res)) res_detailed_errors.emplace_back(res);
888+
if (HasErrorMsg(res)) res_detailed_errors.emplace_back(std::move(res));
889889
}
890890
}
891891

892892
// Return right away if we have a detailed error
893-
if (!res_detailed_errors.empty()) return res_detailed_errors.front();
893+
if (!res_detailed_errors.empty()) return std::move(res_detailed_errors.front());
894894

895895

896896
// General "Insufficient Funds"
897-
return util::Result<SelectionResult>(util::Error());
898-
}();
899-
900-
return res;
897+
return util::Error{};
898+
}
901899
}
902900

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

src/wallet/test/fuzz/coinselection.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,10 @@ FUZZ_TARGET(coinselection)
291291
}
292292

293293
std::vector<COutput> utxos;
294-
std::vector<util::Result<SelectionResult>> results{result_srd, result_knapsack, result_bnb};
294+
std::vector<util::Result<SelectionResult>> results;
295+
results.emplace_back(std::move(result_srd));
296+
results.emplace_back(std::move(result_knapsack));
297+
results.emplace_back(std::move(result_bnb));
295298
CAmount new_total_balance{CreateCoins(fuzzed_data_provider, utxos, coin_params, next_locktime)};
296299
if (new_total_balance > 0) {
297300
std::set<std::shared_ptr<COutput>> new_utxo_pool;

0 commit comments

Comments
 (0)