Skip to content

Commit 758501b

Browse files
committed
wallet: use optional for change position as an optional in CreateTransaction
Instead of making -1 a magic number meaning no change or random change position, use an optional to have that meaning.
1 parent 2d39db7 commit 758501b

File tree

7 files changed

+28
-36
lines changed

7 files changed

+28
-36
lines changed

src/wallet/feebumper.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
316316
// We cannot source new unconfirmed inputs(bip125 rule 2)
317317
new_coin_control.m_min_depth = 1;
318318

319-
constexpr int RANDOM_CHANGE_POSITION = -1;
320-
auto res = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, new_coin_control, false);
319+
auto res = CreateTransaction(wallet, recipients, std::nullopt, new_coin_control, false);
321320
if (!res) {
322321
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + util::ErrorString(res));
323322
return Result::WALLET_ERROR;

src/wallet/interfaces.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,12 @@ class WalletImpl : public Wallet
281281
CAmount& fee) override
282282
{
283283
LOCK(m_wallet->cs_wallet);
284-
auto res = CreateTransaction(*m_wallet, recipients, change_pos,
284+
auto res = CreateTransaction(*m_wallet, recipients, change_pos == -1 ? std::nullopt : std::make_optional(change_pos),
285285
coin_control, sign);
286286
if (!res) return util::Error{util::ErrorString(res)};
287287
const auto& txr = *res;
288288
fee = txr.fee;
289-
change_pos = txr.change_pos;
289+
change_pos = txr.change_pos ? *txr.change_pos : -1;
290290

291291
return txr.tx;
292292
}

src/wallet/rpc/spend.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,7 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto
155155
std::shuffle(recipients.begin(), recipients.end(), FastRandomContext());
156156

157157
// Send
158-
constexpr int RANDOM_CHANGE_POSITION = -1;
159-
auto res = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, coin_control, true);
158+
auto res = CreateTransaction(wallet, recipients, std::nullopt, coin_control, true);
160159
if (!res) {
161160
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, util::ErrorString(res).original);
162161
}

src/wallet/spend.cpp

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -965,15 +965,12 @@ static void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng
965965
static util::Result<CreatedTransactionResult> CreateTransactionInternal(
966966
CWallet& wallet,
967967
const std::vector<CRecipient>& vecSend,
968-
int change_pos,
968+
std::optional<unsigned int> change_pos,
969969
const CCoinControl& coin_control,
970970
bool sign) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
971971
{
972972
AssertLockHeld(wallet.cs_wallet);
973973

974-
// out variables, to be packed into returned result structure
975-
int nChangePosInOut = change_pos;
976-
977974
FastRandomContext rng_fast;
978975
CMutableTransaction txNew; // The resulting transaction that we make
979976

@@ -1132,15 +1129,15 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
11321129
const CAmount change_amount = result.GetChange(coin_selection_params.min_viable_change, coin_selection_params.m_change_fee);
11331130
if (change_amount > 0) {
11341131
CTxOut newTxOut(change_amount, scriptChange);
1135-
if (nChangePosInOut == -1) {
1132+
if (!change_pos) {
11361133
// Insert change txn at random position:
1137-
nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1);
1138-
} else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
1134+
change_pos = rng_fast.randrange(txNew.vout.size() + 1);
1135+
} else if ((unsigned int)*change_pos > txNew.vout.size()) {
11391136
return util::Error{_("Transaction change output index out of range")};
11401137
}
1141-
txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
1138+
txNew.vout.insert(txNew.vout.begin() + *change_pos, newTxOut);
11421139
} else {
1143-
nChangePosInOut = -1;
1140+
change_pos = std::nullopt;
11441141
}
11451142

11461143
// Shuffle selected coins and fill in final vin
@@ -1217,8 +1214,8 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
12171214
}
12181215

