LCOV - code coverage report
Current view: top level - src - coins.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 189 222 85.1 %
Date: 2026-06-25 07:23:51 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      155691 : CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
      26       68649 : 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         615 : uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
      29           0 : std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
      30         201 : void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
      31          26 : 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      465181 : CCoinsViewCache::CCoinsViewCache(CCoinsView* baseIn, bool deterministic) :
      36      310808 :     CCoinsViewBacked(baseIn), m_deterministic(deterministic),
      37             :     cacheCoins(0, SaltedOutpointHasher(/*deterministic=*/deterministic), CCoinsMap::key_equal{}, &m_cache_coins_memory_resource)
      38      309777 : {}
      39             : 
      40      173405 : size_t CCoinsViewCache::DynamicMemoryUsage() const {
      41      173405 :     return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
      42             : }
      43             : 
      44    58931939 : CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const {
      45    58931939 :     CCoinsMap::iterator it = cacheCoins.find(outpoint);
      46    58931939 :     if (it != cacheCoins.end())
      47      967936 :         return it;
      48    57964003 :     Coin tmp;
      49    57964003 :     if (!base->GetCoin(outpoint, tmp))
      50    57120779 :         return cacheCoins.end();
      51      843224 :     CCoinsMap::iterator ret = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::forward_as_tuple(std::move(tmp))).first;
      52      843224 :     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      843224 :     cachedCoinsUsage += ret->second.coin.DynamicMemoryUsage();
      58      843224 :     return ret;
      59    58931939 : }
      60             : 
      61    35664826 : bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const {
      62    35664826 :     CCoinsMap::const_iterator it = FetchCoin(outpoint);
      63    35664826 :     if (it != cacheCoins.end()) {
      64      838887 :         coin = it->second.coin;
      65      838887 :         return !coin.IsSpent();
      66             :     }
      67    34825939 :     return false;
      68    35664826 : }
      69             : 
      70      158054 : void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
      71      158054 :     assert(!coin.IsSpent());
      72      158054 :     if (coin.out.scriptPubKey.IsUnspendable()) return;
      73      142408 :     CCoinsMap::iterator it;
      74             :     bool inserted;
      75      142408 :     std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::tuple<>());
      76      142408 :     bool fresh = false;
      77      142408 :     if (!inserted) {
      78       11957 :         cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
      79       11957 :     }
      80      142408 :     if (!possible_overwrite) {
      81       38713 :         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       38697 :         fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY);
      98       38697 :     }
      99      142392 :     it->second.coin = std::move(coin);
     100      142392 :     it->second.flags |= CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0);
     101      142392 :     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      158038 : }
     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      117263 : void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
     119      117263 :     bool fCoinbase = tx.IsCoinBase();
     120      117263 :     const uint256& txid = tx.GetHash();
     121      224450 :     for (size_t i = 0; i < tx.vout.size(); ++i) {
     122      107187 :         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      107187 :         cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite);
     126      107187 :     }
     127      117263 : }
     128             : 
     129       69828 : bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
     130       69828 :     CCoinsMap::iterator it = FetchCoin(outpoint);
     131       69828 :     if (it == cacheCoins.end()) return false;
     132       69822 :     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       69822 :     if (moveout) {
     140       34931 :         *moveout = std::move(it->second.coin);
     141       34931 :     }
     142       69822 :     if (it->second.flags & CCoinsCacheEntry::FRESH) {
     143        8786 :         cacheCoins.erase(it);
     144        8786 :     } else {
     145       61036 :         it->second.flags |= CCoinsCacheEntry::DIRTY;
     146       61036 :         it->second.coin.Clear();
     147             :     }
     148       69822 :     return true;
     149       69828 : }
     150             : 
     151         218 : static const Coin coinEmpty;
     152             : 
     153    21840933 : const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
     154    21840933 :     CCoinsMap::const_iterator it = FetchCoin(outpoint);
     155    21840933 :     if (it == cacheCoins.end()) {
     156    21335814 :         return coinEmpty;
     157             :     } else {
     158      505119 :         return it->second.coin;
     159             :     }
     160    21840933 : }
     161             : 
     162     1356352 : bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const {
     163     1356352 :     CCoinsMap::const_iterator it = FetchCoin(outpoint);
     164     1356352 :     return (it != cacheCoins.end() && !it->second.coin.IsSpent());
     165             : }
     166             : 
     167      215364 : bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const {
     168      215364 :     CCoinsMap::const_iterator it = cacheCoins.find(outpoint);
     169      215364 :     return (it != cacheCoins.end() && !it->second.coin.IsSpent());
     170             : }
     171             : 
     172       99881 : uint256 CCoinsViewCache::GetBestBlock() const {
     173       99881 :     if (hashBlock.IsNull())
     174       50195 :         hashBlock = base->GetBestBlock();
     175       99881 :     return hashBlock;
     176             : }
     177             : 
     178       25461 : void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
     179       25461 :     hashBlock = hashBlockIn;
     180       25461 : }
     181             : 
     182       26047 : bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn, bool erase) {
     183      556859 :     for (CCoinsMap::iterator it = mapCoins.begin();
     184      556859 :             it != mapCoins.end();
     185      530812 :             it = erase ? mapCoins.erase(it) : std::next(it)) {
     186             :         // Ignore non-dirty entries (optimization).
     187      530820 :         if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
     188      312508 :             continue;
     189             :         }
     190      218312 :         CCoinsMap::iterator itUs = cacheCoins.find(it->first);
     191      218312 :         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      160321 :             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      160320 :                 CCoinsCacheEntry& entry = cacheCoins[it->first];
     198      160320 :                 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      140966 :                     entry.coin = std::move(it->second.coin);
     203      140966 :                 } else {
     204       19354 :                     entry.coin = it->second.coin;
     205             :                 }
     206      160320 :                 cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
     207      160320 :                 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      160320 :                 if (it->second.flags & CCoinsCacheEntry::FRESH) {
     212       37094 :                     entry.flags |= CCoinsCacheEntry::FRESH;
     213       37094 :                 }
     214      160320 :             }
     215      160321 :         } else {
     216             :             // Found the entry in the parent cache
     217       57991 :             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       57983 :             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        3758 :                 cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
     229        3758 :                 cacheCoins.erase(itUs);
     230        3758 :             } else {
     231             :                 // A normal modification.
     232       54225 :                 cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
     233       54225 :                 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       44785 :                     itUs->second.coin = std::move(it->second.coin);
     238       44785 :                 } else {
     239        9440 :                     itUs->second.coin = it->second.coin;
     240             :                 }
     241       54225 :                 cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
     242       54225 :                 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      218304 :     }
     250       26039 :     hashBlock = hashBlockIn;
     251       26039 :     return true;
     252           0 : }
     253             : 
     254       25991 : bool CCoinsViewCache::Flush() {
     255       25991 :     bool fOk = base->BatchWrite(cacheCoins, hashBlock, /*erase=*/true);
     256       25991 :     if (fOk) {
     257       25991 :         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       25991 :         ReallocateCache();
     262       25991 :     }
     263       25991 :     cachedCoinsUsage = 0;
     264       25991 :     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       11917 : void CCoinsViewCache::Uncache(const COutPoint& hash)
     285             : {
     286       11917 :     CCoinsMap::iterator it = cacheCoins.find(hash);
     287       11917 :     if (it != cacheCoins.end() && it->second.flags == 0) {
     288        1147 :         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        1147 :         cacheCoins.erase(it);
     296        1147 :     }
     297       11917 : }
     298             : 
     299       99445 : unsigned int CCoinsViewCache::GetCacheSize() const {
     300       99445 :     return cacheCoins.size();
     301             : }
     302             : 
     303       31021 : bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
     304             : {
     305       31021 :     if (!tx.IsCoinBase()) {
     306       33064 :         for (unsigned int i = 0; i < tx.vin.size(); i++) {
     307        2046 :             if (!HaveCoin(tx.vin[i].prevout)) {
     308           3 :                 return false;
     309             :             }
     310        2043 :         }
     311       31018 :     }
     312       31018 :     return true;
     313       31021 : }
     314             : 
     315       25991 : void CCoinsViewCache::ReallocateCache()
     316             : {
     317             :     // Cache should be empty when we're calling this.
     318       25991 :     assert(cacheCoins.size() == 0);
     319       25991 :     cacheCoins.~CCoinsMap();
     320       25991 :     m_cache_coins_memory_resource.~CCoinsMapMemoryResource();
     321       25991 :     ::new (&m_cache_coins_memory_resource) CCoinsMapMemoryResource{};
     322       25991 :     ::new (&cacheCoins) CCoinsMap{0, SaltedOutpointHasher{/*deterministic=*/m_deterministic}, CCoinsMap::key_equal{}, &m_cache_coins_memory_resource};
     323       25991 : }
     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         218 : static const size_t MAX_OUTPUTS_PER_BLOCK = MaxBlockSize() /  ::GetSerializeSize(CTxOut(), PROTOCOL_VERSION);
     343             : 
     344         172 : const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
     345             : {
     346         172 :     COutPoint iter(txid, 0);
     347    20444596 :     while (iter.n < MAX_OUTPUTS_PER_BLOCK) {
     348    20444504 :         const Coin& alternate = view.AccessCoin(iter);
     349    20444504 :         if (!alternate.IsSpent()) return alternate;
     350    20444424 :         ++iter.n;
     351             :     }
     352          92 :     return coinEmpty;
     353         172 : }
     354             : 
     355       68649 : bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) const {
     356             :     try {
     357       68649 :         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