@@ -330,137 +330,146 @@ CoinsResult AvailableCoins(const CWallet& wallet,
330330 std::vector<COutPoint> outpoints;
331331
332332 std::set<Txid> trusted_parents;
333- for (const auto & entry : wallet.mapWallet )
334- {
335- const Txid& txid = entry.first ;
336- const CWalletTx& wtx = entry.second ;
337-
338- if (wallet.IsTxImmatureCoinBase (wtx) && !params.include_immature_coinbase )
339- continue ;
340-
341- int nDepth = wallet.GetTxDepthInMainChain (wtx);
342- if (nDepth < 0 )
343- continue ;
344-
345- // We should not consider coins which aren't at least in our mempool
346- // It's possible for these to be conflicted via ancestors which we may never be able to detect
347- if (nDepth == 0 && !wtx.InMempool ())
348- continue ;
349-
350- bool safeTx = CachedTxIsTrusted (wallet, wtx, trusted_parents);
351-
352- // We should not consider coins from transactions that are replacing
353- // other transactions.
354- //
355- // Example: There is a transaction A which is replaced by bumpfee
356- // transaction B. In this case, we want to prevent creation of
357- // a transaction B' which spends an output of B.
358- //
359- // Reason: If transaction A were initially confirmed, transactions B
360- // and B' would no longer be valid, so the user would have to create
361- // a new transaction C to replace B'. However, in the case of a
362- // one-block reorg, transactions B' and C might BOTH be accepted,
363- // when the user only wanted one of them. Specifically, there could
364- // be a 1-block reorg away from the chain where transactions A and C
365- // were accepted to another chain where B, B', and C were all
366- // accepted.
367- if (nDepth == 0 && wtx.mapValue .count (" replaces_txid" )) {
368- safeTx = false ;
369- }
370-
371- // Similarly, we should not consider coins from transactions that
372- // have been replaced. In the example above, we would want to prevent
373- // creation of a transaction A' spending an output of A, because if
374- // transaction B were initially confirmed, conflicting with A and
375- // A', we wouldn't want to the user to create a transaction D
376- // intending to replace A', but potentially resulting in a scenario
377- // where A, A', and D could all be accepted (instead of just B and
378- // D, or just A and A' like the user would want).
379- if (nDepth == 0 && wtx.mapValue .count (" replaced_by_txid" )) {
380- safeTx = false ;
381- }
382-
383- if (only_safe && !safeTx) {
384- continue ;
385- }
333+ // Cache for whether each tx passes the tx level checks (first bool), and whether the transaction is "safe" (second bool)
334+ std::unordered_map<uint256, std::pair<bool , bool >, SaltedTxidHasher> tx_safe_cache;
335+ for (const auto & [outpoint, txo] : wallet.GetTXOs ()) {
336+ const CWalletTx& wtx = txo.GetWalletTx ();
337+ const CTxOut& output = txo.GetTxOut ();
386338
387- if (nDepth < min_depth || nDepth > max_depth ) {
339+ if (tx_safe_cache. contains (outpoint. hash ) && !tx_safe_cache. at (outpoint. hash ). first ) {
388340 continue ;
389341 }
390342
391- bool tx_from_me = CachedTxIsFromMe ( wallet, wtx, ISMINE_ALL );
343+ int nDepth = wallet. GetTxDepthInMainChain ( wtx);
392344
393- for ( unsigned int i = 0 ; i < wtx. tx -> vout . size (); i++) {
394- const CTxOut& output = wtx. tx -> vout [i];
395- const COutPoint outpoint (txid, i) ;
345+ // Perform tx level checks if we haven't already come across outputs from this tx before.
346+ if (!tx_safe_cache. contains (outpoint. hash )) {
347+ tx_safe_cache[outpoint. hash ] = { false , false } ;
396348
397- if (output. nValue < params.min_amount || output. nValue > params. max_amount )
349+ if (wallet. IsTxImmatureCoinBase (wtx) && ! params.include_immature_coinbase )
398350 continue ;
399351
400- // Skip manually selected coins (the caller can fetch them directly)
401- if (coinControl && coinControl->HasSelected () && coinControl->IsSelected (outpoint))
352+ if (nDepth < 0 )
402353 continue ;
403354
404- if (wallet.IsLockedCoin (outpoint) && params.skip_locked )
355+ // We should not consider coins which aren't at least in our mempool
356+ // It's possible for these to be conflicted via ancestors which we may never be able to detect
357+ if (nDepth == 0 && !wtx.InMempool ())
405358 continue ;
406359
407- if (wallet.IsSpent (outpoint))
408- continue ;
360+ bool safeTx = CachedTxIsTrusted (wallet, wtx, trusted_parents);
361+
362+ // We should not consider coins from transactions that are replacing
363+ // other transactions.
364+ //
365+ // Example: There is a transaction A which is replaced by bumpfee
366+ // transaction B. In this case, we want to prevent creation of
367+ // a transaction B' which spends an output of B.
368+ //
369+ // Reason: If transaction A were initially confirmed, transactions B
370+ // and B' would no longer be valid, so the user would have to create
371+ // a new transaction C to replace B'. However, in the case of a
372+ // one-block reorg, transactions B' and C might BOTH be accepted,
373+ // when the user only wanted one of them. Specifically, there could
374+ // be a 1-block reorg away from the chain where transactions A and C
375+ // were accepted to another chain where B, B', and C were all
376+ // accepted.
377+ if (nDepth == 0 && wtx.mapValue .count (" replaces_txid" )) {
378+ safeTx = false ;
379+ }
409380
410- isminetype mine = wallet.IsMine (output);
381+ // Similarly, we should not consider coins from transactions that
382+ // have been replaced. In the example above, we would want to prevent
383+ // creation of a transaction A' spending an output of A, because if
384+ // transaction B were initially confirmed, conflicting with A and
385+ // A', we wouldn't want to the user to create a transaction D
386+ // intending to replace A', but potentially resulting in a scenario
387+ // where A, A', and D could all be accepted (instead of just B and
388+ // D, or just A and A' like the user would want).
389+ if (nDepth == 0 && wtx.mapValue .count (" replaced_by_txid" )) {
390+ safeTx = false ;
391+ }
411392
412- if (mine == ISMINE_NO ) {
393+ if (only_safe && !safeTx ) {
413394 continue ;
414395 }
415396
416- if (!allow_used_addresses && wallet. IsSpentKey (output. scriptPubKey ) ) {
397+ if (nDepth < min_depth || nDepth > max_depth ) {
417398 continue ;
418399 }
419400
420- std::unique_ptr<SigningProvider> provider = wallet.GetSolvingProvider (output.scriptPubKey );
421-
422- int input_bytes = CalculateMaximumSignedInputSize (output, COutPoint (), provider.get (), can_grind_r, coinControl);
423- // Because CalculateMaximumSignedInputSize infers a solvable descriptor to get the satisfaction size,
424- // it is safe to assume that this input is solvable if input_bytes is greater than -1.
425- bool solvable = input_bytes > -1 ;
426- bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
427-
428- // Filter by spendable outputs only
429- if (!spendable && params.only_spendable ) continue ;
430-
431- // Obtain script type
432- std::vector<std::vector<uint8_t >> script_solutions;
433- TxoutType type = Solver (output.scriptPubKey , script_solutions);
434-
435- // If the output is P2SH and solvable, we want to know if it is
436- // a P2SH (legacy) or one of P2SH-P2WPKH, P2SH-P2WSH (P2SH-Segwit). We can determine
437- // this from the redeemScript. If the output is not solvable, it will be classified
438- // as a P2SH (legacy), since we have no way of knowing otherwise without the redeemScript
439- bool is_from_p2sh{false };
440- if (type == TxoutType::SCRIPTHASH && solvable) {
441- CScript script;
442- if (!provider->GetCScript (CScriptID (uint160 (script_solutions[0 ])), script)) continue ;
443- type = Solver (script, script_solutions);
444- is_from_p2sh = true ;
445- }
401+ tx_safe_cache[outpoint.hash ] = {true , safeTx};
402+ }
403+ const auto & [tx_ok, tx_safe] = tx_safe_cache.at (outpoint.hash );
404+ if (!Assume (tx_ok)) {
405+ continue ;
406+ }
446407
447- result. Add ( GetOutputType (type, is_from_p2sh),
448- COutput (outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx. GetTxTime (), tx_from_me, feerate)) ;
408+ if (output. nValue < params. min_amount || output. nValue > params. max_amount )
409+ continue ;
449410
450- outpoints.push_back (outpoint);
411+ // Skip manually selected coins (the caller can fetch them directly)
412+ if (coinControl && coinControl->HasSelected () && coinControl->IsSelected (outpoint))
413+ continue ;
451414
452- // Checks the sum amount of all UTXO's.
453- if (params.min_sum_amount != MAX_MONEY) {
454- if (result.GetTotalAmount () >= params.min_sum_amount ) {
455- return result;
456- }
457- }
415+ if (wallet.IsLockedCoin (outpoint) && params.skip_locked )
416+ continue ;
417+
418+ if (wallet.IsSpent (outpoint))
419+ continue ;
458420
459- // Checks the maximum number of UTXO's.
460- if (params.max_count > 0 && result.Size () >= params.max_count ) {
421+ isminetype mine = wallet.IsMine (output);
422+ assert (mine != ISMINE_NO);
423+
424+ if (!allow_used_addresses && wallet.IsSpentKey (output.scriptPubKey )) {
425+ continue ;
426+ }
427+
428+ bool tx_from_me = CachedTxIsFromMe (wallet, wtx, ISMINE_ALL);
429+
430+ std::unique_ptr<SigningProvider> provider = wallet.GetSolvingProvider (output.scriptPubKey );
431+
432+ int input_bytes = CalculateMaximumSignedInputSize (output, COutPoint (), provider.get (), can_grind_r, coinControl);
433+ // Because CalculateMaximumSignedInputSize infers a solvable descriptor to get the satisfaction size,
434+ // it is safe to assume that this input is solvable if input_bytes is greater than -1.
435+ bool solvable = input_bytes > -1 ;
436+ bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
437+
438+ // Filter by spendable outputs only
439+ if (!spendable && params.only_spendable ) continue ;
440+
441+ // Obtain script type
442+ std::vector<std::vector<uint8_t >> script_solutions;
443+ TxoutType type = Solver (output.scriptPubKey , script_solutions);
444+
445+ // If the output is P2SH and solvable, we want to know if it is
446+ // a P2SH (legacy) or one of P2SH-P2WPKH, P2SH-P2WSH (P2SH-Segwit). We can determine
447+ // this from the redeemScript. If the output is not solvable, it will be classified
448+ // as a P2SH (legacy), since we have no way of knowing otherwise without the redeemScript
449+ bool is_from_p2sh{false };
450+ if (type == TxoutType::SCRIPTHASH && solvable) {
451+ CScript script;
452+ if (!provider->GetCScript (CScriptID (uint160 (script_solutions[0 ])), script)) continue ;
453+ type = Solver (script, script_solutions);
454+ is_from_p2sh = true ;
455+ }
456+
457+ result.Add (GetOutputType (type, is_from_p2sh),
458+ COutput (outpoint, output, nDepth, input_bytes, spendable, solvable, tx_safe, wtx.GetTxTime (), tx_from_me, feerate));
459+
460+ outpoints.push_back (outpoint);
461+
462+ // Checks the sum amount of all UTXO's.
463+ if (params.min_sum_amount != MAX_MONEY) {
464+ if (result.GetTotalAmount () >= params.min_sum_amount ) {
461465 return result;
462466 }
463467 }
468+
469+ // Checks the maximum number of UTXO's.
470+ if (params.max_count > 0 && result.Size () >= params.max_count ) {
471+ return result;
472+ }
464473 }
465474
466475 if (feerate.has_value ()) {
0 commit comments