@@ -79,30 +79,68 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
7979 return CalculateMaximumSignedTxSize (tx, wallet, txouts, coin_control);
8080}
8181
82- uint64_t CoinsResult::size () const
82+ size_t CoinsResult::Size () const
8383{
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;
8589}
8690
87- std::vector<COutput> CoinsResult::all () const
91+ std::vector<COutput> CoinsResult::All () const
8892{
8993 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+ }
9698 return all;
9799}
98100
99- void CoinsResult::clear ()
101+ void CoinsResult::Clear () {
102+ coins.clear ();
103+ }
104+
105+ void CoinsResult::Erase (std::set<COutPoint>& preset_coins)
100106{
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+ }
106144}
107145
108146CoinsResult AvailableCoins (const CWallet& wallet,
@@ -222,51 +260,32 @@ CoinsResult AvailableCoins(const CWallet& wallet,
222260 // Filter by spendable outputs only
223261 if (!spendable && only_spendable) continue ;
224262
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-
231263 // If the Output is P2SH and spendable, we want to know if it is
232264 // a P2SH (legacy) or one of P2SH-P2WPKH, P2SH-P2WSH (P2SH-Segwit). We can determine
233265 // this from the redeemScript. If the Output is not spendable, it will be classified
234266 // as a P2SH (legacy), since we have no way of knowing otherwise without the redeemScript
267+ CScript script;
268+ bool is_from_p2sh{false };
235269 if (output.scriptPubKey .IsPayToScriptHash () && solvable) {
236- CScript redeemScript;
237270 CTxDestination destination;
238271 if (!ExtractDestination (output.scriptPubKey , destination))
239272 continue ;
240273 const CScriptID& hash = CScriptID (std::get<ScriptHash>(destination));
241- if (!provider->GetCScript (hash, redeemScript ))
274+ if (!provider->GetCScript (hash, script ))
242275 continue ;
243- type = Solver (redeemScript, return_values_unused);
244276 is_from_p2sh = true ;
245277 } else {
246- type = Solver ( output.scriptPubKey , return_values_unused) ;
278+ script = output.scriptPubKey ;
247279 }
248280
249281 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);
270289
271290 // Cache total amount as we go
272291 result.total_amount += output.nValue ;
@@ -278,7 +297,7 @@ CoinsResult AvailableCoins(const CWallet& wallet,
278297 }
279298
280299 // Checks the maximum number of UTXO's.
281- if (nMaximumCount > 0 && result.size () >= nMaximumCount) {
300+ if (nMaximumCount > 0 && result.Size () >= nMaximumCount) {
282301 return result;
283302 }
284303 }
@@ -334,7 +353,7 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
334353
335354 std::map<CTxDestination, std::vector<COutput>> result;
336355
337- for (const COutput& coin : AvailableCoinsListUnspent (wallet).all ()) {
356+ for (const COutput& coin : AvailableCoinsListUnspent (wallet).All ()) {
338357 CTxDestination address;
339358 if ((coin.spendable || (wallet.IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.solvable )) &&
340359 ExtractDestination (FindNonChangeParentOutput (wallet, coin.outpoint ).scriptPubKey , address)) {
@@ -457,31 +476,25 @@ std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAm
457476{
458477 // Run coin selection on each OutputType and compute the Waste Metric
459478 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+ }
471483 }
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;
480493 }
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 ;
485498};
486499
487500std::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
592605
593606 // remove preset inputs from coins so that Coin Selection doesn't pick them.
594607 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);
600609 }
601610
602611 unsigned int limit_ancestor_count = 0 ;
@@ -608,15 +617,11 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
608617
609618 // form groups from remaining coins; note that preset coins will not
610619 // 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) {
612621 // Cases where we have 101+ outputs all pointing to the same destination may result in
613622 // privacy leaks as they will potentially be deterministically sorted. We solve that by
614623 // 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 );
620625 }
621626
622627 // Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
0 commit comments