Skip to content

Commit dfbd145

Browse files
committed
refactor: interfaces, make 'createTransaction' less error-prone
Bundle all function's outputs inside the util::Result returned object. Reasons for the refactoring: - The 'change_pos' ref argument has been a source of bugs in the past. - The 'fee' ref argument is currently only set when the transaction creation process succeeds.
1 parent dec14e4 commit dfbd145

File tree

3 files changed

+19
-25
lines changed

3 files changed

+19
-25
lines changed

src/interfaces/wallet.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <utility>
2727
#include <vector>
2828

29+
struct CreatedTransactionResult;
2930
class CFeeRate;
3031
class CKey;
3132
enum class FeeReason;
@@ -142,11 +143,10 @@ class Wallet
142143
virtual void listLockedCoins(std::vector<COutPoint>& outputs) = 0;
143144

144145
//! Create transaction.
145-
virtual util::Result<CTransactionRef> createTransaction(const std::vector<wallet::CRecipient>& recipients,
146+
virtual util::Result<CreatedTransactionResult> createTransaction(const std::vector<wallet::CRecipient>& recipients,
146147
const wallet::CCoinControl& coin_control,
147148
bool sign,
148-
int& change_pos,
149-
CAmount& fee) = 0;
149+
std::optional<unsigned int> change_pos) = 0;
150150

151151
//! Commit transaction.
152152
virtual void commitTransaction(CTransactionRef tx,

src/qt/walletmodel.cpp

+12-11
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <psbt.h>
2323
#include <util/translation.h>
2424
#include <wallet/coincontrol.h>
25+
#include <wallet/util_spend.h>
2526
#include <wallet/wallet.h> // for CRecipient
2627

2728
#include <stdint.h>
@@ -153,6 +154,8 @@ bool WalletModel::validateAddress(const QString& address) const
153154

154155
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
155156
{
157+
transaction.getWtx() = nullptr; // reset tx output
158+
156159
CAmount total = 0;
157160
bool fSubtractFeeFromAmount = false;
158161
QList<SendCoinsRecipient> recipients = transaction.getRecipients();
@@ -204,22 +207,20 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
204207
}
205208

206209
try {
207-
CAmount nFeeRequired = 0;
208-
int nChangePosRet = -1;
209-
210210
auto& newTx = transaction.getWtx();
211-
const auto& res = m_wallet->createTransaction(vecSend, coinControl, /*sign=*/!wallet().privateKeysDisabled(), nChangePosRet, nFeeRequired);
212-
newTx = res ? *res : nullptr;
213-
transaction.setTransactionFee(nFeeRequired);
214-
if (fSubtractFeeFromAmount && newTx)
215-
transaction.reassignAmounts(nChangePosRet);
216-
217-
if (!newTx) {
211+
const auto& res = m_wallet->createTransaction(vecSend, coinControl, /*sign=*/!wallet().privateKeysDisabled(), /*change_pos=*/std::nullopt);
212+
if (!res) {
218213
Q_EMIT message(tr("Send Coins"), QString::fromStdString(util::ErrorString(res).translated),
219-
CClientUIInterface::MSG_ERROR);
214+
CClientUIInterface::MSG_ERROR);
220215
return TransactionCreationFailed;
221216
}
222217

218+
newTx = res->tx;
219+
CAmount nFeeRequired = res->fee;
220+
transaction.setTransactionFee(nFeeRequired);
221+
if (fSubtractFeeFromAmount && newTx)
222+
transaction.reassignAmounts(res->change_pos ? int(*res->change_pos) : -1);
223+
223224
// Reject absurdly high fee. (This can never happen because the
224225
// wallet never creates transactions with fee greater than
225226
// m_default_max_tx_fee. This merely a belt-and-suspenders check).

src/wallet/interfaces.cpp

+4-11
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <wallet/receive.h>
2828
#include <wallet/rpc/wallet.h>
2929
#include <wallet/spend.h>
30+
#include <wallet/util_spend.h>
3031
#include <wallet/wallet.h>
3132

3233
#include <memory>
@@ -274,21 +275,13 @@ class WalletImpl : public Wallet
274275
LOCK(m_wallet->cs_wallet);
275276
return m_wallet->ListLockedCoins(outputs);
276277
}
277-
util::Result<CTransactionRef> createTransaction(const std::vector<CRecipient>& recipients,
278+
util::Result<CreatedTransactionResult> createTransaction(const std::vector<CRecipient>& recipients,
278279
const CCoinControl& coin_control,
279280
bool sign,
280-
int& change_pos,
281-
CAmount& fee) override
281+
std::optional<unsigned int> change_pos) override
282282
{
283283
LOCK(m_wallet->cs_wallet);
284-
auto res = CreateTransaction(*m_wallet, recipients, change_pos == -1 ? std::nullopt : std::make_optional(change_pos),
285-
coin_control, sign);
286-
if (!res) return util::Error{util::ErrorString(res)};
287-
const auto& txr = *res;
288-
fee = txr.fee;
289-
change_pos = txr.change_pos ? *txr.change_pos : -1;
290-
291-
return txr.tx;
284+
return CreateTransaction(*m_wallet, recipients, change_pos, coin_control, sign);
292285
}
293286
void commitTransaction(CTransactionRef tx,
294287
WalletValueMap value_map,

0 commit comments

Comments
 (0)