Skip to content

Commit 3318ee2

Browse files
committed
send: move wallet CreateTransaction* flow to use OperationResult/CallResult.
1 parent f198d27 commit 3318ee2

File tree

9 files changed

+55
-73
lines changed

9 files changed

+55
-73
lines changed

src/interfaces/wallet.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
1212
#include <script/standard.h> // For CTxDestination
1313
#include <support/allocators/secure.h> // For SecureString
14+
#include <operationresult.h>
1415
#include <util/message.h>
1516
#include <util/ui_change_type.h>
1617

@@ -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 CallResult<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/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+
auto res = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired);
210+
newTx = *res.GetObjResult();
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/wallet/feebumper.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,10 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
221221
CTransactionRef tx_new;
222222
CAmount fee_ret;
223223
int change_pos_in_out = -1; // No requested location for change
224-
bilingual_str fail_reason;
225224
FeeCalculation fee_calc_out;
226-
if (!CreateTransaction(wallet, recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, fee_calc_out, false)) {
227-
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
225+
auto res = CreateTransaction(wallet, recipients, tx_new, fee_ret, change_pos_in_out, new_coin_control, fee_calc_out, false);
226+
if (!res) {
227+
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + res.GetError());
228228
return Result::WALLET_ERROR;
229229
}
230230

src/wallet/interfaces.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -249,21 +249,18 @@ class WalletImpl : public Wallet
249249
LOCK(m_wallet->cs_wallet);
250250
return m_wallet->ListLockedCoins(outputs);
251251
}
252-
CTransactionRef createTransaction(const std::vector<CRecipient>& recipients,
252+
CallResult<CTransactionRef> createTransaction(const std::vector<CRecipient>& recipients,
253253
const CCoinControl& coin_control,
254254
bool sign,
255255
int& change_pos,
256-
CAmount& fee,
257-
bilingual_str& fail_reason) override
256+
CAmount& fee) override
258257
{
259258
LOCK(m_wallet->cs_wallet);
260259
CTransactionRef tx;
261260
FeeCalculation fee_calc_out;
262-
if (!CreateTransaction(*m_wallet, recipients, tx, fee, change_pos,
263-
fail_reason, coin_control, fee_calc_out, sign)) {
264-
return {};
265-
}
266-
return tx;
261+
auto res = CreateTransaction(*m_wallet, recipients, tx, fee, change_pos,
262+
coin_control, fee_calc_out, sign);
263+
return res ? CallResult<CTransactionRef>(tx) : CallResult<CTransactionRef>(res.GetError());
267264
}
268265
void commitTransaction(CTransactionRef tx,
269266
WalletValueMap value_map,

src/wallet/rpc/spend.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,11 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto
157157
// Send
158158
CAmount nFeeRequired = 0;
159159
int nChangePosRet = -1;
160-
bilingual_str error;
161160
CTransactionRef tx;
162161
FeeCalculation fee_calc_out;
163-
const bool fCreated = CreateTransaction(wallet, recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
164-
if (!fCreated) {
165-
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
162+
auto res = CreateTransaction(wallet, recipients, tx, nFeeRequired, nChangePosRet, coin_control, fee_calc_out, true);
163+
if (!res) {
164+
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, res.GetError().original);
166165
}
167166
wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
168167
if (verbose) {

src/wallet/spend.cpp

Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -651,13 +651,12 @@ static void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng
651651
}
652652
}
653653

654-
static bool CreateTransactionInternal(
654+
static OperationResult CreateTransactionInternal(
655655
CWallet& wallet,
656656
const std::vector<CRecipient>& vecSend,
657657
CTransactionRef& tx,
658658
CAmount& nFeeRet,
659659
int& nChangePosInOut,
660-
bilingual_str& error,
661660
const CCoinControl& coin_control,
662661
FeeCalculation& fee_calc_out,
663662
bool sign) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@@ -666,6 +665,7 @@ static bool CreateTransactionInternal(
666665

667666
FastRandomContext rng_fast;
668667
CMutableTransaction txNew; // The resulting transaction that we make
668+
bilingual_str error; // possible error str
669669

670670
CoinSelectionParams coin_selection_params{rng_fast}; // Parameters for coin selection, init with dummy
671671
coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
@@ -736,13 +736,11 @@ static bool CreateTransactionInternal(
736736
// Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
737737
// provided one
738738
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
739-
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
740-
return false;
739+
return ErrorOut(strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB)));
741740
}
742741
if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
743742
// eventually allow a fallback fee
744-
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
745-
return false;
743+
return ErrorOut(_("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."));
746744
}
747745

