/ src / txdb.cpp
txdb.cpp
  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/vector.h>
 18  
 19  #include <cassert>
 20  #include <cstdlib>
 21  #include <iterator>
 22  #include <utility>
 23  
 24  static constexpr uint8_t DB_COIN{'C'};
 25  static constexpr uint8_t DB_BEST_BLOCK{'B'};
 26  static constexpr uint8_t DB_HEAD_BLOCKS{'H'};
 27  // Keys used in previous version that might still be found in the DB:
 28  static constexpr uint8_t DB_COINS{'c'};
 29  
 30  // Threshold for warning when writing this many dirty cache entries to disk.
 31  static constexpr size_t WARN_FLUSH_COINS_COUNT{10'000'000};
 32  
 33  bool CCoinsViewDB::NeedsUpgrade()
 34  {
 35      std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()};
 36      // DB_COINS was deprecated in v0.15.0, commit
 37      // 1088b02f0ccd7358d2b7076bb9e122d59d502d02
 38      cursor->Seek(std::make_pair(DB_COINS, uint256{}));
 39      return cursor->Valid();
 40  }
 41  
 42  namespace {
 43  
 44  struct CoinEntry {
 45      COutPoint* outpoint;
 46      uint8_t key{DB_COIN};
 47      explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)) {}
 48  
 49      SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
 50  };
 51  
 52  } // namespace
 53  
 54  CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) :
 55      m_db_params{std::move(db_params)},
 56      m_options{std::move(options)},
 57      m_db{std::make_unique<CDBWrapper>(m_db_params)} { }
 58  
 59  void CCoinsViewDB::ResizeCache(size_t new_cache_size)
 60  {
 61      // We can't do this operation with an in-memory DB since we'll lose all the coins upon
 62      // reset.
 63      if (!m_db_params.memory_only) {
 64          // Have to do a reset first to get the original `m_db` state to release its
 65          // filesystem lock.
 66          m_db.reset();
 67          m_db_params.cache_bytes = new_cache_size;
 68          m_db_params.wipe_data = false;
 69          m_db = std::make_unique<CDBWrapper>(m_db_params);
 70      }
 71  }
 72  
 73  std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint) const
 74  {
 75      if (Coin coin; m_db->Read(CoinEntry(&outpoint), coin)) {
 76          Assert(!coin.IsSpent()); // The UTXO database should never contain spent coins
 77          return coin;
 78      }
 79      return std::nullopt;
 80  }
 81  
 82  std::optional<Coin> CCoinsViewDB::PeekCoin(const COutPoint& outpoint) const
 83  {
 84      return GetCoin(outpoint);
 85  }
 86  
 87  bool CCoinsViewDB::HaveCoin(const COutPoint& outpoint) const
 88  {
 89      return m_db->Exists(CoinEntry(&outpoint));
 90  }
 91  
 92  uint256 CCoinsViewDB::GetBestBlock() const {
 93      uint256 hashBestChain;
 94      if (!m_db->Read(DB_BEST_BLOCK, hashBestChain))
 95          return uint256();
 96      return hashBestChain;
 97  }
 98  
 99  std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
