10
10
#include < consensus/amount.h>
11
11
#include < consensus/validation.h>
12
12
#include < interfaces/chain.h>
13
+ #include < key_io.h>
13
14
#include < node/types.h>
14
15
#include < numeric>
15
16
#include < policy/policy.h>
@@ -1139,6 +1140,7 @@ std::optional<std::map<size_t, WitnessV1Taproot>> CreateSilentPaymentOutputs(
1139
1140
static util::Result<CreatedTransactionResult> CreateTransactionInternal (
1140
1141
CWallet& wallet,
1141
1142
const std::vector<CRecipient>& vecSend,
1143
+ const OutputType change_type,
1142
1144
std::optional<unsigned int > change_pos,
1143
1145
const CCoinControl& coin_control,
1144
1146
bool sign) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@@ -1166,7 +1168,6 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1166
1168
coin_selection_params.tx_noinputs_size = 10 + GetSizeOfCompactSize (vecSend.size ()); // bytes for output count
1167
1169
1168
1170
CAmount recipients_sum = 0 ;
1169
- const OutputType change_type = wallet.TransactionChangeType (coin_control.m_change_type ? *coin_control.m_change_type : wallet.m_default_change_type , vecSend);
1170
1171
ReserveDestination reservedest (&wallet, change_type);
1171
1172
unsigned int outputs_to_subtract_fee_from = 0 ; // The number of outputs which we are subtracting the fee from
1172
1173
for (const auto & recipient : vecSend) {
@@ -1302,26 +1303,29 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1302
1303
result.GetSelectedValue ());
1303
1304
1304
1305
std::vector<CRecipient> mutableVecSend = vecSend;
1305
- if (coin_control.m_silent_payment ) {
1306
+ bool fSilentPayment = coin_control.m_silent_payment || std::holds_alternative<V0SilentPaymentDestination>(change_dest);
1307
+ if (fSilentPayment ) {
1306
1308
// Get the silent payment destinations, generate the scriptPubKeys,
1307
1309
// and update vecSend with the generated scriptPubKeys
1308
1310
std::map<size_t , V0SilentPaymentDestination> sp_dests;
1311
+ size_t sp_change_index = 0 ;
1309
1312
for (size_t i = 0 ; i < mutableVecSend.size (); ++i) {
1310
1313
if (const auto * sp = std::get_if<V0SilentPaymentDestination>(&mutableVecSend.at (i).dest )) {
1311
1314
// Keep track of the index in vecSend
1312
1315
sp_dests[i] = *sp;
1316
+ sp_change_index += 1 ;
1313
1317
}
1314
1318
}
1315
- if (const auto * change_sp = std::get_if<V0SilentPaymentDestination>(&change_dest)) {
1319
+ if (const auto * sp_change = std::get_if<V0SilentPaymentDestination>(&change_dest)) {
1316
1320
// Generate output for change too
1317
- sp_dests[mutableVecSend. size () ] = *change_sp ;
1321
+ sp_dests[sp_change_index ] = *sp_change ;
1318
1322
}
1319
1323
const auto & silent_payment_tr_spks = CreateSilentPaymentOutputs (wallet, sp_dests, result.GetInputSet (), error);
1320
1324
if (!silent_payment_tr_spks.has_value ()) {
1321
1325
return util::Error{error};
1322
1326
}
1323
1327
for (const auto & [out_idx, tr_dest] : *silent_payment_tr_spks) {
1324
- if (out_idx == mutableVecSend. size () ) {
1328
+ if (out_idx == sp_change_index ) {
1325
1329
change_dest = tr_dest;
1326
1330
continue ;
1327
1331
}
@@ -1535,7 +1539,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
1535
1539
// TODO: this feels a bit hacky, but given the current way we handle change and receiving addresses,
1536
1540
// I think this is good enough. Ultimately, improving our change detection and how we handle the address
1537
1541
// seems like a better time to revisit this.
1538
- if (coin_control. m_silent_payment && wallet.IsWalletFlagSet (WALLET_FLAG_SILENT_PAYMENTS)) {
1542
+ if (fSilentPayment && wallet.IsWalletFlagSet (WALLET_FLAG_SILENT_PAYMENTS)) {
1539
1543
// If our wallet supports receiving silent payments, check if this transaction is a self transfer
1540
1544
std::map<COutPoint, Coin> spent_coins;
1541
1545
for (const auto & utxo : result.GetInputSet ()) {
@@ -1565,14 +1569,33 @@ util::Result<CreatedTransactionResult> CreateTransaction(
1565
1569
1566
1570
LOCK (wallet.cs_wallet );
1567
1571
1568
- auto res = CreateTransactionInternal (wallet, vecSend, change_pos, coin_control, sign);
1569
- TRACEPOINT (coin_selection, normal_create_tx_internal,
1572
+ wallet::CreatedTransactionResult txr_ungrouped;
1573
+ OutputType change_type = wallet.TransactionChangeType (coin_control.m_change_type ? *coin_control.m_change_type : wallet.m_default_change_type , vecSend);
1574
+ auto res1 = CreateTransactionInternal (wallet, vecSend, change_type, change_pos, coin_control, sign);
1575
+ if (!res1) {
1576
+ change_type = wallet.TransactionChangeType (
1577
+ coin_control.m_change_type ? *coin_control.m_change_type : wallet.m_default_change_type ,
1578
+ vecSend, /* exclude_sp= */ true );
1579
+ auto res2 = CreateTransactionInternal (wallet, vecSend, change_type, change_pos, coin_control, false );
1580
+
1581
+ TRACEPOINT (coin_selection, normal_create_tx_internal,
1582
+ wallet.GetName ().c_str (),
1583
+ bool (res2),
1584
+ res2 ? res2->fee : 0 ,
1585
+ res2 && res2->change_pos .has_value () ? int32_t (*res2->change_pos ) : -1 );
1586
+ if (!res2) return res2;
1587
+
1588
+ txr_ungrouped = *res2;
1589
+ } else {
1590
+ TRACEPOINT (coin_selection, normal_create_tx_internal,
1570
1591
wallet.GetName ().c_str (),
1571
- bool (res),
1572
- res ? res->fee : 0 ,
1573
- res && res->change_pos .has_value () ? int32_t (*res->change_pos ) : -1 );
1574
- if (!res) return res;
1575
- const auto & txr_ungrouped = *res;
1592
+ bool (res1),
1593
+ res1->fee ,
1594
+ res1->change_pos .has_value () ? int32_t (*res1->change_pos ) : -1 );
1595
+
1596
+ txr_ungrouped = *res1;
1597
+ }
1598
+
1576
1599
// try with avoidpartialspends unless it's enabled already
1577
1600
if (txr_ungrouped.fee > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends ) {
1578
1601
TRACEPOINT (coin_selection, attempting_aps_create_tx, wallet.GetName ().c_str ());
@@ -1586,7 +1609,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
1586
1609
ExtractDestination (txr_ungrouped.tx ->vout [*txr_ungrouped.change_pos ].scriptPubKey , tmp_cc.destChange );
1587
1610
}
1588
1611
1589
- auto txr_grouped = CreateTransactionInternal (wallet, vecSend, change_pos, tmp_cc, sign);
1612
+ auto txr_grouped = CreateTransactionInternal (wallet, vecSend, change_type, change_pos, tmp_cc, sign);
1590
1613
// if fee of this alternative one is within the range of the max fee, we use this one
1591
1614
const bool use_aps{txr_grouped.has_value () ? (txr_grouped->fee <= txr_ungrouped.fee + wallet.m_max_aps_fee ) : false };
1592
1615
TRACEPOINT (coin_selection, aps_create_tx_internal,
@@ -1601,7 +1624,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
1601
1624
if (use_aps) return txr_grouped;
1602
1625
}
1603
1626
}
1604
- return res ;
1627
+ return txr_ungrouped ;
1605
1628
}
1606
1629
1607
1630
util::Result<CreatedTransactionResult> FundTransaction (CWallet& wallet, const CMutableTransaction& tx, const std::vector<CRecipient>& vecSend, std::optional<unsigned int > change_pos, bool lockUnspents, CCoinControl coinControl)
0 commit comments