748746
// Calculate the cost of change
@@ -766,10 +764,8 @@ static bool CreateTransactionInternal(
766764
coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
767765
}
768766

769-
if (IsDust(txout, wallet.chain().relayDustFee()))
770-
{
771-
error = _("Transaction amount too small");
772-
return false;
767+
if (IsDust(txout, wallet.chain().relayDustFee())) {
768+
return ErrorOut(_("Transaction amount too small"));
773769
}
774770
txNew.vout.push_back(txout);
775771
}
@@ -785,8 +781,7 @@ static bool CreateTransactionInternal(
785781
// Choose coins to use
786782
std::optional<SelectionResult> result = SelectCoins(wallet, vAvailableCoins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params);
787783
if (!result) {
788-
error = _("Insufficient funds");
789-
return false;
784+
return ErrorOut(_("Insufficient funds"));
790785
}
791786

792787
// Always make a change output
@@ -799,10 +794,8 @@ static bool CreateTransactionInternal(
799794
// Insert change txn at random position:
800795
nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1);
801796
}
802-
else if ((unsigned int)nChangePosInOut > txNew.vout.size())
803-
{
804-
error = _("Transaction change output index out of range");
805-
return false;
797+
else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
798+
return ErrorOut(_("Transaction change output index out of range"));
806799
}
807800

