Skip to content

Commit 316afb1

Browse files
author
MacroFake
committed
Merge bitcoin/bitcoin#25218: refactor: introduce generic 'Result' class and connect it to CreateTransaction and GetNewDestination
111ea3a wallet: refactor GetNewDestination, use BResult (furszy) 2235172 send: refactor CreateTransaction flow to return a BResult<CTransactionRef> (furszy) 198fcca wallet: refactor, include 'FeeCalculation' inside 'CreatedTransactionResult' (furszy) 7a45c33 Introduce generic 'Result' class (furszy) Pull request description: Based on a common function signature pattern that we have all around the sources: ```cpp bool doSomething(arg1, arg2, arg3, arg4, &result_obj, &error_string) { // do something... if (error) { error_string = "something bad happened"; return false; } result = goodResult; return true; } ``` Introduced a generic class `BResult` that encapsulate the function boolean result, the result object (in case of having it) and, in case of failure, the string error reason. Obtaining in this way cleaner function signatures and removing boilerplate code: ```cpp BResult<Obj> doSomething(arg1, arg2, arg3, arg4) { // do something... if (error) return "something bad happened"; return goodResult; } ``` Same cleanup applies equally to the function callers' side as well. There is no longer need to add the error string and the result object declarations before calling the function: Before: ```cpp Obj result_obj; std::string error_string; if (!doSomething(arg1, arg2, arg3, arg4, result_obj, error_string)) { LogPrintf("Error: %s", error_string); } return result_obj; ``` Now: ```cpp BResult<Obj> op_res = doSomething(arg1, arg2, arg3, arg4); if (!op_res) { LogPrintf("Error: %s", op_res.GetError()); } return op_res.GetObjResult(); ``` ### Initial Implementation: Have connected this new concept to two different flows for now: 1) The `CreateTransaction` flow. --> 7ba2b87c 2) The `GetNewDestination` flow. --> bcee0912 Happy note: even when introduced a new class into the sources, the amount of lines removed is almost equal to added ones :). Extra note: this work is an extended version (and a decoupling) of the work that is inside #24845 (which does not contain the `GetNewDestination` changes nor the inclusion of the `FeeCalculation` field inside `CreatedTransactionResult`). ACKs for top commit: achow101: ACK 111ea3a w0xlt: reACK bitcoin/bitcoin@111ea3a theStack: re-ACK 111ea3a MarcoFalke: review ACK 111ea3a 🎏 Tree-SHA512: 6d84d901a4cb923727067f25ff64542a40edd1ea84fdeac092312ac684c34e3688a52ac5eb012717d2b73f4cb742b9d78e458eb0e9cb9d6d72a916395be91f69
2 parents 27a4dd0 + 111ea3a commit 316afb1

21 files changed

+201
-193
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ BITCOIN_CORE_H = \
277277
util/overloaded.h \
278278
util/rbf.h \
279279
util/readwritefile.h \
280+
util/result.h \
280281
util/serfloat.h \
281282
util/settings.h \
282283
util/sock.h \

src/bench/wallet_loading.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,11 @@ static void BenchUnloadWallet(std::shared_ptr<CWallet>&& wallet)
4747

4848
static void AddTx(CWallet& wallet)
4949
{
50-
bilingual_str error;
51-
CTxDestination dest;
52-
wallet.GetNewDestination(OutputType::BECH32, "", dest, error);
50+
const auto& dest = wallet.GetNewDestination(OutputType::BECH32, "");
51+
assert(dest.HasRes());
5352

5453
CMutableTransaction mtx;
55-
mtx.vout.push_back({COIN, GetScriptForDestination(dest)});
54+
mtx.vout.push_back({COIN, GetScriptForDestination(dest.GetObj())});
5655
mtx.vin.push_back(CTxIn());
5756

5857
wallet.AddToWallet(MakeTransactionRef(mtx), TxStateInactive{});

src/interfaces/wallet.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <script/standard.h> // For CTxDestination
1313
#include <support/allocators/secure.h> // For SecureString
1414
#include <util/message.h>
15+
#include <util/result.h>
1516
#include <util/ui_change_type.h>
1617

1718
#include <cstdint>
@@ -87,7 +88,7 @@ class Wallet
8788
virtual std::string getWalletName() = 0;
8889

8990
// Get a new address.
90-
virtual bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) = 0;
91+
virtual BResult<CTxDestination> getNewDestination(const OutputType type, const std::string label) = 0;
9192