12191216
// If there is a change output and we overpay the fees then increase the change to match the fee needed
1220-
if (nChangePosInOut != -1 && fee_needed < current_fee) {
1221-
auto& change = txNew.vout.at(nChangePosInOut);
1217+
if (change_pos && fee_needed < current_fee) {
1218+
auto& change = txNew.vout.at(*change_pos);
12221219
change.nValue += current_fee - fee_needed;
12231220
current_fee = result.GetSelectedValue() - CalculateOutputValue(txNew);
12241221
if (fee_needed != current_fee) {
@@ -1229,11 +1226,11 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
12291226
// Reduce output values for subtractFeeFromAmount
12301227
if (coin_selection_params.m_subtract_fee_outputs) {
12311228
CAmount to_reduce = fee_needed - current_fee;
1232-
int i = 0;
1229+
unsigned int i = 0;
12331230
bool fFirst = true;
12341231
for (const auto& recipient : vecSend)
12351232
{
1236-
if (i == nChangePosInOut) {
1233+
if (change_pos && i == *change_pos) {
12371234
++i;
12381235
}
12391236
CTxOut& txout = txNew.vout[i];
@@ -1272,7 +1269,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
12721269
}
12731270

12741271
// Give up if change keypool ran out and change is required
1275-
if (scriptChange.empty() && nChangePosInOut != -1) {
1272+
if (scriptChange.empty() && change_pos) {
12761273
return util::Error{error};
12771274
}
12781275

@@ -1313,13 +1310,13 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
13131310
feeCalc.est.fail.start, feeCalc.est.fail.end,
13141311
(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,
13151312
feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
1316-
return CreatedTransactionResult(tx, current_fee, nChangePosInOut, feeCalc);
1313+
return CreatedTransactionResult(tx, current_fee, change_pos, feeCalc);
13171314
}
13181315

13191316
util::Result<CreatedTransactionResult> CreateTransaction(
13201317
CWallet& wallet,
13211318
const std::vector<CRecipient>& vecSend,
1322-
int change_pos,
1319+
std::optional<unsigned int> change_pos,
13231320
const CCoinControl& coin_control,
13241321
bool sign)
13251322
{
@@ -1335,7 +1332,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
13351332

13361333
auto res = CreateTransactionInternal(wallet, vecSend, change_pos, coin_control, sign);
13371334
TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), bool(res),
1338-
res ? res->fee : 0, res ? res->change_pos : 0);
1335+
res ? res->fee : 0, res && res->change_pos.has_value() ? *res->change_pos : 0);
13391336
if (!res) return res;
13401337
const auto& txr_ungrouped = *res;
13411338
// try with avoidpartialspends unless it's enabled already
@@ -1345,16 +1342,15 @@ util::Result<CreatedTransactionResult> CreateTransaction(
13451342
tmp_cc.m_avoid_partial_spends = true;
13461343

13471344
// Reuse the change destination from the first creation attempt to avoid skipping BIP44 indexes
1348-
const int ungrouped_change_pos = txr_ungrouped.change_pos;
1349-
if (ungrouped_change_pos != -1) {
1350-
ExtractDestination(txr_ungrouped.tx->vout[ungrouped_change_pos].scriptPubKey, tmp_cc.destChange);
1345+
if (txr_ungrouped.change_pos) {
1346+
ExtractDestination(txr_ungrouped.tx->vout[*txr_ungrouped.change_pos].scriptPubKey, tmp_cc.destChange);
13511347
}
13521348

13531349
auto txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, tmp_cc, sign);
13541350
// if fee of this alternative one is within the range of the max fee, we use this one
13551351
const bool use_aps{txr_grouped.has_value() ? (txr_grouped->fee <= txr_ungrouped.fee + wallet.m_max_aps_fee) : false};
13561352
TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, txr_grouped.has_value(),
1357-
txr_grouped.has_value() ? txr_grouped->fee : 0, txr_grouped.has_value() ? txr_grouped->change_pos : 0);
1353+
txr_grouped.has_value() ? txr_grouped->fee : 0, txr_grouped.has_value() && txr_grouped->change_pos.has_value() ? *txr_grouped->change_pos : 0);
13581354
if (txr_grouped) {
13591355
wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n",
13601356
txr_ungrouped.fee, txr_grouped->fee, use_aps ? "grouped" : "non-grouped");
@@ -1412,15 +1408,15 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
14121408
preset_txin.SetScriptWitness(txin.scriptWitness);
14131409
}
14141410

1415-
auto res = CreateTransaction(wallet, vecSend, nChangePosInOut, coinControl, false);
1411+
auto res = CreateTransaction(wallet, vecSend, nChangePosInOut == -1 ? std::nullopt : std::optional<unsigned int>((unsigned int)nChangePosInOut), coinControl, false);
14161412
if (!res) {
14171413
error = util::ErrorString(res);
14181414
return false;
14191415
}
14201416
const auto& txr = *res;
14211417
CTransactionRef tx_new = txr.tx;
14221418
nFeeRet = txr.fee;
1423-
nChangePosInOut = txr.change_pos;
1419+
nChangePosInOut = txr.change_pos ? *txr.change_pos : -1;
14241420

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

src/wallet/spend.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,9 @@ struct CreatedTransactionResult
207207
CTransactionRef tx;
208208
CAmount fee;
209209
FeeCalculation fee_calc;
210-
int change_pos;
210+
std::optional<unsigned int> change_pos;
211211

212-
CreatedTransactionResult(CTransactionRef _tx, CAmount _fee, int _change_pos, const FeeCalculation& _fee_calc)
212+
CreatedTransactionResult(CTransactionRef _tx, CAmount _fee, std::optional<unsigned int> _change_pos, const FeeCalculation& _fee_calc)
213213
: tx(_tx), fee(_fee), fee_calc(_fee_calc), change_pos(_change_pos) {}
214214
};
215215

@@ -218,7 +218,7 @@ struct CreatedTransactionResult
218218
* selected by SelectCoins(); Also create the change output, when needed
219219
* @note passing change_pos as -1 will result in setting a random position
220220
*/
221-
util::Result<CreatedTransactionResult> CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, int change_pos, const CCoinControl& coin_control, bool sign = true);
221+
util::Result<CreatedTransactionResult> CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, std::optional<unsigned int> change_pos, const CCoinControl& coin_control, bool sign = true);
222222

