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.
This commit is contained in:
furszy 2024-03-22 09:34:04 -03:00
parent e2c3ec9bf4
commit 4c0d4f6f93
No known key found for this signature in database
GPG Key ID: 5DD23CCC686AA623
3 changed files with 19 additions and 25 deletions

View File

@ -40,6 +40,7 @@ namespace node {
enum class TransactionError;
} // namespace node
namespace wallet {
struct CreatedTransactionResult;
class CCoinControl;
class CWallet;
enum class AddressPurpose;
@ -142,11 +143,10 @@ public:
virtual void listLockedCoins(std::vector<COutPoint>& outputs) = 0;
//! Create transaction.
virtual util::Result<CTransactionRef> createTransaction(const std::vector<wallet::CRecipient>& recipients,
virtual util::Result<wallet::CreatedTransactionResult> createTransaction(const std::vector<wallet::CRecipient>& recipients,
const wallet::CCoinControl& coin_control,
bool sign,
int& change_pos,
CAmount& fee) = 0;
std::optional<unsigned int> change_pos) = 0;
//! Commit transaction.
virtual void commitTransaction(CTransactionRef tx,

View File

@ -23,6 +23,7 @@
#include <psbt.h>
#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/types.h>
#include <wallet/wallet.h>
#include <cstdint>
@ -149,6 +150,8 @@ bool WalletModel::validateAddress(const QString& address) const
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
{
transaction.getWtx() = nullptr; // reset tx output
CAmount total = 0;
bool fSubtractFeeFromAmount = false;
QList<SendCoinsRecipient> recipients = transaction.getRecipients();
@ -199,22 +202,21 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
}
try {
CAmount nFeeRequired = 0;
int nChangePosRet = -1;
auto& newTx = transaction.getWtx();
const auto& res = m_wallet->createTransaction(vecSend, coinControl, /*sign=*/!wallet().privateKeysDisabled(), nChangePosRet, nFeeRequired);
newTx = res ? *res : nullptr;
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && newTx)
transaction.reassignAmounts(nChangePosRet);
if (!newTx) {
const auto& res = m_wallet->createTransaction(vecSend, coinControl, /*sign=*/!wallet().privateKeysDisabled(), /*change_pos=*/std::nullopt);
if (!res) {
Q_EMIT message(tr("Send Coins"), QString::fromStdString(util::ErrorString(res).translated),
CClientUIInterface::MSG_ERROR);
CClientUIInterface::MSG_ERROR);
return TransactionCreationFailed;
}
newTx = res->tx;
CAmount nFeeRequired = res->fee;
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && newTx) {
transaction.reassignAmounts(static_cast<int>(res->change_pos.value_or(-1)));
}
// Reject absurdly high fee. (This can never happen because the
// wallet never creates transactions with fee greater than
// m_default_max_tx_fee. This merely a belt-and-suspenders check).

View File

@ -257,21 +257,13 @@ public:
LOCK(m_wallet->cs_wallet);
return m_wallet->ListLockedCoins(outputs);
}
util::Result<CTransactionRef> createTransaction(const std::vector<CRecipient>& recipients,
util::Result<wallet::CreatedTransactionResult> createTransaction(const std::vector<CRecipient>& recipients,
const CCoinControl& coin_control,
bool sign,
int& change_pos,
CAmount& fee) override
std::optional<unsigned int> change_pos) override
{
LOCK(m_wallet->cs_wallet);
auto res = CreateTransaction(*m_wallet, recipients, change_pos == -1 ? std::nullopt : std::make_optional(change_pos),
coin_control, sign);
if (!res) return util::Error{util::ErrorString(res)};
const auto& txr = *res;
fee = txr.fee;
change_pos = txr.change_pos ? int(*txr.change_pos) : -1;
return txr.tx;
return CreateTransaction(*m_wallet, recipients, change_pos, coin_control, sign);
}
void commitTransaction(CTransactionRef tx,
WalletValueMap value_map,