@@ -79,30 +79,68 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
79
79
return CalculateMaximumSignedTxSize (tx, wallet, txouts, coin_control);
80
80
}
81
81
82
- uint64_t CoinsResult::size () const
82
+ size_t CoinsResult::Size () const
83
83
{
84
- return bech32m.size () + bech32.size () + P2SH_segwit.size () + legacy.size () + other.size ();
84
+ size_t size{0 };
85
+ for (const auto & it : coins) {
86
+ size += it.second .size ();
87
+ }
88
+ return size;
85
89
}
86
90
87
- std::vector<COutput> CoinsResult::all () const
91
+ std::vector<COutput> CoinsResult::All () const
88
92
{
89
93
std::vector<COutput> all;
90
- all.reserve (this ->size ());
91
- all.insert (all.end (), bech32m.begin (), bech32m.end ());
92
- all.insert (all.end (), bech32.begin (), bech32.end ());
93
- all.insert (all.end (), P2SH_segwit.begin (), P2SH_segwit.end ());
94
- all.insert (all.end (), legacy.begin (), legacy.end ());
95
- all.insert (all.end (), other.begin (), other.end ());
94
+ all.reserve (coins.size ());
95
+ for (const auto & it : coins) {
96
+ all.insert (all.end (), it.second .begin (), it.second .end ());
97
+ }
96
98
return all;
97
99
}
98
100
99
- void CoinsResult::clear ()
101
+ void CoinsResult::Clear () {
102
+ coins.clear ();
103
+ }
104
+
105
+ void CoinsResult::Erase (std::set<COutPoint>& preset_coins)
100
106
{
101
- bech32m.clear ();
102
- bech32.clear ();
103
- P2SH_segwit.clear ();
104
- legacy.clear ();
105
- other.clear ();
107
+ for (auto & it : coins) {
108
+ auto & vec = it.second ;
109
+ auto i = std::find_if (vec.begin (), vec.end (), [&](const COutput &c) { return preset_coins.count (c.outpoint );});
110
+ if (i != vec.end ()) {
111
+ vec.erase (i);
112
+ break ;
113
+ }
114
+ }
115
+ }
116
+
117
+ void CoinsResult::Shuffle (FastRandomContext& rng_fast)
118
+ {
119
+ for (auto & it : coins) {
120
+ ::Shuffle (it.second.begin(), it.second.end(), rng_fast);
121
+ }
122
+ }
123
+
124
+ void CoinsResult::Add (OutputType type, const COutput& out)
125
+ {
126
+ coins[type].emplace_back (out);
127
+ }
128
+
129
+ static OutputType GetOutputType (TxoutType type, bool is_from_p2sh)
130
+ {
131
+ switch (type) {
132
+ case TxoutType::WITNESS_V1_TAPROOT:
133
+ return OutputType::BECH32M;
134
+ case TxoutType::WITNESS_V0_KEYHASH:
135
+ case TxoutType::WITNESS_V0_SCRIPTHASH:
136
+ if (is_from_p2sh) return OutputType::P2SH_SEGWIT;
137
+ else return OutputType::BECH32;
138
+ case TxoutType::SCRIPTHASH:
139
+ case TxoutType::PUBKEYHASH:
140
+ return OutputType::LEGACY;
141
+ default :
142
+ return OutputType::UNKNOWN;
143
+ }
106
144
}
107
145
108
146
CoinsResult AvailableCoins (const CWallet& wallet,
@@ -222,51 +260,32 @@ CoinsResult AvailableCoins(const CWallet& wallet,
222
260
// Filter by spendable outputs only
223
261
if (!spendable && only_spendable) continue ;
224
262
225
- // When parsing a scriptPubKey, Solver returns the parsed pubkeys or hashes (depending on the script)
226
- // We don't need those here, so we are leaving them in return_values_unused
227
- std::vector<std::vector<uint8_t >> return_values_unused;
228
- TxoutType type;
229
- bool is_from_p2sh{false };
230
-
231
263
// If the Output is P2SH and spendable, we want to know if it is
232
264
// a P2SH (legacy) or one of P2SH-P2WPKH, P2SH-P2WSH (P2SH-Segwit). We can determine
233
265
// this from the redeemScript. If the Output is not spendable, it will be classified
234
266
// as a P2SH (legacy), since we have no way of knowing otherwise without the redeemScript
267
+ CScript script;
268
+ bool is_from_p2sh{false };
235
269
if (output.scriptPubKey .IsPayToScriptHash () && solvable) {
236
- CScript redeemScript;
237
270
CTxDestination destination;
238
271
if (!ExtractDestination (output.scriptPubKey , destination))
239
272
continue ;
240
273
const CScriptID& hash = CScriptID (std::get<ScriptHash>(destination));
241
- if (!provider->GetCScript (hash, redeemScript ))
274
+ if (!provider->GetCScript (hash, script ))
242
275
continue ;
243
- type = Solver (redeemScript, return_values_unused);
244
276
is_from_p2sh = true ;
245
277
} else {
246
- type = Solver ( output.scriptPubKey , return_values_unused) ;
278
+ script = output.scriptPubKey ;
247
279
}
248
280
249
281
COutput coin (outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime (), tx_from_me, feerate);
250
- switch (type) {
251
- case TxoutType::WITNESS_UNKNOWN:
252
- case TxoutType::WITNESS_V1_TAPROOT:
253
- result.bech32m .push_back (coin);
254
- break ;
255
- case TxoutType::WITNESS_V0_KEYHASH:
256
- case TxoutType::WITNESS_V0_SCRIPTHASH:
257
- if (is_from_p2sh) {
258
- result.P2SH_segwit .push_back (coin);
259
- break ;
260
- }
261
- result.bech32 .push_back (coin);
262
- break ;
263
- case TxoutType::SCRIPTHASH:
264
- case TxoutType::PUBKEYHASH:
265
- result.legacy .push_back (coin);
266
- break ;
267
- default :
268
- result.other .push_back (coin);
269
- };
282
+
283
+ // When parsing a scriptPubKey, Solver returns the parsed pubkeys or hashes (depending on the script)
284
+ // We don't need those here, so we are leaving them in return_values_unused
285
+ std::vector<std::vector<uint8_t >> return_values_unused;
286
+ TxoutType type;
287
+ type = Solver (script, return_values_unused);
288
+ result.Add (GetOutputType (type, is_from_p2sh), coin);
270
289
271
290
// Cache total amount as we go
272
291
result.total_amount += output.nValue ;
@@ -278,7 +297,7 @@ CoinsResult AvailableCoins(const CWallet& wallet,
278
297
}
279
298
280
299
// Checks the maximum number of UTXO's.
281
- if (nMaximumCount > 0 && result.size () >= nMaximumCount) {
300
+ if (nMaximumCount > 0 && result.Size () >= nMaximumCount) {
282
301
return result;
283
302
}
284
303
}
@@ -334,7 +353,7 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
334
353
335
354
std::map<CTxDestination, std::vector<COutput>> result;
336
355
337
- for (const COutput& coin : AvailableCoinsListUnspent (wallet).all ()) {
356
+ for (const COutput& coin : AvailableCoinsListUnspent (wallet).All ()) {
338
357
CTxDestination address;
339
358
if ((coin.spendable || (wallet.IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.solvable )) &&
340
359
ExtractDestination (FindNonChangeParentOutput (wallet, coin.outpoint ).scriptPubKey , address)) {
@@ -457,31 +476,25 @@ std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAm
457
476
{
458
477
// Run coin selection on each OutputType and compute the Waste Metric
459
478
std::vector<SelectionResult> results;
460
- if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, available_coins.legacy , coin_selection_params)}) {
461
- results.push_back (*result);
462
- }
463
- if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, available_coins.P2SH_segwit , coin_selection_params)}) {
464
- results.push_back (*result);
465
- }
466
- if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, available_coins.bech32 , coin_selection_params)}) {
467
- results.push_back (*result);
468
- }
469
- if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, available_coins.bech32m , coin_selection_params)}) {
470
- results.push_back (*result);
479
+ for (const auto & it : available_coins.coins ) {
480
+ if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, it.second , coin_selection_params)}) {
481
+ results.push_back (*result);
482
+ }
471
483
}
472
-
473
- // If we can't fund the transaction from any individual OutputType, run coin selection
474
- // over all available coins, else pick the best solution from the results
475
- if (results.size () == 0 ) {
476
- if (allow_mixed_output_types) {
477
- if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, available_coins.all (), coin_selection_params)}) {
478
- return result;
479
- }
484
+ // If we have at least one solution for funding the transaction without mixing, choose the minimum one according to waste metric
485
+ // and return the result
486
+ if (results.size () > 0 ) return *std::min_element (results.begin (), results.end ());
487
+
488
+ // If we can't fund the transaction from any individual OutputType, run coin selection one last time
489
+ // over all available coins, which would allow mixing
490
+ if (allow_mixed_output_types) {
491
+ if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, available_coins.All (), coin_selection_params)}) {
492
+ return result;
480
493
}
481
- return std::optional<SelectionResult>();
482
- };
483
- std::optional<SelectionResult> result{* std::min_element (results. begin (), results. end ())};
484
- return result ;
494
+ }
495
+ // Either mixing is not allowed and we couldn't find a solution from any single OutputType, or mixing was allowed and we still couldn't
496
+ // find a solution using all available coins
497
+ return std::nullopt ;
485
498
};
486
499
487
500
std::optional<SelectionResult> ChooseSelectionResult (const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, const std::vector<COutput>& available_coins, const CoinSelectionParams& coin_selection_params)
@@ -592,11 +605,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
592
605
593
606
// remove preset inputs from coins so that Coin Selection doesn't pick them.
594
607
if (coin_control.HasSelected ()) {
595
- available_coins.legacy .erase (remove_if (available_coins.legacy .begin (), available_coins.legacy .end (), [&](const COutput& c) { return preset_coins.count (c.outpoint ); }), available_coins.legacy .end ());
596
- available_coins.P2SH_segwit .erase (remove_if (available_coins.P2SH_segwit .begin (), available_coins.P2SH_segwit .end (), [&](const COutput& c) { return preset_coins.count (c.outpoint ); }), available_coins.P2SH_segwit .end ());
597
- available_coins.bech32 .erase (remove_if (available_coins.bech32 .begin (), available_coins.bech32 .end (), [&](const COutput& c) { return preset_coins.count (c.outpoint ); }), available_coins.bech32 .end ());
598
- available_coins.bech32m .erase (remove_if (available_coins.bech32m .begin (), available_coins.bech32m .end (), [&](const COutput& c) { return preset_coins.count (c.outpoint ); }), available_coins.bech32m .end ());
599
- available_coins.other .erase (remove_if (available_coins.other .begin (), available_coins.other .end (), [&](const COutput& c) { return preset_coins.count (c.outpoint ); }), available_coins.other .end ());
608
+ available_coins.Erase (preset_coins);
600
609
}
601
610
602
611
unsigned int limit_ancestor_count = 0 ;
@@ -608,15 +617,11 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
608
617
609
618
// form groups from remaining coins; note that preset coins will not
610
619
// automatically have their associated (same address) coins included
611
- if (coin_control.m_avoid_partial_spends && available_coins.size () > OUTPUT_GROUP_MAX_ENTRIES) {
620
+ if (coin_control.m_avoid_partial_spends && available_coins.Size () > OUTPUT_GROUP_MAX_ENTRIES) {
612
621
// Cases where we have 101+ outputs all pointing to the same destination may result in
613
622
// privacy leaks as they will potentially be deterministically sorted. We solve that by
614
623
// explicitly shuffling the outputs before processing
615
- Shuffle (available_coins.legacy .begin (), available_coins.legacy .end (), coin_selection_params.rng_fast );
616
- Shuffle (available_coins.P2SH_segwit .begin (), available_coins.P2SH_segwit .end (), coin_selection_params.rng_fast );
617
- Shuffle (available_coins.bech32 .begin (), available_coins.bech32 .end (), coin_selection_params.rng_fast );
618
- Shuffle (available_coins.bech32m .begin (), available_coins.bech32m .end (), coin_selection_params.rng_fast );
619
- Shuffle (available_coins.other .begin (), available_coins.other .end (), coin_selection_params.rng_fast );
624
+ available_coins.Shuffle (coin_selection_params.rng_fast );
620
625
}
621
626
622
627
// Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
0 commit comments