223223
/**
224224
* 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
@@ -28,13 +28,12 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
2828
// instead of the miner.
2929
auto check_tx = [&wallet](CAmount leftover_input_amount) {
3030
CRecipient recipient{PubKeyDestination({}), 50 * COIN - leftover_input_amount, /*subtract_fee=*/true};
31-
constexpr int RANDOM_CHANGE_POSITION = -1;
3231
CCoinControl coin_control;
3332
coin_control.m_feerate.emplace(10000);
3433
coin_control.fOverrideFeeRate = true;
3534
// 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
3635
coin_control.m_change_type = OutputType::LEGACY;
37-
auto res = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, coin_control);
36+
auto res = CreateTransaction(*wallet, {recipient}, std::nullopt, coin_control);
3837
BOOST_CHECK(res);
3938
const auto& txr = *res;
4039
BOOST_CHECK_EQUAL(txr.tx->vout.size(), 1);

src/wallet/test/wallet_tests.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,8 +558,7 @@ class ListCoinsTestingSetup : public TestChain100Setup
558558
CTransactionRef tx;
559559
CCoinControl dummy;
560560
{
561-
constexpr int RANDOM_CHANGE_POSITION = -1;
562-
auto res = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, dummy);
561+
auto res = CreateTransaction(*wallet, {recipient}, std::nullopt, dummy);
563562
BOOST_CHECK(res);
564563
tx = res->tx;
565564
}

0 commit comments

Comments
 (0)