9293
//! Get public key.
9394
virtual bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) = 0;
@@ -138,12 +139,11 @@ class Wallet
138139
virtual void listLockedCoins(std::vector<COutPoint>& outputs) = 0;
139140

140141
//! Create transaction.
141-
virtual CTransactionRef createTransaction(const std::vector<wallet::CRecipient>& recipients,
142+
virtual BResult<CTransactionRef> createTransaction(const std::vector<wallet::CRecipient>& recipients,
142143
const wallet::CCoinControl& coin_control,
143144
bool sign,
144145
int& change_pos,
145-
CAmount& fee,
146-
bilingual_str& fail_reason) = 0;
146+
CAmount& fee) = 0;
147147

148148
//! Commit transaction.
149149
virtual void commitTransaction(CTransactionRef tx,

src/qt/addresstablemodel.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -370,23 +370,21 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
370370
else if(type == Receive)
371371
{
372372
// Generate a new address to associate with given label
373-
CTxDestination dest;
374-
if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
375-
{
373+
auto op_dest = walletModel->wallet().getNewDestination(address_type, strLabel);
374+
if (!op_dest) {
376375
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
377-
if(!ctx.isValid())
378-
{
376+
if (!ctx.isValid()) {
379377
// Unlock wallet failed or was cancelled
380378
editStatus = WALLET_UNLOCK_FAILURE;
381379
return QString();
382380
}
383-
if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
384-
{
381+
op_dest = walletModel->wallet().getNewDestination(address_type, strLabel);
382+
if (!op_dest) {
385383
editStatus = KEY_GENERATION_FAILURE;
386384
return QString();
387385
}
388386
}
389-
strAddress = EncodeDestination(dest);
387+
strAddress = EncodeDestination(op_dest.GetObj());
390388
}
391389
else
392390
{

src/qt/walletmodel.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,10 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
204204
{
205205
CAmount nFeeRequired = 0;
206206
int nChangePosRet = -1;
207-
bilingual_str error;
208207

209208
auto& newTx = transaction.getWtx();
210-
newTx = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, error);
209+
const auto& res = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired);
210+
newTx = res ? res.GetObj() : nullptr;
211211
transaction.setTransactionFee(nFeeRequired);
212212
if (fSubtractFeeFromAmount && newTx)
213213
transaction.reassignAmounts(nChangePosRet);
@@ -218,7 +218,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
218218
{
219219
return SendCoinsReturn(AmountWithFeeExceedsBalance);
220220
}
221-
Q_EMIT message(tr("Send Coins"), QString::fromStdString(error.translated),
221+
Q_EMIT message(tr("Send Coins"), QString::fromStdString(res.GetError().translated),
222222
CClientUIInterface::MSG_ERROR);
223223
return TransactionCreationFailed;
224224
}

src/test/util/wallet.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,10 @@ const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqq
2020
std::string getnewaddress(CWallet& w)
2121
{
2222
constexpr auto output_type = OutputType::BECH32;
23-
CTxDestination dest;
24-
bilingual_str error;
25-
if (!w.GetNewDestination(output_type, "", dest, error)) assert(false);
23+
auto op_dest = w.GetNewDestination(output_type, "");
24+
assert(op_dest.HasRes());
2625

27-
return EncodeDestination(dest);
26+
return EncodeDestination(op_dest.GetObj());
2827
}
2928

3029
#endif // ENABLE_WALLET

src/util/result.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or https://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_UTIL_RESULT_H
6+
#define BITCOIN_UTIL_RESULT_H
7+
8+
#include <util/translation.h>
9+
#include <variant>
10+
11+
/*
12+
* 'BResult' is a generic class useful for wrapping a return object
13+
* (in case of success) or propagating the error cause.
14+
*/
15+
template<class T>
16+
class BResult {
17+
private:
18+
std::variant<bilingual_str, T> m_variant;
19+
20+
public:
21+
BResult() : m_variant(Untranslated("")) {}
22+
BResult(const T& _obj) : m_variant(_obj) {}
23+
BResult(const bilingual_str& error) : m_variant(error) {}
24+
25+
/* Whether the function succeeded or not */
26+
bool HasRes() const { return std::holds_alternative<T>(m_variant); }
27+
28+
/* In case of success, the result object */
29+
const T& GetObj() const {
30+
assert(HasRes());
31+
return std::get<T>(m_variant);
32+
}
33+
34+
/* In case of failure, the error cause */
35+
const bilingual_str& GetError() const {
36+
assert(!HasRes());
37+
return std::get<bilingual_str>(m_variant);
38+
}
39+
40+
explicit operator bool() const { return HasRes(); }
41+
};
42+
43+
#endif // BITCOIN_UTIL_RESULT_H

src/wallet/feebumper.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,19 +219,18 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
219219
new_coin_control.m_min_depth = 1;
220220

221221
constexpr int RANDOM_CHANGE_POSITION = -1;
222-
bilingual_str fail_reason;
223-
FeeCalculation fee_calc_out;
224-
std::optional<CreatedTransactionResult> txr = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, fail_reason, new_coin_control, fee_calc_out, false);
225-
if (!txr) {
226-
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
222+
auto res = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, new_coin_control, false);
223+
if (!res) {
224+
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + res.GetError());
227225
return Result::WALLET_ERROR;
228226
}
229227

228+
const auto& txr = res.GetObj();
230229
// Write back new fee if successful
231-
new_fee = txr->fee;
230+
new_fee = txr.fee;
232231

233232
// Write back transaction
234-
mtx = CMutableTransaction(*txr->tx);
233+
mtx = CMutableTransaction(*txr.tx);
235234

236235
return Result::OK;
237236
}

src/wallet/interfaces.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,10 @@ class WalletImpl : public Wallet
146146
void abortRescan() override { m_wallet->AbortRescan(); }
147147
bool backupWallet(const std::string& filename) override { return m_wallet->BackupWallet(filename); }
148148
std::string getWalletName() override { return m_wallet->GetName(); }
149-
bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) override
149+
BResult<CTxDestination> getNewDestination(const OutputType type, const std::string label) override
150150
{
151151
LOCK(m_wallet->cs_wallet);
152-
bilingual_str error;
153-
return m_wallet->GetNewDestination(type, label, dest, error);
152+
return m_wallet->GetNewDestination(type, label);
154153
}
155154
bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) override
156155
{
@@ -250,22 +249,21 @@ class WalletImpl : public Wallet
250249
LOCK(m_wallet->cs_wallet);
251250
return m_wallet->ListLockedCoins(outputs);
252251
}
253-
CTransactionRef createTransaction(const std::vector<CRecipient>& recipients,
252+
BResult<CTransactionRef> createTransaction(const std::vector<CRecipient>& recipients,
254253
const CCoinControl& coin_control,
255254
bool sign,
256255
int& change_pos,
257-
CAmount& fee,
258-
bilingual_str& fail_reason) override
256+
CAmount& fee) override
259257
{
260258
LOCK(m_wallet->cs_wallet);
261-
FeeCalculation fee_calc_out;
262-
std::optional<CreatedTransactionResult> txr = CreateTransaction(*m_wallet, recipients, change_pos,
263-
fail_reason, coin_control, fee_calc_out, sign);
264-
if (!txr) return {};
265-
fee = txr->fee;
266-
change_pos = txr->change_pos;
259+
const auto& res = CreateTransaction(*m_wallet, recipients, change_pos,
260+
coin_control, sign);
261+
if (!res) return res.GetError();
262+
const auto& txr = res.GetObj();
263+
fee = txr.fee;
264+
change_pos = txr.change_pos;
267265

268-
return txr->tx;
266+
return txr.tx;
269267
}
270268
void commitTransaction(CTransactionRef tx,
271269
WalletValueMap value_map,

src/wallet/rpc/addresses.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,12 @@ RPCHelpMan getnewaddress()
5858
output_type = parsed.value();
5959
}
6060

61-
CTxDestination dest;
62-
bilingual_str error;
63-
if (!pwallet->GetNewDestination(output_type, label, dest, error)) {
64-
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error.original);
61+
auto op_dest = pwallet->GetNewDestination(output_type, label);
62+
if (!op_dest) {
63+
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, op_dest.GetError().original);
6564
}
6665

67-
return EncodeDestination(dest);
66+
return EncodeDestination(op_dest.GetObj());
6867
},
6968
};
7069
}
@@ -106,12 +105,11 @@ RPCHelpMan getrawchangeaddress()
106105
output_type = parsed.value();
107106
}
108107

109-
CTxDestination dest;
110-
bilingual_str error;
111-
if (!pwallet->GetNewChangeDestination(output_type, dest, error)) {
112-
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error.original);
108+
auto op_dest = pwallet->GetNewChangeDestination(output_type);
109+
if (!op_dest) {
110+
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, op_dest.GetError().original);
113111
}
114-
return EncodeDestination(dest);
112+
return EncodeDestination(op_dest.GetObj());
115113
},
116114
};
117115
}

0 commit comments

Comments
 (0)