100      std::vector<uint256> vhashHeadBlocks;
101      if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
102          return std::vector<uint256>();
103      }
104      return vhashHeadBlocks;
105  }
106  
107  void CCoinsViewDB::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& block_hash)
108  {
109      CDBBatch batch(*m_db);
110      size_t count = 0;
111      const size_t dirty_count{cursor.GetDirtyCount()};
112      assert(!block_hash.IsNull());
113  
114      uint256 old_tip = GetBestBlock();
115      if (old_tip.IsNull()) {
116          // We may be in the middle of replaying.
117          std::vector<uint256> old_heads = GetHeadBlocks();
118          if (old_heads.size() == 2) {
119              if (old_heads[0] != block_hash) {
120                  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");
121              }
122              assert(old_heads[0] == block_hash);
123              old_tip = old_heads[1];
124          }
125      }
126  
127      if (dirty_count > WARN_FLUSH_COINS_COUNT) LogWarning("Flushing large (%d entries) UTXO set to disk, it may take several minutes", dirty_count);
128      LOG_TIME_MILLIS_WITH_CATEGORY(strprintf("write coins cache to disk (%d out of %d cached coins)",
129          dirty_count, cursor.GetTotalCount()), BCLog::BENCH);
130  
131      // In the first batch, mark the database as being in the middle of a
132      // transition from old_tip to block_hash.
133      // A vector is used for future extensibility, as we may want to support
134      // interrupting after partial writes from multiple independent reorgs.
135      batch.Erase(DB_BEST_BLOCK);
136      batch.Write(DB_HEAD_BLOCKS, Vector(block_hash, old_tip));
137  
138      for (auto it{cursor.Begin()}; it != cursor.End();) {
139          if (it->second.IsDirty()) {
140              CoinEntry entry(&it->first);
141              if (it->second.coin.IsSpent()) {
142                  batch.Erase(entry);
143              } else {
144                  batch.Write(entry, it->second.coin);
145              }
146          }
147          count++;
148          it = cursor.NextAndMaybeErase(*it);
149          if (batch.ApproximateSize() > m_options.batch_write_bytes) {
150              LogDebug(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.ApproximateSize() / double(1_MiB));
151  
152              m_db->WriteBatch(batch);
153              batch.Clear();
154              if (m_options.simulate_crash_ratio) {
155                  static FastRandomContext rng;
156                  if (rng.randrange(m_options.simulate_crash_ratio) == 0) {
157                      LogError("Simulating a crash. Goodbye.");
158                      _Exit(0);
159                  }
160              }
161          }
162      }
163  
164      // In the last batch, mark the database as consistent with block_hash again.
165      batch.Erase(DB_HEAD_BLOCKS);
166      batch.Write(DB_BEST_BLOCK, block_hash);
167  
168      LogDebug(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.ApproximateSize() / double(1_MiB));
169      m_db->WriteBatch(batch);
170      LogDebug(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...", (unsigned int)dirty_count, (unsigned int)count);
171  }
172  
173  size_t CCoinsViewDB::EstimateSize() const
174  {
175      return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
176  }
177  
178  /** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
179  class CCoinsViewDBCursor: public CCoinsViewCursor
180  {
181  public:
182      // Prefer using CCoinsViewDB::Cursor() since we want to perform some
183      // cache warmup on instantiation.
184      CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256& in_block_hash):
185          CCoinsViewCursor(in_block_hash), pcursor(pcursorIn) {}
186      ~CCoinsViewDBCursor() = default;
187  
188      bool GetKey(COutPoint &key) const override;
189      bool GetValue(Coin &coin) const override;
190  
191      bool Valid() const override;
192      void Next() override;
193  
194  private:
195      std::unique_ptr<CDBIterator> pcursor;
196      std::pair<char, COutPoint> keyTmp;
197  
198      friend class CCoinsViewDB;
199  };
200  
201  std::unique_ptr<CCoinsViewCursor> CCoinsViewDB::Cursor() const
202  {
203      auto i = std::make_unique<CCoinsViewDBCursor>(
204          const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock());
205      /* It seems that there are no "const iterators" for LevelDB.  Since we
206         only need read operations on it, use a const-cast to get around
207         that restriction.  */
208      i->pcursor->Seek(DB_COIN);
209      // Cache key of first record
210      if (i->pcursor->Valid()) {
211          CoinEntry entry(&i->keyTmp.second);
212          i->pcursor->GetKey(entry);
213          i->keyTmp.first = entry.key;
214      } else {
215          i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false
216      }
217      return i;
218  }
219  
220  bool CCoinsViewDBCursor::GetKey(COutPoint &key) const
221  {
222      // Return cached key
223      if (keyTmp.first == DB_COIN) {
224          key = keyTmp.second;
225          return true;
226      }
227      return false;
228  }
229  
230  bool CCoinsViewDBCursor::GetValue(Coin &coin) const
231  {
232      return pcursor->GetValue(coin);
233  }
234  
235  bool CCoinsViewDBCursor::Valid() const
236  {
237      return keyTmp.first == DB_COIN;
238  }
239  
240  void CCoinsViewDBCursor::Next()
241  {
242      pcursor->Next();
243      CoinEntry entry(&keyTmp.second);
244      if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
245          keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
246      } else {
247          keyTmp.first = entry.key;
248      }
249  }