Skip to content

Commit 834f65e

Browse files
ryanofskystickies-v
andcommitted
refactor: Drop util::Result operator=
`util::Result` objects are aggregates that can hold multiple fields with different information. Currently Result objects can only hold a success value of an arbitrary type or a single bilingual_str error message. In followup PR bitcoin/bitcoin#25722, Result objects may be able to hold both success and failure values of different types, plus error and warning messages. Having a Result::operator= assignment operator that completely erases all existing Result information before assigning new information is potentially dangerous in this case. For example, code that looks like it is assigning a warning value could erase previously-assigned success or failure values. Conversely, code that looks like it is just assigning a success or failure value could erase previously assigned error and warning messages. To prevent potential bugs like this, disable Result::operator= assignment operator. It is possible in the future we may want to re-enable operator= in limited cases (such as when implicit conversions are not used) or add a Replace() or Reset() method that mimicks default operator= behavior. Followup PR bitcoin/bitcoin#25722 also adds a Result::Update() method providing another way to update an existing Result object. Co-authored-by: stickies-v <[email protected]>
1 parent 2eff198 commit 834f65e

File tree

7 files changed

+40
-28
lines changed

7 files changed

+40
-28
lines changed

src/init.cpp

+2-4
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

+6-5
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

+10-6
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

+10
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,23 @@ class Result
3939

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

42+
//! Disallow operator= to avoid confusion in the future when the Result
43+
//! class gains support for richer error reporting, and callers should have
44+
//! ability to set a new result value without clearing existing error
45+
//! messages.
46+
Result& operator=(const Result&) = delete;
47+
Result& operator=(Result&&) = delete;
48+
4249
template <typename FT>
4350
friend bilingual_str ErrorString(const Result<FT>& result);
4451

4552
public:
4653
Result() : m_variant{std::in_place_index_t<1>{}, std::monostate{}} {} // constructor for void
4754
Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {}
4855
Result(Error error) : m_variant{std::in_place_index_t<0>{}, std::move(error.message)} {}
56+
Result(const Result&) = default;
57+
Result(Result&&) = default;
58+
~Result() = default;
4959

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

src/validation.cpp

+8-5
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/test/fuzz/notifications.cpp

+2-4
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

+2-4
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)