Skip to content

Commit 2d30567

Browse files
committed
Merge bitcoin/bitcoin#29906: Disable util::Result copying and assignment
6a8b2be refactor: Avoid copying util::Result values (Ryan Ofsky) 834f65e refactor: Drop util::Result operator= (Ryan Ofsky) Pull request description: This PR just contains the first two commits of #25665. It disables copying of `util::Result` objects because unnecessary copies are inefficient and not possible after #25665, which makes `util::Result` object move-only. It disables the assignment operator and replaces it with an `Update()` method, because #25665 adds more information to `util::Result` objects (warning and error messages and failure values) and having an assignment operator that overwrites data instead of merging it would make it easy to accidentally erase existing information while trying to assign new information. ACKs for top commit: stickies-v: re-ACK 6a8b2be achow101: ACK 6a8b2be furszy: re-ACK bitcoin/bitcoin@6a8b2be Tree-SHA512: 3f21af9031d50d6c68cca69133de03080f69b1ddcf8b140bdeb762069f14645209b2586037236d15b6ebd8973af0fbefd7e83144aeb7b84078a4cb4df812f984
2 parents 15f696b + 6a8b2be commit 2d30567

File tree

9 files changed

+60
-45
lines changed

9 files changed

+60
-45
lines changed

src/init.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -992,10 +992,8 @@ bool AppInitParameterInteraction(const ArgsManager& args)
992992
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
993993

994994
// ********************************************************* Step 3: parameter-to-internal-flags
995-
auto result = init::SetLoggingCategories(args);
996-
if (!result) return InitError(util::ErrorString(result));
997-
result = init::SetLoggingLevel(args);
998-
if (!result) return InitError(util::ErrorString(result));
995+
if (auto result{init::SetLoggingCategories(args)}; !result) return InitError(util::ErrorString(result));
996+
if (auto result{init::SetLoggingLevel(args)}; !result) return InitError(util::ErrorString(result));
999997

1000998
nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
1001999
if (nConnectTimeout <= 0) {

src/qt/addresstablemodel.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -369,21 +369,22 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
369369
else if(type == Receive)
370370
{
371371
// Generate a new address to associate with given label
372-
auto op_dest = walletModel->wallet().getNewDestination(address_type, strLabel);
373-
if (!op_dest) {
372+
if (auto dest{walletModel->wallet().getNewDestination(address_type, strLabel)}) {
373+
strAddress = EncodeDestination(*dest);
374+
} else {
374375
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
375376
if (!ctx.isValid()) {
376377
// Unlock wallet failed or was cancelled
377378
editStatus = WALLET_UNLOCK_FAILURE;
378379
return QString();
379380
}
380-
op_dest = walletModel->wallet().getNewDestination(address_type, strLabel);
381-
if (!op_dest) {
381+
if (auto dest_retry{walletModel->wallet().getNewDestination(address_type, strLabel)}) {
382+
strAddress = EncodeDestination(*dest_retry);
383+
} else {
382384
editStatus = KEY_GENERATION_FAILURE;
383385
return QString();
384386
}
385387
}
386-
strAddress = EncodeDestination(*op_dest);
387388
}
388389
else
389390
{

src/test/mempool_tests.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,11 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
202202
tx7.vout[1].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
203203
tx7.vout[1].nValue = 1 * COIN;
204204

205-
auto ancestors_calculated{pool.CalculateMemPoolAncestors(entry.Fee(2000000LL).FromTx(tx7), CTxMemPool::Limits::NoLimits())};
206-
BOOST_REQUIRE(ancestors_calculated.has_value());
207-
BOOST_CHECK(*ancestors_calculated == setAncestors);
205+
{
206+
auto ancestors_calculated{pool.CalculateMemPoolAncestors(entry.Fee(2000000LL).FromTx(tx7), CTxMemPool::Limits::NoLimits())};
207+
BOOST_REQUIRE(ancestors_calculated.has_value());
208+
BOOST_CHECK(*ancestors_calculated == setAncestors);
209+
}
208210

209211
pool.addUnchecked(entry.FromTx(tx7), setAncestors);
210212
BOOST_CHECK_EQUAL(pool.size(), 7U);
@@ -260,9 +262,11 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
260262
tx10.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
261263
tx10.vout[0].nValue = 10 * COIN;
262264

263-
ancestors_calculated = pool.CalculateMemPoolAncestors(entry.Fee(200000LL).Time(NodeSeconds{4s}).FromTx(tx10), CTxMemPool::Limits::NoLimits());
264-
BOOST_REQUIRE(ancestors_calculated);
265-
BOOST_CHECK(*ancestors_calculated == setAncestors);
265+
{
266+
auto ancestors_calculated{pool.CalculateMemPoolAncestors(entry.Fee(200000LL).Time(NodeSeconds{4s}).FromTx(tx10), CTxMemPool::Limits::NoLimits())};
267+
BOOST_REQUIRE(ancestors_calculated);
268+
BOOST_CHECK(*ancestors_calculated == setAncestors);
269+
}
266270

267271
pool.addUnchecked(entry.FromTx(tx10), setAncestors);
268272

src/util/result.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,25 @@ 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+
45+
//! Disallow operator= to avoid confusion in the future when the Result
46+
//! class gains support for richer error reporting, and callers should have
47+
//! ability to set a new result value without clearing existing error
48+
//! messages.
49+
Result& operator=(const Result&) = delete;
50+
Result& operator=(Result&&) = delete;
51+
4252
template <typename FT>
4353
friend bilingual_str ErrorString(const Result<FT>& result);
4454

4555
public:
4656
Result() : m_variant{std::in_place_index_t<1>{}, std::monostate{}} {} // constructor for void
4757
Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {}
4858
Result(Error error) : m_variant{std::in_place_index_t<0>{}, std::move(error.message)} {}
59+
Result(Result&&) = default;
60+
~Result() = default;
4961

5062
//! std::optional methods, so functions returning optional<T> can change to
5163
//! return Result<T> with minimal changes to existing code, and vice versa.

src/validation.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -946,8 +946,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
946946
maybe_rbf_limits.descendant_size_vbytes += conflict->GetSizeWithDescendants();
947947
}
948948

949-
auto ancestors{m_pool.CalculateMemPoolAncestors(*entry, maybe_rbf_limits)};
950-
if (!ancestors) {
949+
if (auto ancestors{m_pool.CalculateMemPoolAncestors(*entry, maybe_rbf_limits)}) {
950+
ws.m_ancestors = std::move(*ancestors);
951+
} else {
951952
// If CalculateMemPoolAncestors fails second time, we want the original error string.
952953
// Contracting/payment channels CPFP carve-out:
953954
// If the new transaction is relatively small (up to 40k weight)
@@ -970,11 +971,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
970971
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT) {
971972
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message);
972973
}
973-
ancestors = m_pool.CalculateMemPoolAncestors(*entry, cpfp_carve_out_limits);
974-
if (!ancestors) return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message);
974+
if (auto ancestors_retry{m_pool.CalculateMemPoolAncestors(*entry, cpfp_carve_out_limits)}) {
975+
ws.m_ancestors = std::move(*ancestors_retry);
976+
} else {
977+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message);
978+
}
975979
}
976980

