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