@@ -1185,12 +1185,12 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1185
1185
}
1186
1186
1187
1187
// Create change script that will be used if we need change
1188
- CScript scriptChange ;
1188
+ CTxDestination change_dest ;
1189
1189
bilingual_str error; // possible error str
1190
1190
1191
1191
// coin control: send change to custom address
1192
1192
if (!std::get_if<CNoDestination>(&coin_control.destChange )) {
1193
- scriptChange = GetScriptForDestination ( coin_control.destChange ) ;
1193
+ change_dest = coin_control.destChange ;
1194
1194
} else { // no coin control: send change to newly generated address
1195
1195
// Note: We use a new key here to keep it from being obvious which side is the change.
1196
1196
// The drawback is that by not reusing a previous key, the change may be lost if a
@@ -1201,20 +1201,18 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1201
1201
1202
1202
// Reserve a new key pair from key pool. If it fails, provide a dummy
1203
1203
// destination in case we don't need change.
1204
- CTxDestination dest;
1205
1204
auto op_dest = reservedest.GetReservedDestination (true );
1206
1205
if (!op_dest) {
1207
1206
error = _ (" Transaction needs a change address, but we can't generate it." ) + Untranslated (" " ) + util::ErrorString (op_dest);
1208
1207
} else {
1209
- dest = *op_dest;
1210
- scriptChange = GetScriptForDestination (dest);
1208
+ change_dest = *op_dest;
1211
1209
}
1212
- // A valid destination implies a change script (and
1213
- // vice-versa). An empty change script will abort later, if the
1214
- // change keypool ran out, but change is required.
1215
- CHECK_NONFATAL (IsValidDestination (dest) != scriptChange.empty ());
1216
1210
}
1217
- CTxOut change_prototype_txout (0 , scriptChange);
1211
+ CScript change_prototype_script = GetScriptForDestination (change_dest);
1212
+ if (std::get_if<V0SilentPaymentDestination>(&change_dest)) {
1213
+ change_prototype_script = GetScriptForDestination (WitnessV1Taproot ());
1214
+ }
1215
+ CTxOut change_prototype_txout (0 , change_prototype_script);
1218
1216
coin_selection_params.change_output_size = GetSerializeSize (change_prototype_txout);
1219
1217
1220
1218
// Get size of spending the change output
@@ -1311,25 +1309,40 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1311
1309
sp_dests[i] = *sp;
1312
1310
}
1313
1311
}
1312
+ if (const auto * change_sp = std::get_if<V0SilentPaymentDestination>(&change_dest)) {
1313
+ // Generate output for change too
1314
+ sp_dests[mutableVecSend.size ()] = *change_sp;
1315
+ }
1314
1316
const auto & silent_payment_tr_spks = CreateSilentPaymentOutputs (wallet, sp_dests, result.GetInputSet (), error);
1315
1317
if (!silent_payment_tr_spks.has_value ()) {
1316
1318
return util::Error{error};
1317
1319
}
1318
1320
for (const auto & [out_idx, tr_dest] : *silent_payment_tr_spks) {
1321
+ if (out_idx == mutableVecSend.size ()) {
1322
+ change_dest = tr_dest;
1323
+ continue ;
1324
+ }
1325
+
1319
1326
assert (out_idx < mutableVecSend.size ());
1320
1327
mutableVecSend[out_idx].dest = tr_dest;
1321
1328
}
1322
-
1323
1329
}
1324
1330
// vouts to the payees
1325
1331
txNew.vout .reserve (vecSend.size () + 1 ); // + 1 because of possible later insert
1326
- for (const auto & recipient : mutableVecSend)
1327
- {
1332
+ for (const auto & recipient : mutableVecSend) {
1328
1333
txNew.vout .emplace_back (recipient.nAmount , GetScriptForDestination (recipient.dest ));
1329
1334
}
1330
1335
const CAmount change_amount = result.GetChange (coin_selection_params.min_viable_change , coin_selection_params.m_change_fee );
1331
1336
if (change_amount > 0 ) {
1332
- CTxOut newTxOut (change_amount, scriptChange);
1337
+ // Give up if change keypool ran out as change is required
1338
+ if (!IsValidDestination (change_dest)) {
1339
+ return util::Error{error};
1340
+ }
1341
+
1342
+ CScript change_script = GetScriptForDestination (change_dest);
1343
+ Assert (!change_script.empty ());
1344
+
1345
+ CTxOut newTxOut (change_amount, change_script);
1333
1346
if (!change_pos) {
1334
1347
// Insert change txn at random position:
1335
1348
change_pos = rng_fast.randrange (txNew.vout .size () + 1 );
@@ -1470,11 +1483,6 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1470
1483
return util::Error{Untranslated (STR_INTERNAL_BUG (" Fee needed > fee paid" ))};
1471
1484
}
1472
1485
1473
- // Give up if change keypool ran out and change is required
1474
- if (scriptChange.empty () && change_pos) {
1475
- return util::Error{error};
1476
- }
1477
-
1478
1486
if (sign && !wallet.SignTransaction (txNew)) {
1479
1487
return util::Error{_ (" Signing transaction failed" )};
1480
1488
}
@@ -1533,7 +1541,8 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1533
1541
if (wallet.IsMine (*tx, spent_coins))
1534
1542
wallet.WalletLogPrintf (" Detected Silent Payments self-transfer: %s" , tx->GetHash ().ToString ());
1535
1543
}
1536
- return CreatedTransactionResult (tx, current_fee, change_pos, feeCalc);
1544
+ LogPrintf (" change_type: %s" , FormatOutputType (change_type));
1545
+ return CreatedTransactionResult (tx, current_fee, change_pos, change_type, feeCalc);
1537
1546
}
1538
1547
1539
1548
util::Result<CreatedTransactionResult> CreateTransaction (
@@ -1568,7 +1577,9 @@ util::Result<CreatedTransactionResult> CreateTransaction(
1568
1577
tmp_cc.m_avoid_partial_spends = true ;
1569
1578
1570
1579
// Reuse the change destination from the first creation attempt to avoid skipping BIP44 indexes
1571
- if (txr_ungrouped.change_pos ) {
1580
+ // Do not reuse the change dest if it came from a silent payments destination
1581
+ // silent payments change dest must be generated with the other silent payment outputs
1582
+ if (txr_ungrouped.change_pos && txr_ungrouped.change_type != OutputType::SILENT_PAYMENTS) {
1572
1583
ExtractDestination (txr_ungrouped.tx ->vout [*txr_ungrouped.change_pos ].scriptPubKey , tmp_cc.destChange );
1573
1584
}
1574
1585
0 commit comments