Skip to content

Commit 27572e8

Browse files
josibakeEunovo
authored andcommitted
use silent payments change when sending
1 parent 8b00ea5 commit 27572e8

File tree

3 files changed

+43
-24
lines changed

3 files changed

+43
-24
lines changed

src/wallet/spend.cpp

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,12 +1185,12 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
11851185
}
11861186

11871187
// Create change script that will be used if we need change
1188-
CScript scriptChange;
1188+
CTxDestination change_dest;
11891189
bilingual_str error; // possible error str
11901190

11911191
// coin control: send change to custom address
11921192
if (!std::get_if<CNoDestination>(&coin_control.destChange)) {
1193-
scriptChange = GetScriptForDestination(coin_control.destChange);
1193+
change_dest = coin_control.destChange;
11941194
} else { // no coin control: send change to newly generated address
11951195
// Note: We use a new key here to keep it from being obvious which side is the change.
11961196
// 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(
12011201

12021202
// Reserve a new key pair from key pool. If it fails, provide a dummy
12031203
// destination in case we don't need change.
1204-
CTxDestination dest;
12051204
auto op_dest = reservedest.GetReservedDestination(true);
12061205
if (!op_dest) {
12071206
error = _("Transaction needs a change address, but we can't generate it.") + Untranslated(" ") + util::ErrorString(op_dest);
12081207
} else {
1209-
dest = *op_dest;
1210-
scriptChange = GetScriptForDestination(dest);
1208+
change_dest = *op_dest;
12111209
}
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());
12161210
}
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);
12181216
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
12191217

12201218
// Get size of spending the change output
@@ -1311,25 +1309,40 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
13111309
sp_dests[i] = *sp;
13121310
}
13131311
}
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+
}
13141316
const auto& silent_payment_tr_spks = CreateSilentPaymentOutputs(wallet, sp_dests, result.GetInputSet(), error);
13151317
if (!silent_payment_tr_spks.has_value()) {
13161318
return util::Error{error};
13171319
}
13181320
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+
13191326
assert(out_idx < mutableVecSend.size());
13201327
mutableVecSend[out_idx].dest = tr_dest;
13211328
}
1322-
13231329
}
13241330
// vouts to the payees
13251331
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) {
13281333
txNew.vout.emplace_back(recipient.nAmount, GetScriptForDestination(recipient.dest));
13291334
}
13301335
const CAmount change_amount = result.GetChange(coin_selection_params.min_viable_change, coin_selection_params.m_change_fee);
13311336
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);
13331346
if (!change_pos) {
13341347
// Insert change txn at random position:
13351348
change_pos = rng_fast.randrange(txNew.vout.size() + 1);
@@ -1470,11 +1483,6 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
14701483
return util::Error{Untranslated(STR_INTERNAL_BUG("Fee needed > fee paid"))};
14711484
}
14721485

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-
14781486
if (sign && !wallet.SignTransaction(txNew)) {
14791487
return util::Error{_("Signing transaction failed")};
14801488
}
@@ -1533,7 +1541,8 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
15331541
if (wallet.IsMine(*tx, spent_coins))
15341542
wallet.WalletLogPrintf("Detected Silent Payments self-transfer: %s", tx->GetHash().ToString());
15351543
}
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);
15371546
}
15381547

15391548
util::Result<CreatedTransactionResult> CreateTransaction(
@@ -1568,7 +1577,9 @@ util::Result<CreatedTransactionResult> CreateTransaction(
15681577
tmp_cc.m_avoid_partial_spends = true;
15691578

15701579
// 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) {
15721583
ExtractDestination(txr_ungrouped.tx->vout[*txr_ungrouped.change_pos].scriptPubKey, tmp_cc.destChange);
15731584
}
15741585

src/wallet/spend.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,10 @@ struct CreatedTransactionResult
213213
CAmount fee;
214214
FeeCalculation fee_calc;
215215
std::optional<unsigned int> change_pos;
216+
OutputType change_type;
216217

217-
CreatedTransactionResult(CTransactionRef _tx, CAmount _fee, std::optional<unsigned int> _change_pos, const FeeCalculation& _fee_calc)
218-
: tx(_tx), fee(_fee), fee_calc(_fee_calc), change_pos(_change_pos) {}
218+
CreatedTransactionResult(CTransactionRef _tx, CAmount _fee, std::optional<unsigned int> _change_pos, const OutputType change_type, const FeeCalculation& _fee_calc)
219+
: tx(_tx), fee(_fee), fee_calc(_fee_calc), change_pos(_change_pos), change_type(change_type) {}
219220
};
220221

221222
/**

src/wallet/wallet.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2247,13 +2247,16 @@ OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& chang
22472247
return OutputType::LEGACY;
22482248
}
22492249

2250+
bool any_sp{false};
22502251
bool any_tr{false};
22512252
bool any_wpkh{false};
22522253
bool any_sh{false};
22532254
bool any_pkh{false};
22542255

22552256
for (const auto& recipient : vecSend) {
2256-
if (std::get_if<WitnessV1Taproot>(&recipient.dest) || std::get_if<V0SilentPaymentDestination>(&recipient.dest)) {
2257+
if (std::get_if<V0SilentPaymentDestination>(&recipient.dest)) {
2258+
any_sp = true;
2259+
} else if (std::get_if<WitnessV1Taproot>(&recipient.dest)) {
22572260
any_tr = true;
22582261
} else if (std::get_if<WitnessV0KeyHash>(&recipient.dest)) {
22592262
any_wpkh = true;
@@ -2264,6 +2267,10 @@ OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& chang
22642267
}
22652268
}
22662269

2270+
const bool has_sp_spkman(GetScriptPubKeyMan(OutputType::SILENT_PAYMENTS, /*internal=*/true));
2271+
if (has_sp_spkman && any_sp) {
2272+
return OutputType::SILENT_PAYMENTS;
2273+
}
22672274
const bool has_bech32m_spkman(GetScriptPubKeyMan(OutputType::BECH32M, /*internal=*/true));
22682275
if (has_bech32m_spkman && any_tr) {
22692276
// Currently tr is the only type supported by the BECH32M spkman

0 commit comments

Comments
 (0)