808801
assert(nChangePosInOut != -1);
@@ -829,8 +822,7 @@ static bool CreateTransactionInternal(
829822
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
830823
int nBytes = tx_sizes.vsize;
831824
if (nBytes == -1) {
832-
error = _("Missing solving data for estimating transaction size");
833-
return false;
825+
return ErrorOut(_("Missing solving data for estimating transaction size"));
834826
}
835827
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
836828

@@ -894,7 +886,7 @@ static bool CreateTransactionInternal(
894886
} else {
895887
error = _("The transaction amount is too small to send after the fee has been deducted");
896888
}
897-
return false;
889+
return ErrorOut(error);
898890
}
899891
}
900892
++i;
@@ -904,12 +896,11 @@ static bool CreateTransactionInternal(
904896

905897
// Give up if change keypool ran out and change is required
906898
if (scriptChange.empty() && nChangePosInOut != -1) {
907-
return false;
899+
return ErrorOut(error);
908900
}
909901

910902
if (sign && !wallet.SignTransaction(txNew)) {
911-
error = _("Signing transaction failed");
912-
return false;
903+
return ErrorOut(_("Signing transaction failed"));
913904
}
914905

915906
// Return the constructed transaction data.
@@ -919,20 +910,17 @@ static bool CreateTransactionInternal(
919910
if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
920911
(!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
921912
{
922-
error = _("Transaction too large");
923-
return false;
913+
return ErrorOut(_("Transaction too large"));
924914
}
925915

926916
if (nFeeRet > wallet.m_default_max_tx_fee) {
927-
error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
928-
return false;
917+
return ErrorOut(TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED));
929918
}
930919

931920
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
932921
// Lastly, ensure this tx will pass the mempool's chain limits
933922
if (!wallet.chain().checkChainLimits(tx)) {
934-
error = _("Transaction has too long of a mempool chain");
935-
return false;
923+
return ErrorOut(_("Transaction has too long of a mempool chain"));
936924
}
937925
}
938926

@@ -949,44 +937,41 @@ static bool CreateTransactionInternal(
949937
feeCalc.est.fail.start, feeCalc.est.fail.end,
950938
(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,
951939
feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
952-
return true;
940+
return {true};
953941
}
954942

955-
bool CreateTransaction(
956-
CWallet& wallet,
957-
const std::vector<CRecipient>& vecSend,
958-
CTransactionRef& tx,
959-
CAmount& nFeeRet,
960-
int& nChangePosInOut,
961-
bilingual_str& error,
962-
const CCoinControl& coin_control,
963-
FeeCalculation& fee_calc_out,
964-
bool sign)
943+
OperationResult CreateTransaction(
944+
CWallet& wallet,
945+
const std::vector<CRecipient>& vecSend,
946+
CTransactionRef& tx,
947+
CAmount& nFeeRet,
948+
int& nChangePosInOut,
949+
const CCoinControl& coin_control,
950+
FeeCalculation& fee_calc_out,
951+
bool sign)
965952
{
966953
if (vecSend.empty()) {
967-
error = _("Transaction must have at least one recipient");
968-
return false;
954+
return ErrorOut(_("Transaction must have at least one recipient"));
969955
}
970956

971957
if (std::any_of(vecSend.cbegin(), vecSend.cend(), [](const auto& recipient){ return recipient.nAmount < 0; })) {
972-
error = _("Transaction amounts must not be negative");
973-
return false;
958+
return ErrorOut(_("Transaction amounts must not be negative"));
974959
}
975960

976961
LOCK(wallet.cs_wallet);
977962

978963
int nChangePosIn = nChangePosInOut;
979964
Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
980-
bool res = CreateTransactionInternal(wallet, vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
965+
auto res = CreateTransactionInternal(wallet, vecSend, tx, nFeeRet, nChangePosInOut, coin_control, fee_calc_out, sign);
981966
// try with avoidpartialspends unless it's enabled already
982967
if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
983968
CCoinControl tmp_cc = coin_control;
984969
tmp_cc.m_avoid_partial_spends = true;
985970
CAmount nFeeRet2;
986971
CTransactionRef tx2;
987972
int nChangePosInOut2 = nChangePosIn;
988-
bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
989-
if (CreateTransactionInternal(wallet, vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
973+
// if an error occurs, we discard the results
974+
if (CreateTransactionInternal(wallet, vecSend, tx2, nFeeRet2, nChangePosInOut2, tmp_cc, fee_calc_out, sign)) {
990975
// if fee of this alternative one is within the range of the max fee, we use this one
991976
const bool use_aps = nFeeRet2 <= nFeeRet + wallet.m_max_aps_fee;
992977
wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
@@ -1023,7 +1008,9 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
10231008

10241009
CTransactionRef tx_new;
10251010
FeeCalculation fee_calc_out;
1026-
if (!CreateTransaction(wallet, vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
1011+
auto res = CreateTransaction(wallet, vecSend, tx_new, nFeeRet, nChangePosInOut, coinControl, fee_calc_out, false);
1012+
if (!res) {
1013+
error = res.GetError();
10271014
return false;
10281015
}
10291016

src/wallet/spend.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BITCOIN_WALLET_SPEND_H
77

88
#include <consensus/amount.h>
9+
#include <operationresult.h>
910
#include <wallet/coinselection.h>
1011
#include <wallet/transaction.h>
1112
#include <wallet/wallet.h>
@@ -87,7 +88,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
8788
* selected by SelectCoins(); Also create the change output, when needed
8889
* @note passing nChangePosInOut as -1 will result in setting a random position
8990
*/
90-
bool CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
91+
OperationResult CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
9192

9293
/**
9394
* Insert additional inputs into the transaction by

src/wallet/test/spend_tests.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,13 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
3030
CTransactionRef tx;
3131
CAmount fee;
3232
int change_pos = -1;
33-
bilingual_str error;
3433
CCoinControl coin_control;
3534
coin_control.m_feerate.emplace(10000);
3635
coin_control.fOverrideFeeRate = true;
3736
// 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
3837
coin_control.m_change_type = OutputType::LEGACY;
3938
FeeCalculation fee_calc;
40-
BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
39+
BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, change_pos, coin_control, fee_calc));
4140
BOOST_CHECK_EQUAL(tx->vout.size(), 1);
4241
BOOST_CHECK_EQUAL(tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - fee);
4342
BOOST_CHECK_GT(fee, 0);

src/wallet/test/wallet_tests.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,11 +523,10 @@ class ListCoinsTestingSetup : public TestChain100Setup
523523
CTransactionRef tx;
524524
CAmount fee;
525525
int changePos = -1;
526-
bilingual_str error;
527526
CCoinControl dummy;
528527
FeeCalculation fee_calc_out;
529528
{
530-
BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, changePos, error, dummy, fee_calc_out));
529+
BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, changePos, dummy, fee_calc_out));
531530
}
532531
wallet->CommitTransaction(tx, {}, {});
533532
CMutableTransaction blocktx;

0 commit comments

Comments
 (0)