LCOV - code coverage report
Current view: top level - src - txdb.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 79.9 % 144 115
Test Date: 2026-09-23 06:36:48 Functions: 87.5 % 24 21
Branches: 46.2 % 197 91

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2                 :             : // Copyright (c) 2009-present The Bitcoin Core developers
       3                 :             : // Distributed under the MIT software license, see the accompanying
       4                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5                 :             : 
       6                 :             : #include <txdb.h>
       7                 :             : 
       8                 :             : #include <coins.h>
       9                 :             : #include <dbwrapper.h>
      10                 :             : #include <logging/timer.h>
      11                 :             : #include <primitives/transaction.h>
      12                 :             : #include <random.h>
      13                 :             : #include <serialize.h>
      14                 :             : #include <uint256.h>
      15                 :             : #include <util/byte_units.h>
      16                 :             : #include <util/log.h>
      17                 :             : #include <util/threadnames.h>
      18                 :             : #include <util/vector.h>
      19                 :             : 
      20                 :             : #include <cassert>
      21                 :             : #include <chrono>
      22                 :             : #include <cstdlib>
      23                 :             : #include <exception>
      24                 :             : #include <future>
      25                 :             : #include <iterator>
      26                 :             : #include <utility>
      27                 :             : 
      28                 :             : static constexpr uint8_t DB_COIN{'C'};
      29                 :             : static constexpr uint8_t DB_BEST_BLOCK{'B'};
      30                 :             : static constexpr uint8_t DB_HEAD_BLOCKS{'H'};
      31                 :             : // Keys used in previous version that might still be found in the DB:
      32                 :             : static constexpr uint8_t DB_COINS{'c'};
      33                 :             : 
      34                 :             : // Threshold for warning when writing this many dirty cache entries to disk.
      35                 :             : static constexpr size_t WARN_FLUSH_COINS_COUNT{10'000'000};
      36                 :             : 
      37                 :        3178 : bool CCoinsViewDB::NeedsUpgrade()
      38                 :             : {
      39         [ +  - ]:        3178 :     std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()};
      40                 :             :     // DB_COINS was deprecated in v0.15.0, commit
      41                 :             :     // 1088b02f0ccd7358d2b7076bb9e122d59d502d02
      42         [ +  - ]:        3178 :     cursor->Seek(std::make_pair(DB_COINS, uint256{}));
      43         [ +  - ]:        6356 :     return cursor->Valid();
      44                 :        3178 : }
      45                 :             : 
      46                 :             : namespace {
      47                 :             : 
      48                 :             : struct CoinEntry {
      49                 :             :     COutPoint* outpoint;
      50                 :             :     uint8_t key{DB_COIN};
      51         [ +  - ]:     3644012 :     explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)) {}
      52                 :             : 
      53                 :    21015544 :     SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
      54                 :             : };
      55                 :             : 
      56                 :             : } // namespace
      57                 :             : 
      58                 :       12120 : CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) :
      59                 :       12120 :     m_db_params{std::move(db_params)},
      60                 :       12120 :     m_options{std::move(options)},
      61         [ +  - ]:       12120 :     m_db{std::make_unique<CDBWrapper>(m_db_params)} { }
      62                 :             : 
      63                 :       12120 : CCoinsViewDB::~CCoinsViewDB()
      64                 :             : {
      65         [ +  + ]:       12120 :     if (m_compaction.valid()) {
      66         [ +  + ]:        3219 :         if (m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
      67         [ -  + ]:        3830 :             LogInfo("Waiting for background chainstate compaction of %s", fs::PathToString(m_db_params.path));
      68                 :             :         }
      69                 :        3219 :         m_compaction.wait();
      70                 :             :     }
      71         [ +  + ]:       15339 : }
      72                 :             : 
      73                 :        4302 : void CCoinsViewDB::ResizeCache(size_t new_cache_size)
      74                 :             : {
      75                 :             :     // We can't do this operation with an in-memory DB since we'll lose all the coins upon
      76                 :             :     // reset.
      77         [ -  + ]:        4302 :     if (!m_db_params.memory_only) {
      78                 :           0 :         LOCK(m_db_mutex);
      79                 :             :         // Have to do a reset first to get the original `m_db` state to release its
      80                 :             :         // filesystem lock.
      81         [ #  # ]:           0 :         m_db.reset();
      82                 :           0 :         m_db_params.cache_bytes = new_cache_size;
      83                 :           0 :         m_db_params.wipe_data = false;
      84   [ #  #  #  # ]:           0 :         m_db = std::make_unique<CDBWrapper>(m_db_params);
      85                 :           0 :     }
      86                 :        4302 : }
      87                 :             : 
      88                 :     3635312 : std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint) const
      89                 :             : {
      90                 :     3635312 :     Coin coin;
      91         [ +  - ]:     3635312 :     const CDBWrapper::ReadStatus res = m_db->TryRead(CoinEntry(&outpoint), coin);
      92         [ -  + ]:     3635312 :     if (!res) {
      93                 :             :         // Propagate errors so CCoinsViewErrorCatcher triggers a clean shutdown.
      94      [ #  #  # ]:           0 :         switch (const auto& [err_code, err_msg] = res.error(); err_code) {
      95                 :           0 :             case CDBWrapper::ReadFailure::Code::DeserializationError:
      96         [ #  # ]:           0 :                 throw dbwrapper_error{strprintf("Coin deserialization failure: %s", err_msg)};
      97                 :           0 :             case CDBWrapper::ReadFailure::Code::DatabaseError:
      98         [ #  # ]:           0 :                 throw dbwrapper_error{strprintf("Coin DB read failure: %s", err_msg)};
      99                 :             :         } // no default case, so the compiler can warn about missing cases
     100                 :           0 :         std::abort(); // unreachable
     101                 :             :     }
     102                 :             : 
     103                 :             :     // Check whether the coin exists
     104   [ +  -  +  + ]:     3635312 :     if (!res.value()) return std::nullopt;
     105                 :             :     // Coin found, ensure UTXO database never contains spent coins
     106         [ -  + ]:      453353 :     Assert(!coin.IsSpent());
     107                 :      453353 :     return coin;
     108                 :     3635312 : }
     109                 :             : 
     110                 :     1924916 : std::optional<Coin> CCoinsViewDB::PeekCoin(const COutPoint& outpoint) const
     111                 :             : {
     112                 :     1924916 :     return GetCoin(outpoint);
     113                 :             : }
     114                 :             : 
     115                 :        8700 : bool CCoinsViewDB::HaveCoin(const COutPoint& outpoint) const
     116                 :             : {
     117                 :        8700 :     return m_db->Exists(CoinEntry(&outpoint));
     118                 :             : }
     119                 :             : 
     120                 :     3304634 : uint256 CCoinsViewDB::GetBestBlock() const {
     121                 :     3304634 :     uint256 hashBestChain;
     122         [ +  + ]:     3304634 :     if (!m_db->Read(DB_BEST_BLOCK, hashBestChain))
     123                 :       21477 :         return uint256();
     124                 :     3283157 :     return hashBestChain;
     125                 :             : }
     126                 :             : 
     127                 :       32801 : std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
     128                 :       32801 :     std::vector<uint256> vhashHeadBlocks;
     129   [ +  -  +  - ]:       32801 :     if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
     130                 :       32801 :         return std::vector<uint256>();
     131                 :             :     }
     132                 :           0 :     return vhashHeadBlocks;
     133                 :       32801 : }
     134                 :             : 
     135                 :     3148214 : void CCoinsViewDB::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& block_hash)
     136                 :             : {
     137                 :     3148214 :     CDBBatch batch(*m_db);
     138                 :     3148214 :     size_t count = 0;
     139                 :     3148214 :     const size_t dirty_count{cursor.GetDirtyCount()};
     140         [ -  + ]:     6296428 :     assert(!block_hash.IsNull());
     141                 :             : 
     142         [ +  - ]:     3148214 :     uint256 old_tip = GetBestBlock();
     143         [ +  + ]:     3148214 :     if (old_tip.IsNull()) {
     144                 :             :         // We may be in the middle of replaying.
     145         [ +  - ]:        8282 :         std::vector<uint256> old_heads = GetHeadBlocks();
     146   [ -  +  -  + ]:        8282 :         if (old_heads.size() == 2) {
     147         [ #  # ]:           0 :             if (old_heads[0] != block_hash) {
     148         [ #  # ]:           0 :                 LogError("The coins database detected an inconsistent state, likely due to a previous crash or shutdown. You will need to restart bitcoind with the -reindex-chainstate or -reindex configuration option.\n");
     149                 :             :             }
     150         [ #  # ]:           0 :             assert(old_heads[0] == block_hash);
     151                 :           0 :             old_tip = old_heads[1];
     152                 :             :         }
     153                 :        8282 :     }
     154                 :             : 
     155   [ -  +  -  - ]:     3148214 :     if (dirty_count > WARN_FLUSH_COINS_COUNT) LogWarning("Flushing large (%d entries) UTXO set to disk, it may take several minutes", dirty_count);
     156   [ +  -  +  -  :     6296428 :     LOG_TIME_MILLIS_WITH_CATEGORY(strprintf("write coins cache to disk (%d out of %d cached coins)",
                   +  - ]
     157                 :             :         dirty_count, cursor.GetTotalCount()), BCLog::BENCH);
     158                 :             : 
     159                 :             :     // In the first batch, mark the database as being in the middle of a
     160                 :             :     // transition from old_tip to block_hash.
     161                 :             :     // A vector is used for future extensibility, as we may want to support
     162                 :             :     // interrupting after partial writes from multiple independent reorgs.
     163         [ +  - ]:     3148214 :     batch.Erase(DB_BEST_BLOCK);
     164   [ +  -  +  - ]:     3148214 :     batch.Write(DB_HEAD_BLOCKS, Vector(block_hash, old_tip));
     165                 :             : 
     166         [ +  + ]:     3372530 :     for (auto it{cursor.Begin()}; it != cursor.End();) {
     167         [ +  - ]:      224316 :         if (it->second.IsDirty()) {
     168                 :      224316 :             CoinEntry entry(&it->first);
     169         [ +  + ]:      224316 :             if (it->second.coin.IsSpent()) {
     170         [ +  - ]:        3652 :                 batch.Erase(entry);
     171                 :             :             } else {
     172         [ +  - ]:      220664 :                 batch.Write(entry, it->second.coin);
     173                 :             :             }
     174                 :             :         }
     175                 :      224316 :         count++;
     176                 :      224316 :         it = cursor.NextAndMaybeErase(*it);
     177   [ +  -  -  + ]:      224316 :         if (batch.ApproximateSize() > m_options.batch_write_bytes) {
     178   [ #  #  #  #  :           0 :             LogDebug(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.ApproximateSize() / double(1_MiB));
             #  #  #  # ]
     179                 :             : 
     180         [ #  # ]:           0 :             m_db->WriteBatch(batch);
     181         [ #  # ]:           0 :             batch.Clear();
     182         [ #  # ]:           0 :             if (m_options.simulate_crash_ratio) {
     183   [ #  #  #  # ]:           0 :                 static FastRandomContext rng;
     184         [ #  # ]:           0 :                 if (rng.randrange(m_options.simulate_crash_ratio) == 0) {
     185         [ #  # ]:           0 :                     LogError("Simulating a crash. Goodbye.");
     186                 :           0 :                     _Exit(0);
     187                 :             :                 }
     188                 :             :             }
     189                 :             :         }
     190                 :             :     }
     191                 :             : 
     192                 :             :     // In the last batch, mark the database as consistent with block_hash again.
     193         [ +  - ]:     3148214 :     batch.Erase(DB_HEAD_BLOCKS);
     194         [ +  - ]:     3148214 :     batch.Write(DB_BEST_BLOCK, block_hash);
     195                 :             : 
     196   [ +  -  +  +  :     3148214 :     LogDebug(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.ApproximateSize() / double(1_MiB));
             +  -  +  - ]
     197         [ +  - ]:     3148214 :     m_db->WriteBatch(batch);
     198   [ +  -  +  +  :     3148214 :     LogDebug(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...", (unsigned int)dirty_count, (unsigned int)count);
                   +  - ]
     199                 :     3148214 : }
     200                 :             : 
     201                 :      150259 : size_t CCoinsViewDB::EstimateSize() const
     202                 :             : {
     203                 :      150259 :     return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
     204                 :             : }
     205                 :             : 
     206                 :           0 : std::optional<std::string> CCoinsViewDB::GetDBProperty(const std::string& property)
     207                 :             : {
     208                 :           0 :     return m_db->GetProperty(property);
     209                 :             : }
     210                 :             : 
     211                 :      422336 : std::shared_future<void> CCoinsViewDB::CompactFullAsync()
     212                 :             : {
     213                 :      422336 :     AssertLockHeld(::cs_main);
     214   [ +  +  +  +  :      422336 :     if (m_compaction.valid() && m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) return m_compaction;
                   +  - ]
     215                 :       25466 :     m_compaction = std::async(std::launch::async, [this] {
     216                 :       25466 :         try {
     217   [ +  -  +  - ]:       25466 :             util::ThreadRename("utxocompact");
     218         [ +  - ]:       25466 :             LOCK(m_db_mutex);
     219                 :             : 
     220   [ +  -  +  +  :       26053 :             LogDebug(BCLog::COINDB, "Starting chainstate compaction of %s", fs::PathToString(m_db_params.path));
             -  +  +  - ]
     221         [ +  - ]:       25466 :             m_db->CompactFull();
     222   [ +  -  +  +  :       26053 :             LogDebug(BCLog::COINDB, "Finished chainstate compaction of %s", fs::PathToString(m_db_params.path));
          -  +  +  -  +  
                      - ]
     223         [ -  - ]:       25466 :         } catch (const std::exception& e) {
     224         [ -  - ]:           0 :             LogWarning("Failed chainstate compaction (%s)", e.what());
     225                 :           0 :         }
     226   [ -  +  -  + ]:       50932 :     }).share();
     227         [ +  - ]:       25466 :     return m_compaction;
     228                 :             : }
     229                 :             : 
     230                 :             : /** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
     231                 :             : class CCoinsViewDBCursor: public CCoinsViewCursor
     232                 :             : {
     233                 :             : public:
     234                 :             :     // Prefer using CCoinsViewDB::Cursor() since we want to perform some
     235                 :             :     // cache warmup on instantiation.
     236                 :      137620 :     CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256& in_block_hash):
     237                 :      137620 :         CCoinsViewCursor(in_block_hash), pcursor(pcursorIn) {}
     238                 :      137620 :     ~CCoinsViewDBCursor() = default;
     239                 :             : 
     240                 :             :     bool GetKey(COutPoint &key) const override;
     241                 :             :     bool GetValue(Coin &coin) const override;
     242                 :             : 
     243                 :             :     bool Valid() const override;
     244                 :             :     void Next() override;
     245                 :             : 
     246                 :             : private:
     247                 :             :     std::unique_ptr<CDBIterator> pcursor;
     248                 :             :     std::pair<char, COutPoint> keyTmp;
     249                 :             : 
     250                 :             :     friend class CCoinsViewDB;
     251                 :             : };
     252                 :             : 
     253                 :      137620 : std::unique_ptr<CCoinsViewCursor> CCoinsViewDB::Cursor() const
     254                 :             : {
     255                 :      137620 :     auto i = std::make_unique<CCoinsViewDBCursor>(
     256                 :      137620 :         const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock());
     257                 :             :     /* It seems that there are no "const iterators" for LevelDB.  Since we
     258                 :             :        only need read operations on it, use a const-cast to get around
     259                 :             :        that restriction.  */
     260         [ +  - ]:      137620 :     i->pcursor->Seek(DB_COIN);
     261                 :             :     // Cache key of first record
     262   [ +  -  +  + ]:      137620 :     if (i->pcursor->Valid()) {
     263         [ +  - ]:      130492 :         CoinEntry entry(&i->keyTmp.second);
     264         [ +  - ]:      130492 :         i->pcursor->GetKey(entry);
     265                 :      130492 :         i->keyTmp.first = entry.key;
     266                 :             :     } else {
     267                 :        7128 :         i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false
     268                 :             :     }
     269                 :      137620 :     return i;
     270                 :      137620 : }
     271                 :             : 
     272                 :     6636424 : bool CCoinsViewDBCursor::GetKey(COutPoint &key) const
     273                 :             : {
     274                 :             :     // Return cached key
     275         [ +  - ]:     6636424 :     if (keyTmp.first == DB_COIN) {
     276                 :     6636424 :         key = keyTmp.second;
     277                 :     6636424 :         return true;
     278                 :             :     }
     279                 :             :     return false;
     280                 :             : }
     281                 :             : 
     282                 :     6636424 : bool CCoinsViewDBCursor::GetValue(Coin &coin) const
     283                 :             : {
     284                 :     6636424 :     return pcursor->GetValue(coin);
     285                 :             : }
     286                 :             : 
     287                 :     6765344 : bool CCoinsViewDBCursor::Valid() const
     288                 :             : {
     289                 :     6765344 :     return keyTmp.first == DB_COIN;
     290                 :             : }
     291                 :             : 
     292                 :     6636424 : void CCoinsViewDBCursor::Next()
     293                 :             : {
     294                 :     6636424 :     pcursor->Next();
     295                 :     6636424 :     CoinEntry entry(&keyTmp.second);
     296   [ +  +  -  + ]:     6636424 :     if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
     297                 :      127472 :         keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
     298                 :             :     } else {
     299                 :     6508952 :         keyTmp.first = entry.key;
     300                 :             :     }
     301                 :     6636424 : }
        

Generated by: LCOV version 2.5.0-full