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