@@ -77,24 +77,62 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wall
7777 return CalculateMaximumSignedTxSize (tx, wallet, txouts, coin_control);
7878}
7979
80- uint64_t CoinsResult::size () const
80+ size_t CoinsResult::Size () const
8181{
82- return legacy.size () + other.size ();
82+ size_t size{0 };
83+ for (const auto & it : coins) {
84+ size += it.second .size ();
85+ }
86+ return size;
8387}
8488
85- std::vector<COutput> CoinsResult::all () const
89+ std::vector<COutput> CoinsResult::All () const
8690{
8791 std::vector<COutput> all;
88- all.reserve (this ->size ());
89- all.insert (all.end (), legacy.begin (), legacy.end ());
90- all.insert (all.end (), other.begin (), other.end ());
92+ all.reserve (Size ());
93+ for (const auto & it : coins) {
94+ all.insert (all.end (), it.second .begin (), it.second .end ());
95+ }
9196 return all;
9297}
9398
94- void CoinsResult::clear ()
99+ void CoinsResult::Clear () {
100+ coins.clear ();
101+ }
102+
103+ void CoinsResult::Erase (std::set<COutPoint>& preset_coins)
95104{
96- legacy.clear ();
97- other.clear ();
105+ for (auto & it : coins) {
106+ auto & vec = it.second ;
107+ auto i = std::find_if (vec.begin (), vec.end (), [&](const COutput &c) { return preset_coins.count (c.outpoint );});
108+ if (i != vec.end ()) {
109+ vec.erase (i);
110+ break ;
111+ }
112+ }
113+ }
114+
115+ void CoinsResult::Shuffle (FastRandomContext& rng_fast)
116+ {
117+ for (auto & it : coins) {
118+ ::Shuffle (it.second.begin(), it.second.end(), rng_fast);
119+ }
120+ }
121+
122+ void CoinsResult::Add (OutputType type, const COutput& out)
123+ {
124+ coins[type].emplace_back (out);
125+ }
126+
127+ static OutputType GetOutputType (TxoutType type)
128+ {
129+ switch (type) {
130+ case TxoutType::SCRIPTHASH:
131+ case TxoutType::PUBKEYHASH:
132+ return OutputType::LEGACY;
133+ default :
134+ return OutputType::UNKNOWN;
135+ }
98136}
99137
100138CoinsResult AvailableCoins (const CWallet& wallet,
@@ -215,37 +253,30 @@ CoinsResult AvailableCoins(const CWallet& wallet,
215253 // Filter by spendable outputs only
216254 if (!spendable && only_spendable) continue ;
217255
218- // When parsing a scriptPubKey, Solver returns the parsed pubkeys or hashes (depending on the script)
219- // We don't need those here, so we are leaving them in return_values_unused
220- std::vector<std::vector<uint8_t >> return_values_unused;
221- TxoutType type;
222-
223256 // If the Output is P2SH and spendable, we want to know if it is
224257 // a P2SH (legacy). We can determine this from the redeemScript.
225258 // If the Output is not spendable, it will be classified as a P2SH (legacy),
226259 // since we have no way of knowing otherwise without the redeemScript
260+ CScript script;
227261 if (output.scriptPubKey .IsPayToScriptHash () && solvable) {
228- CScript redeemScript;
229262 CTxDestination destination;
230263 if (!ExtractDestination (output.scriptPubKey , destination))
231264 continue ;
232265 const CScriptID& hash = CScriptID (std::get<ScriptHash>(destination));
233- if (!provider->GetCScript (hash, redeemScript ))
266+ if (!provider->GetCScript (hash, script ))
234267 continue ;
235- type = Solver (redeemScript, return_values_unused);
236268 } else {
237- type = Solver ( output.scriptPubKey , return_values_unused) ;
269+ script = output.scriptPubKey ;
238270 }
239271
240272 COutput coin (outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime (), tx_from_me, feerate);
241- switch (type) {
242- case TxoutType::SCRIPTHASH:
243- case TxoutType::PUBKEYHASH:
244- result.legacy .push_back (coin);
245- break ;
246- default :
247- result.other .push_back (coin);
248- };
273+
274+ // When parsing a scriptPubKey, Solver returns the parsed pubkeys or hashes (depending on the script)
275+ // We don't need those here, so we are leaving them in return_values_unused
276+ std::vector<std::vector<uint8_t >> return_values_unused;
277+ TxoutType type;
278+ type = Solver (script, return_values_unused);
279+ result.Add (GetOutputType (type), coin);
249280
250281 // Cache total amount as we go
251282 result.total_amount += output.nValue ;
@@ -257,7 +288,7 @@ CoinsResult AvailableCoins(const CWallet& wallet,
257288 }
258289
259290 // Checks the maximum number of UTXO's.
260- if (nMaximumCount > 0 && result.size () >= nMaximumCount) {
291+ if (nMaximumCount > 0 && result.Size () >= nMaximumCount) {
261292 return result;
262293 }
263294 }
@@ -313,7 +344,7 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
313344
314345 std::map<CTxDestination, std::vector<COutput>> result;
315346
316- for (COutput& coin : AvailableCoinsListUnspent (wallet).all ()) {
347+ for (const COutput& coin : AvailableCoinsListUnspent (wallet).All ()) {
317348 CTxDestination address;
318349 if ((coin.spendable || (wallet.IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.solvable )) &&
319350 ExtractDestination (FindNonChangeParentOutput (wallet, coin.outpoint ).scriptPubKey , address)) {
@@ -443,22 +474,25 @@ std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAm
443474{
444475 // Run coin selection on each OutputType and compute the Waste Metric
445476 std::vector<SelectionResult> results;
446- if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, available_coins.legacy , coin_selection_params, nCoinType)}) {
447- results.push_back (*result);
477+ for (const auto & it : available_coins.coins ) {
478+ if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, it.second , coin_selection_params, nCoinType)}) {
479+ results.push_back (*result);
480+ }
448481 }
449-
450- // If we can't fund the transaction from any individual OutputType, run coin selection
451- // over all available coins, else pick the best solution from the results
452- if (results.size () == 0 ) {
453- if (allow_mixed_output_types) {
454- if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, available_coins.all (), coin_selection_params, nCoinType)}) {
455- return result;
456- }
482+ // If we have at least one solution for funding the transaction without mixing, choose the minimum one according to waste metric
483+ // and return the result
484+ if (results.size () > 0 ) return *std::min_element (results.begin (), results.end ());
485+
486+ // If we can't fund the transaction from any individual OutputType, run coin selection one last time
487+ // over all available coins, which would allow mixing
488+ if (allow_mixed_output_types) {
489+ if (auto result{ChooseSelectionResult (wallet, nTargetValue, eligibility_filter, available_coins.All (), coin_selection_params, nCoinType)}) {
490+ return result;
457491 }
458- return std::optional<SelectionResult>();
459- };
460- std::optional<SelectionResult> result{* std::min_element (results. begin (), results. end ())};
461- return result ;
492+ }
493+ // 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
494+ // find a solution using all available coins
495+ return std:: nullopt ;
462496};
463497
464498std::optional<SelectionResult> ChooseSelectionResult (const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, const std::vector<COutput>& available_coins,
@@ -576,7 +610,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
576610 // Calculate the smallest set of inputs required to meet nTargetValue from available_coins
577611 bool success{false };
578612 OutputGroup preset_candidates (coin_selection_params);
579- for (const COutput& out : available_coins.all ()) {
613+ for (const COutput& out : available_coins.All ()) {
580614 if (!out.spendable ) continue ;
581615 if (preset_coins.count (out.outpoint )) {
582616 preset_candidates.Insert (out, /* ancestors=*/ 0 , /* descendants=*/ 0 , /* positive_only=*/ false );
@@ -600,8 +634,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
600634
601635 // remove preset inputs from coins so that Coin Selection doesn't pick them.
602636 if (coin_control.HasSelected ()) {
603- 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 ());
604- 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 ());
637+ available_coins.Erase (preset_coins);
605638 }
606639
607640 unsigned int limit_ancestor_count = 0 ;
@@ -613,12 +646,11 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
613646
614647 // form groups from remaining coins; note that preset coins will not
615648 // automatically have their associated (same address) coins included
616- if (coin_control.m_avoid_partial_spends && available_coins.size () > OUTPUT_GROUP_MAX_ENTRIES) {
649+ if (coin_control.m_avoid_partial_spends && available_coins.Size () > OUTPUT_GROUP_MAX_ENTRIES) {
617650 // Cases where we have 101+ outputs all pointing to the same destination may result in
618651 // privacy leaks as they will potentially be deterministically sorted. We solve that by
619652 // explicitly shuffling the outputs before processing
620- Shuffle (available_coins.legacy .begin (), available_coins.legacy .end (), coin_selection_params.rng_fast );
621- Shuffle (available_coins.other .begin (), available_coins.other .end (), coin_selection_params.rng_fast );
653+ available_coins.Shuffle (coin_selection_params.rng_fast );
622654 }
623655 // Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
624656 // transaction at a target feerate. If an attempt fails, more attempts may be made using a more
0 commit comments