@@ -330,137 +330,146 @@ CoinsResult AvailableCoins(const CWallet& wallet,
330
330
std::vector<COutPoint> outpoints;
331
331
332
332
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 ();
386
338
387
- if (nDepth < min_depth || nDepth > max_depth ) {
339
+ if (tx_safe_cache. contains (outpoint. hash ) && !tx_safe_cache. at (outpoint. hash ). first ) {
388
340
continue ;
389
341
}
390
342
391
- bool tx_from_me = CachedTxIsFromMe ( wallet, wtx, ISMINE_ALL );
343
+ int nDepth = wallet. GetTxDepthInMainChain ( wtx);
392
344
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 } ;
396
348
397
- if (output. nValue < params.min_amount || output. nValue > params. max_amount )
349
+ if (wallet. IsTxImmatureCoinBase (wtx) && ! params.include_immature_coinbase )
398
350
continue ;
399
351
400
- // Skip manually selected coins (the caller can fetch them directly)
401
- if (coinControl && coinControl->HasSelected () && coinControl->IsSelected (outpoint))
352
+ if (nDepth < 0 )
402
353
continue ;
403
354
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 ())
405
358
continue ;
406
359
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
+ }
409
380
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
+ }
411
392
412
- if (mine == ISMINE_NO ) {
393
+ if (only_safe && !safeTx ) {
413
394
continue ;
414
395
}
415
396
416
- if (!allow_used_addresses && wallet. IsSpentKey (output. scriptPubKey ) ) {
397
+ if (nDepth < min_depth || nDepth > max_depth ) {
417
398
continue ;
418
399
}
419
400
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
+ }
446
407
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 ;
449
410
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 ;
451
414
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 ;
458
420
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 ) {
461
465
return result;
462
466
}
463
467
}
468
+
469
+ // Checks the maximum number of UTXO's.
470
+ if (params.max_count > 0 && result.Size () >= params.max_count ) {
471
+ return result;
472
+ }
464
473
}
465
474
466
475
if (feerate.has_value ()) {
0 commit comments