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 }