Skip to content

Commit 4c5ceb0

Browse files
committed
wallet: CreateTransaction(): return out-params as (optional) struct
1 parent c9fdaa5 commit 4c5ceb0

File tree

7 files changed

+52
-71
lines changed

7 files changed

+52
-71
lines changed

src/wallet/feebumper.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,21 +218,20 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
218218
// We cannot source new unconfirmed inputs(bip125 rule 2)
219219
new_coin_control.m_min_depth = 1;
220220

221-
CTransactionRef tx_new;
222-
CAmount fee_ret;
223-
int change_pos_in_out = -1; // No requested location for change
221+
constexpr int RANDOM_CHANGE_POSITION = -1;
224222
bilingual_str fail_reason;
225223
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)) {
224+
std::optional<CreatedTransactionResult> txr = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, fail_reason, new_coin_control, fee_calc_out, false);
225+
if (!txr) {
227226
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
228227
return Result::WALLET_ERROR;
229228
}
230229

231230
// Write back new fee if successful
232-
new_fee = fee_ret;
231+
new_fee = txr->fee;
233232

234233
// Write back transaction
235-
mtx = CMutableTransaction(*tx_new);
234+
mtx = CMutableTransaction(*txr->tx);
236235

237236
return Result::OK;
238237
}

src/wallet/interfaces.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,13 +257,14 @@ class WalletImpl : public Wallet
257257
bilingual_str& fail_reason) override
258258
{
259259
LOCK(m_wallet->cs_wallet);
260-
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+
std::optional<CreatedTransactionResult> txr = CreateTransaction(*m_wallet, recipients, change_pos,
262+
fail_reason, coin_control, fee_calc_out, sign);
263+
if (!txr) return {};
264+
fee = txr->fee;
265+
change_pos = txr->change_pos;
266+
267+
return txr->tx;
267268
}
268269
void commitTransaction(CTransactionRef tx,
269270
WalletValueMap value_map,

src/wallet/rpc/spend.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,14 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto
155155
std::shuffle(recipients.begin(), recipients.end(), FastRandomContext());
156156

157157
// Send
158-
CAmount nFeeRequired = 0;
159-
int nChangePosRet = -1;
158+
constexpr int RANDOM_CHANGE_POSITION = -1;
160159
bilingual_str error;
161-
CTransactionRef tx;
162160
FeeCalculation fee_calc_out;
163-
const bool fCreated = CreateTransaction(wallet, recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
164-
if (!fCreated) {
161+
std::optional<CreatedTransactionResult> txr = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, error, coin_control, fee_calc_out, true);
162+
if (!txr) {
165163
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
166164
}
165+
CTransactionRef tx = txr->tx;
167166
wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
168167
if (verbose) {
169168
UniValue entry(UniValue::VOBJ);

src/wallet/spend.cpp

Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -961,69 +961,48 @@ static std::optional<CreatedTransactionResult> CreateTransactionInternal(
961961
return CreatedTransactionResult(tx, nFeeRet, nChangePosInOut);
962962
}
963963

964-
// TODO: also return std::optional<CreatedTransactionResult> here in order
965-
// to eliminate the out parameters and to simplify the function
966-
bool CreateTransaction(
964+
std::optional<CreatedTransactionResult> CreateTransaction(
967965
CWallet& wallet,
968966
const std::vector<CRecipient>& vecSend,
969-
CTransactionRef& tx,
970-
CAmount& nFeeRet,
971-
int& nChangePosInOut,
967+
int change_pos,
972968
bilingual_str& error,
973969
const CCoinControl& coin_control,
974970
FeeCalculation& fee_calc_out,
975971
bool sign)
976972
{
977973
if (vecSend.empty()) {
978974
error = _("Transaction must have at least one recipient");
979-
return false;
975+
return std::nullopt;
980976
}
981977

982978
if (std::any_of(vecSend.cbegin(), vecSend.cend(), [](const auto& recipient){ return recipient.nAmount < 0; })) {
983979
error = _("Transaction amounts must not be negative");
984-
return false;
980+
return std::nullopt;
985981
}
986982

987983
LOCK(wallet.cs_wallet);
988984

989-
int nChangePosIn = nChangePosInOut;
990-
Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
991-
std::optional<CreatedTransactionResult> txr_ungrouped = CreateTransactionInternal(wallet, vecSend, nChangePosInOut, error, coin_control, fee_calc_out, sign);
992-
bool res = false;
993-
if (txr_ungrouped) {
994-
tx = txr_ungrouped->tx;
995-
nFeeRet = txr_ungrouped->fee;
996-
nChangePosInOut = txr_ungrouped->change_pos;
997-
res = true;
998-
}
999-
TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), res, nFeeRet, nChangePosInOut);
985+
std::optional<CreatedTransactionResult> txr_ungrouped = CreateTransactionInternal(wallet, vecSend, change_pos, error, coin_control, fee_calc_out, sign);
986+
TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), txr_ungrouped.has_value(),
987+
txr_ungrouped.has_value() ? txr_ungrouped->fee : 0, txr_ungrouped.has_value() ? txr_ungrouped->change_pos : 0);
988+
if (!txr_ungrouped) return std::nullopt;
1000989
// try with avoidpartialspends unless it's enabled already
1001-
if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
990+
if (txr_ungrouped->fee > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
1002991
TRACE1(coin_selection, attempting_aps_create_tx, wallet.GetName().c_str());
1003992
CCoinControl tmp_cc = coin_control;
1004993
tmp_cc.m_avoid_partial_spends = true;
1005-
CAmount nFeeRet2;
1006-
CTransactionRef tx2;
1007-
int nChangePosInOut2 = nChangePosIn;
1008994
bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
1009-
std::optional<CreatedTransactionResult> txr_grouped = CreateTransactionInternal(wallet, vecSend, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign);
995+
std::optional<CreatedTransactionResult> txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, error2, tmp_cc, fee_calc_out, sign);
1010996
if (txr_grouped) {
1011-
tx2 = txr_grouped->tx;
1012-
nFeeRet2 = txr_grouped->fee;
1013-
nChangePosInOut2 = txr_grouped->change_pos;
1014-
1015997
// if fee of this alternative one is within the range of the max fee, we use this one
1016-
const bool use_aps = nFeeRet2 <= nFeeRet + wallet.m_max_aps_fee;
1017-
wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
1018-
TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, res, nFeeRet2, nChangePosInOut2);
1019-
if (use_aps) {
1020-
tx = tx2;
1021-
nFeeRet = nFeeRet2;
1022-
nChangePosInOut = nChangePosInOut2;
1023-
}
998+
const bool use_aps = txr_grouped->fee <= txr_ungrouped->fee + wallet.m_max_aps_fee;
999+
wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n",
1000+
txr_ungrouped->fee, txr_grouped->fee, use_aps ? "grouped" : "non-grouped");
1001+
TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, true, txr_grouped->fee, txr_grouped->change_pos);
1002+
if (use_aps) return txr_grouped;
10241003
}
10251004
}
1026-
return res;
1005+
return txr_ungrouped;
10271006
}
10281007