977-
ws.m_ancestors = *ancestors;
978981
// Even though just checking direct mempool parents for inheritance would be sufficient, we
979982
// check using the full ancestor set here because it's more convenient to use what we have
980983
// already calculated.

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;

src/wallet/test/fuzz/notifications.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,11 @@ struct FuzzedWallet {
106106
CTxDestination GetDestination(FuzzedDataProvider& fuzzed_data_provider)
107107
{
108108
auto type{fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES)};
109-
util::Result<CTxDestination> op_dest{util::Error{}};
110109
if (fuzzed_data_provider.ConsumeBool()) {
111-
op_dest = wallet->GetNewDestination(type, "");
110+
return *Assert(wallet->GetNewDestination(type, ""));
112111
} else {
113-
op_dest = wallet->GetNewChangeDestination(type);
112+
return *Assert(wallet->GetNewChangeDestination(type));
114113
}
115-
return *Assert(op_dest);
116114
}
117115
CScript GetScriptPubKey(FuzzedDataProvider& fuzzed_data_provider) { return GetScriptForDestination(GetDestination(fuzzed_data_provider)); }
118116
void FundTx(FuzzedDataProvider& fuzzed_data_provider, CMutableTransaction tx)

src/wallet/test/spend_tests.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,11 @@ BOOST_FIXTURE_TEST_CASE(wallet_duplicated_preset_inputs_test, TestChain100Setup)
9797
// so that the recipient's amount is no longer equal to the user's selected target of 299 BTC.
9898

9999
// First case, use 'subtract_fee_from_outputs=true'
100-
util::Result<CreatedTransactionResult> res_tx = CreateTransaction(*wallet, recipients, /*change_pos=*/std::nullopt, coin_control);
101-
BOOST_CHECK(!res_tx.has_value());
100+
BOOST_CHECK(!CreateTransaction(*wallet, recipients, /*change_pos=*/std::nullopt, coin_control));
102101

103102
// Second case, don't use 'subtract_fee_from_outputs'.
104103
recipients[0].fSubtractFeeFromAmount = false;
105-
res_tx = CreateTransaction(*wallet, recipients, /*change_pos=*/std::nullopt, coin_control);
106-
BOOST_CHECK(!res_tx.has_value());
104+
BOOST_CHECK(!CreateTransaction(*wallet, recipients, /*change_pos=*/std::nullopt, coin_control));
107105
}
108106

109107
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)