From 7a45c33d1f8a758850cf8e7bd6ad508939ba5c0d Mon Sep 17 00:00:00 2001 From: furszy Date: Fri, 8 Apr 2022 16:24:46 -0300 Subject: Introduce generic 'Result' class Useful to encapsulate the function result object (in case of having it) or, in case of failure, the failure reason. This let us clean lot of boilerplate code, as now instead of returning a boolean and having to add a ref arg for the return object and another ref for the error string. We can simply return a 'BResult'. Example of what we currently have: ``` bool doSomething(arg1, arg2, arg3, arg4, &result, &error_string) { do something... if (error) { error_string = "something bad happened"; return false; } result = goodResult; return true; } ``` Example of what we will get with this commit: ``` BResult doSomething(arg1, arg2, arg3, arg4) { do something... if (error) return {"something happened"}; // good return {goodResult}; } ``` This allows a similar boilerplate cleanup on the function callers side as well. They don't have to add the extra pre-function-call error string and result object declarations to pass the references to the function. --- src/Makefile.am | 1 + src/util/result.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/util/result.h diff --git a/src/Makefile.am b/src/Makefile.am index 3fbbe180fc..a9e9db0a7d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -277,6 +277,7 @@ BITCOIN_CORE_H = \ util/overloaded.h \ util/rbf.h \ util/readwritefile.h \ + util/result.h \ util/serfloat.h \ util/settings.h \ util/sock.h \ diff --git a/src/util/result.h b/src/util/result.h new file mode 100644 index 0000000000..dcf5edaa5b --- /dev/null +++ b/src/util/result.h @@ -0,0 +1,43 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_RESULT_H +#define BITCOIN_UTIL_RESULT_H + +#include +#include + +/* + * 'BResult' is a generic class useful for wrapping a return object + * (in case of success) or propagating the error cause. +*/ +template +class BResult { +private: + std::variant m_variant; + +public: + BResult() : m_variant(Untranslated("")) {} + BResult(const T& _obj) : m_variant(_obj) {} + BResult(const bilingual_str& error) : m_variant(error) {} + + /* Whether the function succeeded or not */ + bool HasRes() const { return std::holds_alternative(m_variant); } + + /* In case of success, the result object */ + const T& GetObj() const { + assert(HasRes()); + return std::get(m_variant); + } + + /* In case of failure, the error cause */ + const bilingual_str& GetError() const { + assert(!HasRes()); + return std::get(m_variant); + } + + explicit operator bool() const { return HasRes(); } +}; + +#endif // BITCOIN_UTIL_RESULT_H -- cgit v1.2.3 From 198fcca162f4d2f877feab485e629ff89818ff56 Mon Sep 17 00:00:00 2001 From: furszy Date: Tue, 24 May 2022 12:29:51 -0300 Subject: wallet: refactor, include 'FeeCalculation' inside 'CreatedTransactionResult' --- src/wallet/feebumper.cpp | 3 +-- src/wallet/interfaces.cpp | 3 +-- src/wallet/rpc/spend.cpp | 5 ++--- src/wallet/spend.cpp | 12 ++++-------- src/wallet/spend.h | 8 +++++--- src/wallet/test/spend_tests.cpp | 3 +-- src/wallet/test/wallet_tests.cpp | 5 +---- src/wallet/wallet.h | 1 - 8 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 5e70ed4a30..9bd7e62c28 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -220,8 +220,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo constexpr int RANDOM_CHANGE_POSITION = -1; bilingual_str fail_reason; - FeeCalculation fee_calc_out; - std::optional txr = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, fail_reason, new_coin_control, fee_calc_out, false); + std::optional txr = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, fail_reason, new_coin_control, false); if (!txr) { errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason); return Result::WALLET_ERROR; diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 327276f2ed..cec103b02b 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -258,9 +258,8 @@ public: bilingual_str& fail_reason) override { LOCK(m_wallet->cs_wallet); - FeeCalculation fee_calc_out; std::optional txr = CreateTransaction(*m_wallet, recipients, change_pos, - fail_reason, coin_control, fee_calc_out, sign); + fail_reason, coin_control, sign); if (!txr) return {}; fee = txr->fee; change_pos = txr->change_pos; diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index 0e8cee5db8..362e9113fb 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -157,8 +157,7 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto // Send constexpr int RANDOM_CHANGE_POSITION = -1; bilingual_str error; - FeeCalculation fee_calc_out; - std::optional txr = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, error, coin_control, fee_calc_out, true); + std::optional txr = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, error, coin_control, true); if (!txr) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original); } @@ -167,7 +166,7 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto if (verbose) { UniValue entry(UniValue::VOBJ); entry.pushKV("txid", tx->GetHash().GetHex()); - entry.pushKV("fee_reason", StringForFeeReason(fee_calc_out.reason)); + entry.pushKV("fee_reason", StringForFeeReason(txr->fee_calc.reason)); return entry; } return tx->GetHash().GetHex(); diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index 1d22d0993e..9846335da7 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -662,7 +662,6 @@ static std::optional CreateTransactionInternal( int change_pos, bilingual_str& error, const CCoinControl& coin_control, - FeeCalculation& fee_calc_out, bool sign) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) { AssertLockHeld(wallet.cs_wallet); @@ -954,7 +953,6 @@ static std::optional CreateTransactionInternal( // Before we return success, we assume any change key will be used to prevent // accidental re-use. reservedest.KeepDestination(); - fee_calc_out = feeCalc; wallet.WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, @@ -964,7 +962,7 @@ static std::optional CreateTransactionInternal( feeCalc.est.fail.start, feeCalc.est.fail.end, (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0, feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool); - return CreatedTransactionResult(tx, nFeeRet, nChangePosInOut); + return CreatedTransactionResult(tx, nFeeRet, nChangePosInOut, feeCalc); } std::optional CreateTransaction( @@ -973,7 +971,6 @@ std::optional CreateTransaction( int change_pos, bilingual_str& error, const CCoinControl& coin_control, - FeeCalculation& fee_calc_out, bool sign) { if (vecSend.empty()) { @@ -988,7 +985,7 @@ std::optional CreateTransaction( LOCK(wallet.cs_wallet); - std::optional txr_ungrouped = CreateTransactionInternal(wallet, vecSend, change_pos, error, coin_control, fee_calc_out, sign); + std::optional txr_ungrouped = CreateTransactionInternal(wallet, vecSend, change_pos, error, coin_control, sign); TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), txr_ungrouped.has_value(), txr_ungrouped.has_value() ? txr_ungrouped->fee : 0, txr_ungrouped.has_value() ? txr_ungrouped->change_pos : 0); if (!txr_ungrouped) return std::nullopt; @@ -998,7 +995,7 @@ std::optional CreateTransaction( CCoinControl tmp_cc = coin_control; tmp_cc.m_avoid_partial_spends = true; bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results - std::optional txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, error2, tmp_cc, fee_calc_out, sign); + std::optional txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, error2, tmp_cc, sign); // if fee of this alternative one is within the range of the max fee, we use this one const bool use_aps{txr_grouped.has_value() ? (txr_grouped->fee <= txr_ungrouped->fee + wallet.m_max_aps_fee) : false}; TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, txr_grouped.has_value(), @@ -1045,8 +1042,7 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, } } - FeeCalculation fee_calc_out; - std::optional txr = CreateTransaction(wallet, vecSend, nChangePosInOut, error, coinControl, fee_calc_out, false); + std::optional txr = CreateTransaction(wallet, vecSend, nChangePosInOut, error, coinControl, false); if (!txr) return false; CTransactionRef tx_new = txr->tx; nFeeRet = txr->fee; diff --git a/src/wallet/spend.h b/src/wallet/spend.h index cba42d6fae..ecf9b695ee 100644 --- a/src/wallet/spend.h +++ b/src/wallet/spend.h @@ -6,6 +6,7 @@ #define BITCOIN_WALLET_SPEND_H #include +#include // for FeeCalculation #include #include #include @@ -107,10 +108,11 @@ struct CreatedTransactionResult { CTransactionRef tx; CAmount fee; + FeeCalculation fee_calc; int change_pos; - CreatedTransactionResult(CTransactionRef tx, CAmount fee, int change_pos) - : tx(tx), fee(fee), change_pos(change_pos) {} + CreatedTransactionResult(CTransactionRef _tx, CAmount _fee, int _change_pos, const FeeCalculation& _fee_calc) + : tx(_tx), fee(_fee), fee_calc(_fee_calc), change_pos(_change_pos) {} }; /** @@ -118,7 +120,7 @@ struct CreatedTransactionResult * selected by SelectCoins(); Also create the change output, when needed * @note passing change_pos as -1 will result in setting a random position */ -std::optional CreateTransaction(CWallet& wallet, const std::vector& vecSend, int change_pos, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true); +std::optional CreateTransaction(CWallet& wallet, const std::vector& vecSend, int change_pos, bilingual_str& error, const CCoinControl& coin_control, bool sign = true); /** * Insert additional inputs into the transaction by diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp index bdc148afb4..435c139170 100644 --- a/src/wallet/test/spend_tests.cpp +++ b/src/wallet/test/spend_tests.cpp @@ -34,8 +34,7 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup) coin_control.fOverrideFeeRate = true; // We need to use a change type with high cost of change so that the leftover amount will be dropped to fee instead of added as a change output coin_control.m_change_type = OutputType::LEGACY; - FeeCalculation fee_calc; - std::optional txr = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, error, coin_control, fee_calc); + std::optional txr = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, error, coin_control); BOOST_CHECK(txr.has_value()); BOOST_CHECK_EQUAL(txr->tx->vout.size(), 1); BOOST_CHECK_EQUAL(txr->tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - txr->fee); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 0c03b2f4a2..9ed840031d 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -4,7 +4,6 @@ #include -#include #include #include #include @@ -13,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -523,10 +521,9 @@ public: CTransactionRef tx; bilingual_str error; CCoinControl dummy; - FeeCalculation fee_calc_out; { constexpr int RANDOM_CHANGE_POSITION = -1; - std::optional txr = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, error, dummy, fee_calc_out); + std::optional txr = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, error, dummy); BOOST_CHECK(txr.has_value()); tx = txr->tx; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6b12752c34..0ebc18ca8b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -45,7 +45,6 @@ using LoadWalletFn = std::function wall class CScript; enum class FeeEstimateMode; -struct FeeCalculation; struct bilingual_str; namespace wallet { -- cgit v1.2.3 From 22351725bc4c5eb63ee45f607374bbf2d76e2b8c Mon Sep 17 00:00:00 2001 From: furszy Date: Fri, 8 Apr 2022 16:43:10 -0300 Subject: send: refactor CreateTransaction flow to return a BResult --- src/interfaces/wallet.h | 6 +-- src/qt/walletmodel.cpp | 6 +-- src/wallet/feebumper.cpp | 12 ++--- src/wallet/interfaces.cpp | 18 ++++---- src/wallet/rpc/spend.cpp | 11 +++-- src/wallet/spend.cpp | 97 ++++++++++++++++++---------------------- src/wallet/spend.h | 3 +- src/wallet/test/spend_tests.cpp | 14 +++--- src/wallet/test/wallet_tests.cpp | 7 ++- 9 files changed, 81 insertions(+), 93 deletions(-) diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index b3cb0ae387..00d238f186 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -12,6 +12,7 @@ #include