LCOV - code coverage report
Current view: top level - src - coins.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 189 222 85.1 %
Date: 2026-06-25 07:23:43 Functions: 31 42 73.8 %

          Line data    Source code
       1             : // Copyright (c) 2012-2021 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <coins.h>
       6             : 
       7             : #include <consensus/consensus.h>
       8             : #include <logging.h>
       9             : #include <random.h>
      10             : #include <util/trace.h>
      11             : #include <version.h>
      12             : 
      13           5 : bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
      14           0 : uint256 CCoinsView::GetBestBlock() const { return uint256(); }
      15           0 : std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
      16           0 : bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase) { return false; }
      17           0 : std::unique_ptr<CCoinsViewCursor> CCoinsView::Cursor() const { return nullptr; }
      18             : 
      19           0 : bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
      20             : {
      21           0 :     Coin coin;
      22           0 :     return GetCoin(outpoint, coin);
      23           0 : }
      24             : 
      25      880374 : CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
      26     3524688 : bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); }
      27           0 : bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->HaveCoin(outpoint); }
      28       51276 : uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
      29           0 : std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
      30      107116 : void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
      31        7523 : bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase) { return base->BatchWrite(mapCoins, hashBlock, erase); }
      32           0 : std::unique_ptr<CCoinsViewCursor> CCoinsViewBacked::Cursor() const { return base->Cursor(); }
      33           0 : size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
      34             : 
      35     2393224 : CCoinsViewCache::CCoinsViewCache(CCoinsView* baseIn, bool deterministic) :
      36     1596170 :     CCoinsViewBacked(baseIn), m_deterministic(deterministic),
      37             :     cacheCoins(0, SaltedOutpointHasher(/*deterministic=*/deterministic), CCoinsMap::key_equal{}, &m_cache_coins_memory_resource)
      38     1595139 : {}
      39             : 
      40     1912325 : size_t CCoinsViewCache::DynamicMemoryUsage() const {
      41     1912325 :     return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
      42             : }
      43             : 
      44   121872670 : CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const {
      45   121872670 :     CCoinsMap::iterator it = cacheCoins.find(outpoint);
      46   121872670 :     if (it != cacheCoins.end())
      47    48019913 :         return it;
      48    73852757 :     Coin tmp;
      49    73852757 :     if (!base->GetCoin(outpoint, tmp))
      50    61683544 :         return cacheCoins.end();
      51    12169211 :     CCoinsMap::iterator ret = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::forward_as_tuple(std::move(tmp))).first;
      52    12169211 :     if (ret->second.coin.IsSpent()) {
      53             :         // The parent only has an empty entry for this outpoint; we can consider our
      54             :         // version as fresh.
      55      217205 :         ret->second.flags = CCoinsCacheEntry::FRESH;
      56      217205 :     }
      57    12169211 :     cachedCoinsUsage += ret->second.coin.DynamicMemoryUsage();
      58    12169211 :     return ret;
      59   121872670 : }
      60             : 
      61    48208994 : bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const {
      62    48208994 :     CCoinsMap::const_iterator it = FetchCoin(outpoint);
      63    48208994 :     if (it != cacheCoins.end()) {
      64    12280359 :         coin = it->second.coin;
      65    12280359 :         return !coin.IsSpent();
      66             :     }
      67    35928635 :     return false;
      68    48208994 : }
      69             : 
      70    17607300 : void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
      71    17607300 :     assert(!coin.IsSpent());
      72    17607300 :     if (coin.out.scriptPubKey.IsUnspendable()) return;
      73    17385538 :     CCoinsMap::iterator it;
      74             :     bool inserted;
      75    17385538 :     std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::tuple<>());
      76    17385538 :     bool fresh = false;
      77    17385538 :     if (!inserted) {
      78       12845 :         cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
      79       12845 :     }
      80    17385538 :     if (!possible_overwrite) {
      81    16810342 :         if (!it->second.coin.IsSpent()) {
      82          16 :             throw std::logic_error("Attempted to overwrite an unspent coin (when possible_overwrite is false)");
      83             :         }
      84             :         // If the coin exists in this cache as a spent coin and is DIRTY, then
      85             :         // its spentness hasn't been flushed to the parent cache. We're
      86             :         // re-adding the coin to this cache now but we can't mark it as FRESH.
      87             :         // If we mark it FRESH and then spend it before the cache is flushed
      88             :         // we would remove it from this cache and would never flush spentness
      89             :         // to the parent cache.
      90             :         //
      91             :         // Re-adding a spent coin can happen in the case of a re-org (the coin
      92             :         // is 'spent' when the block adding it is disconnected and then
      93             :         // re-added when it is also added in a newly connected block).
      94             :         //
      95             :         // If the coin doesn't exist in the current cache, or is spent but not
      96             :         // DIRTY, then it can be marked FRESH.
      97    16810326 :         fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY);
      98    16810326 :     }
      99    17385522 :     it->second.coin = std::move(coin);
     100    17385522 :     it->second.flags |= CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0);
     101    17385522 :     cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
     102             :     TRACE5(utxocache, add,
     103             :            outpoint.hash.data(),
     104             :            (uint32_t)outpoint.n,
     105             :            (uint32_t)it->second.coin.nHeight,
     106             :            (int64_t)it->second.coin.out.nValue,
     107             :            (bool)it->second.coin.IsCoinBase());
     108    17607284 : }
     109             : 
     110         548 : void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin) {
     111         548 :     cachedCoinsUsage += coin.DynamicMemoryUsage();
     112        1096 :     cacheCoins.emplace(
     113             :         std::piecewise_construct,
     114         548 :         std::forward_as_tuple(std::move(outpoint)),
     115         548 :         std::forward_as_tuple(std::move(coin), CCoinsCacheEntry::DIRTY));
     116         548 : }
     117             : 
     118     8764602 : void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
     119     8764602 :     bool fCoinbase = tx.IsCoinBase();
     120     8764602 :     const uint256& txid = tx.GetHash();
     121    26266425 :     for (size_t i = 0; i < tx.vout.size(); ++i) {
     122    17501823 :         bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase;
     123             :         // Coinbase transactions can always be overwritten, in order to correctly
     124             :         // deal with the pre-BIP30 occurrences of duplicate coinbase transactions.
     125    17501823 :         cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite);
     126    17501823 :     }
     127     8764602 : }
     128             : 
     129    11511148 : bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
     130    11511148 :     CCoinsMap::iterator it = FetchCoin(outpoint);
     131    11511148 :     if (it == cacheCoins.end()) return false;
     132    11511142 :     cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
     133             :     TRACE5(utxocache, spent,
     134             :            outpoint.hash.data(),
     135             :            (uint32_t)outpoint.n,
     136             :            (uint32_t)it->second.coin.nHeight,
     137             :            (int64_t)it->second.coin.out.nValue,
     138             :            (bool)it->second.coin.IsCoinBase());
     139    11511142 :     if (moveout) {
     140      317349 :         *moveout = std::move(it->second.coin);
     141      317349 :     }
     142    11511142 :     if (it->second.flags & CCoinsCacheEntry::FRESH) {
     143      274996 :         cacheCoins.erase(it);
     144      274996 :     } else {
     145    11236146 :         it->second.flags |= CCoinsCacheEntry::DIRTY;
     146    11236146 :         it->second.coin.Clear();
     147             :     }
     148    11511142 :     return true;
     149    11511148 : }
     150             : 
     151        3516 : static const Coin coinEmpty;
     152             : 
     153    36914123 : const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
     154    36914123 :     CCoinsMap::const_iterator it = FetchCoin(outpoint);
     155    36914123 :     if (it == cacheCoins.end()) {
     156    23656825 :         return coinEmpty;
     157             :     } else {
     158    13257298 :         return it->second.coin;
     159             :     }
     160    36914123 : }
     161             : 
     162    25238403 : bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const {
     163    25238403 :     CCoinsMap::const_iterator it = FetchCoin(outpoint);
     164    25238403 :     return (it != cacheCoins.end() && !it->second.coin.IsSpent());
     165             : }
     166             : 
     167      553398 : bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const {
     168      553398 :     CCoinsMap::const_iterator it = cacheCoins.find(outpoint);
     169      553398 :     return (it != cacheCoins.end() && !it->second.coin.IsSpent());
     170             : }
     171             : 
     172      901120 : uint256 CCoinsViewCache::GetBestBlock() const {
     173      901120 :     if (hashBlock.IsNull())
     174      409076 :         hashBlock = base->GetBestBlock();
     175      901120 :     return hashBlock;
     176             : }
     177             : 
     178      281016 : void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
     179      281016 :     hashBlock = hashBlockIn;
     180      281016 : }
     181             : 
     182      268775 : bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn, bool erase) {
     183     1669312 :     for (CCoinsMap::iterator it = mapCoins.begin();
     184     1669312 :             it != mapCoins.end();
     185     1400537 :             it = erase ? mapCoins.erase(it) : std::next(it)) {
     186             :         // Ignore non-dirty entries (optimization).
     187     1400545 :         if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
     188      313838 :             continue;
     189             :         }
     190     1086707 :         CCoinsMap::iterator itUs = cacheCoins.find(it->first);
     191     1086707 :         if (itUs == cacheCoins.end()) {
     192             :             // The parent cache does not have an entry, while the child cache does.
     193             :             // We can ignore it if it's both spent and FRESH in the child
     194      891041 :             if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
     195             :                 // Create the coin in the parent cache, move the data up
     196             :                 // and mark it as dirty.
     197      891040 :                 CCoinsCacheEntry& entry = cacheCoins[it->first];
     198      891040 :                 if (erase) {
     199             :                     // The `move` call here is purely an optimization; we rely on the
     200             :                     // `mapCoins.erase` call in the `for` expression to actually remove
     201             :                     // the entry from the child map.
     202      871686 :                     entry.coin = std::move(it->second.coin);
     203      871686 :                 } else {
     204       19354 :                     entry.coin = it->second.coin;
     205             :                 }
     206      891040 :                 cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
     207      891040 :                 entry.flags = CCoinsCacheEntry::DIRTY;
     208             :                 // We can mark it FRESH in the parent if it was FRESH in the child
     209             :                 // Otherwise it might have just been flushed from the parent's cache
     210             :                 // and already exist in the grandparent
     211      891040 :                 if (it->second.flags & CCoinsCacheEntry::FRESH) {
     212      398326 :                     entry.flags |= CCoinsCacheEntry::FRESH;
     213      398326 :                 }
     214      891040 :             }
     215      891041 :         } else {
     216             :             // Found the entry in the parent cache
     217      195666 :             if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent()) {
     218             :                 // The coin was marked FRESH in the child cache, but the coin
     219             :                 // exists in the parent cache. If this ever happens, it means
     220             :                 // the FRESH flag was misapplied and there is a logic error in
     221             :                 // the calling code.
     222           8 :                 throw std::logic_error("FRESH flag misapplied to coin that exists in parent cache");
     223             :             }
     224             : 
     225      195658 :             if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
     226             :                 // The grandparent cache does not have an entry, and the coin
     227             :                 // has been spent. We can just delete it from the parent cache.
     228       82488 :                 cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
     229       82488 :                 cacheCoins.erase(itUs);
     230       82488 :             } else {
     231             :                 // A normal modification.
     232      113170 :                 cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
     233      113170 :                 if (erase) {
     234             :                     // The `move` call here is purely an optimization; we rely on the
     235             :                     // `mapCoins.erase` call in the `for` expression to actually remove
     236             :                     // the entry from the child map.
     237      103730 :                     itUs->second.coin = std::move(it->second.coin);
     238      103730 :                 } else {
     239        9440 :                     itUs->second.coin = it->second.coin;
     240             :                 }
     241      113170 :                 cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
     242      113170 :                 itUs->second.flags |= CCoinsCacheEntry::DIRTY;
     243             :                 // NOTE: It isn't safe to mark the coin as FRESH in the parent
     244             :                 // cache. If it already existed and was spent in the parent
     245             :                 // cache then marking it FRESH would prevent that spentness
     246             :                 // from being flushed to the grandparent.
     247             :             }
     248             :         }
     249     1086699 :     }
     250      268767 :     hashBlock = hashBlockIn;
     251      268767 :     return true;
     252           0 : }
     253             : 
     254      276216 : bool CCoinsViewCache::Flush() {
     255      276216 :     bool fOk = base->BatchWrite(cacheCoins, hashBlock, /*erase=*/true);
     256      276216 :     if (fOk) {
     257      276216 :         if (!cacheCoins.empty()) {
     258             :             /* BatchWrite must erase all cacheCoins elements when erase=true. */
     259           0 :             throw std::logic_error("Not all cached coins were erased");
     260             :         }
     261      276216 :         ReallocateCache();
     262      276216 :     }
     263      276216 :     cachedCoinsUsage = 0;
     264      276216 :     return fOk;
     265           0 : }
     266             : 
     267         204 : bool CCoinsViewCache::Sync()
     268             : {
     269         204 :     bool fOk = base->BatchWrite(cacheCoins, hashBlock, /*erase=*/false);
     270             :     // Instead of clearing `cacheCoins` as we would in Flush(), just clear the
     271             :     // FRESH/DIRTY flags of any coin that isn't spent.
     272      172370 :     for (auto it = cacheCoins.begin(); it != cacheCoins.end(); ) {
     273      172166 :         if (it->second.coin.IsSpent()) {
     274       27527 :             cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
     275       27527 :             it = cacheCoins.erase(it);
     276       27527 :         } else {
     277      144639 :             it->second.flags = 0;
     278      144639 :             ++it;
     279             :         }
     280             :     }
     281         204 :     return fOk;
     282             : }
     283             : 
     284       41688 : void CCoinsViewCache::Uncache(const COutPoint& hash)
     285             : {
     286       41688 :     CCoinsMap::iterator it = cacheCoins.find(hash);
     287       41688 :     if (it != cacheCoins.end() && it->second.flags == 0) {
     288       10297 :         cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
     289             :         TRACE5(utxocache, uncache,
     290             :                hash.hash.data(),
     291             :                (uint32_t)hash.n,
     292             :                (uint32_t)it->second.coin.nHeight,
     293             :                (int64_t)it->second.coin.out.nValue,
     294             :                (bool)it->second.coin.IsCoinBase());
     295       10297 :         cacheCoins.erase(it);
     296       10297 :     }
     297       41688 : }
     298             : 
     299     1085801 : unsigned int CCoinsViewCache::GetCacheSize() const {
     300     1085801 :     return cacheCoins.size();
     301             : }
     302             : 
     303     8429234 : bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
     304             : {
     305     8429234 :     if (!tx.IsCoinBase()) {
     306    19897740 :         for (unsigned int i = 0; i < tx.vin.size(); i++) {
     307    11468555 :             if (!HaveCoin(tx.vin[i].prevout)) {
     308          49 :                 return false;
     309             :             }
     310    11468506 :         }
     311     8429185 :     }
     312     8429185 :     return true;
     313     8429234 : }
     314             : 
     315      276216 : void CCoinsViewCache::ReallocateCache()
     316             : {
     317             :     // Cache should be empty when we're calling this.
     318      276216 :     assert(cacheCoins.size() == 0);
     319      276216 :     cacheCoins.~CCoinsMap();
     320      276216 :     m_cache_coins_memory_resource.~CCoinsMapMemoryResource();
     321      276216 :     ::new (&m_cache_coins_memory_resource) CCoinsMapMemoryResource{};
     322      276216 :     ::new (&cacheCoins) CCoinsMap{0, SaltedOutpointHasher{/*deterministic=*/m_deterministic}, CCoinsMap::key_equal{}, &m_cache_coins_memory_resource};
     323      276216 : }
     324             : 
     325           0 : void CCoinsViewCache::SanityCheck() const
     326             : {
     327           0 :     size_t recomputed_usage = 0;
     328           0 :     for (const auto& [_, entry] : cacheCoins) {
     329           0 :         unsigned attr = 0;
     330           0 :         if (entry.flags & CCoinsCacheEntry::DIRTY) attr |= 1;
     331           0 :         if (entry.flags & CCoinsCacheEntry::FRESH) attr |= 2;
     332           0 :         if (entry.coin.IsSpent()) attr |= 4;
     333             :         // Only 5 combinations are possible.
     334           0 :         assert(attr != 2 && attr != 4 && attr != 7);
     335             : 
     336             :         // Recompute cachedCoinsUsage.
     337           0 :         recomputed_usage += entry.coin.DynamicMemoryUsage();
     338             :     }
     339           0 :     assert(recomputed_usage == cachedCoinsUsage);
     340           0 : }
     341             : 
     342        3516 : static const size_t MAX_OUTPUTS_PER_BLOCK = MaxBlockSize() /  ::GetSerializeSize(CTxOut(), PROTOCOL_VERSION);
     343             : 
     344         208 : const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
     345             : {
     346         208 :     COutPoint iter(txid, 0);
     347    22666852 :     while (iter.n < MAX_OUTPUTS_PER_BLOCK) {
     348    22666750 :         const Coin& alternate = view.AccessCoin(iter);
     349    22666750 :         if (!alternate.IsSpent()) return alternate;
     350    22666644 :         ++iter.n;
     351             :     }
     352         102 :     return coinEmpty;
     353         208 : }
     354             : 
     355     3524688 : bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) const {
     356             :     try {
     357     3524688 :         return CCoinsViewBacked::GetCoin(outpoint, coin);
     358           0 :     } catch(const std::runtime_error& e) {
     359           0 :         for (const auto& f : m_err_callbacks) {
     360           0 :             f();
     361             :         }
     362           0 :         LogPrintf("Error reading from database: %s\n", e.what());
     363             :         // Starting the shutdown sequence and returning false to the caller would be
     364             :         // interpreted as 'entry not found' (as opposed to unable to read data), and
     365             :         // could lead to invalid interpretation. Just exit immediately, as we can't
     366             :         // continue anyway, and all writes should be atomic.
     367           0 :         std::abort();
     368           0 :     }
     369           0 : }

Generated by: LCOV version 1.16