@@ -965,15 +965,12 @@ static void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng
965
965
static util::Result<CreatedTransactionResult> CreateTransactionInternal (
966
966
CWallet& wallet,
967
967
const std::vector<CRecipient>& vecSend,
968
- int change_pos,
968
+ std::optional< unsigned int > change_pos,
969
969
const CCoinControl& coin_control,
970
970
bool sign) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
971
971
{
972
972
AssertLockHeld (wallet.cs_wallet );
973
973
974
- // out variables, to be packed into returned result structure
975
- int nChangePosInOut = change_pos;
976
-
977
974
FastRandomContext rng_fast;
978
975
CMutableTransaction txNew; // The resulting transaction that we make
979
976
@@ -1132,15 +1129,15 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1132
1129
const CAmount change_amount = result.GetChange (coin_selection_params.min_viable_change , coin_selection_params.m_change_fee );
1133
1130
if (change_amount > 0 ) {
1134
1131
CTxOut newTxOut (change_amount, scriptChange);
1135
- if (nChangePosInOut == - 1 ) {
1132
+ if (!change_pos ) {
1136
1133
// 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 ()) {
1139
1136
return util::Error{_ (" Transaction change output index out of range" )};
1140
1137
}
1141
- txNew.vout .insert (txNew.vout .begin () + nChangePosInOut , newTxOut);
1138
+ txNew.vout .insert (txNew.vout .begin () + *change_pos , newTxOut);
1142
1139
} else {
1143
- nChangePosInOut = - 1 ;
1140
+ change_pos = std::nullopt ;
1144
1141
}
1145
1142
1146
1143
// Shuffle selected coins and fill in final vin
@@ -1217,8 +1214,8 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1217
1214
}
1218
1215
1219
1216
// 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 );
1222
1219
change.nValue += current_fee - fee_needed;
1223
1220
current_fee = result.GetSelectedValue () - CalculateOutputValue (txNew);
1224
1221
if (fee_needed != current_fee) {
@@ -1229,11 +1226,11 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1229
1226
// Reduce output values for subtractFeeFromAmount
1230
1227
if (coin_selection_params.m_subtract_fee_outputs ) {
1231
1228
CAmount to_reduce = fee_needed - current_fee;
1232
- int i = 0 ;
1229
+ unsigned int i = 0 ;
1233
1230
bool fFirst = true ;
1234
1231
for (const auto & recipient : vecSend)
1235
1232
{
1236
- if (i == nChangePosInOut ) {
1233
+ if (change_pos && i == *change_pos ) {
1237
1234
++i;
1238
1235
}
1239
1236
CTxOut& txout = txNew.vout [i];
@@ -1272,7 +1269,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1272
1269
}
1273
1270
1274
1271
// Give up if change keypool ran out and change is required
1275
- if (scriptChange.empty () && nChangePosInOut != - 1 ) {
1272
+ if (scriptChange.empty () && change_pos ) {
1276
1273
return util::Error{error};
1277
1274
}
1278
1275
@@ -1313,13 +1310,13 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1313
1310
feeCalc.est .fail .start , feeCalc.est .fail .end ,
1314
1311
(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 ,
1315
1312
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);
1317
1314
}
1318
1315
1319
1316
util::Result<CreatedTransactionResult> CreateTransaction (
1320
1317
CWallet& wallet,
1321
1318
const std::vector<CRecipient>& vecSend,
1322
- int change_pos,
1319
+ std::optional< unsigned int > change_pos,
1323
1320
const CCoinControl& coin_control,
1324
1321
bool sign)
1325
1322
{
@@ -1335,7 +1332,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
1335
1332
1336
1333
auto res = CreateTransactionInternal (wallet, vecSend, change_pos, coin_control, sign);
1337
1334
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 );
1339
1336
if (!res) return res;
1340
1337
const auto & txr_ungrouped = *res;
1341
1338
// try with avoidpartialspends unless it's enabled already
@@ -1345,16 +1342,15 @@ util::Result<CreatedTransactionResult> CreateTransaction(
1345
1342
tmp_cc.m_avoid_partial_spends = true ;
1346
1343
1347
1344
// 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 );
1351
1347
}
1352
1348
1353
1349
auto txr_grouped = CreateTransactionInternal (wallet, vecSend, change_pos, tmp_cc, sign);
1354
1350
// if fee of this alternative one is within the range of the max fee, we use this one
1355
1351
const bool use_aps{txr_grouped.has_value () ? (txr_grouped->fee <= txr_ungrouped.fee + wallet.m_max_aps_fee ) : false };
1356
1352
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 );
1358
1354
if (txr_grouped) {
1359
1355
wallet.WalletLogPrintf (" Fee non-grouped = %lld, grouped = %lld, using %s\n " ,
1360
1356
txr_ungrouped.fee , txr_grouped->fee , use_aps ? " grouped" : " non-grouped" );
@@ -1412,15 +1408,15 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
1412
1408
preset_txin.SetScriptWitness (txin.scriptWitness );
1413
1409
}
1414
1410
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 );
1416
1412
if (!res) {
1417
1413
error = util::ErrorString (res);
1418
1414
return false ;
1419
1415
}
1420
1416
const auto & txr = *res;
1421
1417
CTransactionRef tx_new = txr.tx ;
1422
1418
nFeeRet = txr.fee ;
1423
- nChangePosInOut = txr.change_pos ;
1419
+ nChangePosInOut = txr.change_pos ? *txr. change_pos : - 1 ;
1424
1420
1425
1421
if (nChangePosInOut != -1 ) {
1426
1422
tx.vout .insert (tx.vout .begin () + nChangePosInOut, tx_new->vout [nChangePosInOut]);
0 commit comments