10291008
bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
@@ -1047,11 +1026,12 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
10471026
// CreateTransaction call and LockCoin calls (when lockUnspents is true).
10481027
LOCK(wallet.cs_wallet);
10491028

1050-
CTransactionRef tx_new;
10511029
FeeCalculation fee_calc_out;
1052-
if (!CreateTransaction(wallet, vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
1053-
return false;
1054-
}
1030+
std::optional<CreatedTransactionResult> txr = CreateTransaction(wallet, vecSend, nChangePosInOut, error, coinControl, fee_calc_out, false);
1031+
if (!txr) return false;
1032+
CTransactionRef tx_new = txr->tx;
1033+
nFeeRet = txr->fee;
1034+
nChangePosInOut = txr->change_pos;
10551035

10561036
if (nChangePosInOut != -1) {
10571037
tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]);

src/wallet/spend.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <wallet/transaction.h>
1111
#include <wallet/wallet.h>
1212

13+
#include <optional>
14+
1315
namespace wallet {
1416
/** Get the marginal bytes if spending the specified output from this transaction.
1517
* use_max_sig indicates whether to use the maximum sized, 72 byte signature when calculating the
@@ -95,9 +97,9 @@ struct CreatedTransactionResult
9597
/**
9698
* Create a new transaction paying the recipients with a set of coins
9799
* selected by SelectCoins(); Also create the change output, when needed
98-
* @note passing nChangePosInOut as -1 will result in setting a random position
100+
* @note passing change_pos as -1 will result in setting a random position
99101
*/
100-
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);
102+
std::optional<CreatedTransactionResult> CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, int change_pos, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
101103

102104
/**
103105
* Insert additional inputs into the transaction by

src/wallet/test/spend_tests.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,20 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
2727
// instead of the miner.
2828
auto check_tx = [&wallet](CAmount leftover_input_amount) {
2929
CRecipient recipient{GetScriptForRawPubKey({}), 50 * COIN - leftover_input_amount, true /* subtract fee */};
30-
CTransactionRef tx;
31-
CAmount fee;
32-
int change_pos = -1;
30+
constexpr int RANDOM_CHANGE_POSITION = -1;
3331
bilingual_str error;
3432
CCoinControl coin_control;
3533
coin_control.m_feerate.emplace(10000);
3634
coin_control.fOverrideFeeRate = true;
3735
// 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
3836
coin_control.m_change_type = OutputType::LEGACY;
3937
FeeCalculation fee_calc;
40-
BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
41-
BOOST_CHECK_EQUAL(tx->vout.size(), 1);
42-
BOOST_CHECK_EQUAL(tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - fee);
43-
BOOST_CHECK_GT(fee, 0);
44-
return fee;
38+
std::optional<CreatedTransactionResult> txr = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, error, coin_control, fee_calc);
39+
BOOST_CHECK(txr.has_value());
40+
BOOST_CHECK_EQUAL(txr->tx->vout.size(), 1);
41+
BOOST_CHECK_EQUAL(txr->tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - txr->fee);
42+
BOOST_CHECK_GT(txr->fee, 0);
43+
return txr->fee;
4544
};
4645

4746
// Send full input amount to recipient, check that only nonzero fee is

src/wallet/test/wallet_tests.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,13 +521,14 @@ class ListCoinsTestingSetup : public TestChain100Setup
521521
CWalletTx& AddTx(CRecipient recipient)
522522
{
523523
CTransactionRef tx;
524-
CAmount fee;
525-
int changePos = -1;
526524
bilingual_str error;
527525
CCoinControl dummy;
528526
FeeCalculation fee_calc_out;
529527
{
530-
BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, changePos, error, dummy, fee_calc_out));
528+
constexpr int RANDOM_CHANGE_POSITION = -1;
529+
std::optional<CreatedTransactionResult> txr = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, error, dummy, fee_calc_out);
530+
BOOST_CHECK(txr.has_value());
531+
tx = txr->tx;
531532
}
532533
wallet->CommitTransaction(tx, {}, {});
533534
CMutableTransaction blocktx;

0 commit comments

Comments
 (0)