Line data Source code
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 1 : bool CCoinsViewDB::NeedsUpgrade() 29 : { 30 1 : std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()}; 31 : // DB_COINS was deprecated in v0.15.0, commit 32 : // 1088b02f0ccd7358d2b7076bb9e122d59d502d02 33 1 : cursor->Seek(std::make_pair(DB_COINS, uint256{})); 34 1 : return cursor->Valid(); 35 1 : } 36 : 37 : namespace { 38 : 39 : struct CoinEntry { 40 : COutPoint* outpoint; 41 : uint8_t key; 42 19256 : explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)), key(DB_COIN) {} 43 : 44 57768 : SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); } 45 : }; 46 : 47 : } // namespace 48 : 49 1 : CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) : 50 1 : m_db_params{std::move(db_params)}, 51 1 : m_options{std::move(options)}, 52 2 : m_db{std::make_unique<CDBWrapper>(m_db_params)} { } 53 : 54 1 : 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 1 : 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 0 : m_db.reset(); 62 0 : m_db_params.cache_bytes = new_cache_size; 63 0 : m_db_params.wipe_data = false; 64 0 : m_db = std::make_unique<CDBWrapper>(m_db_params); 65 0 : } 66 1 : } 67 : 68 19256 : bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const { 69 19256 : return m_db->Read(CoinEntry(&outpoint), coin); 70 : } 71 : 72 0 : bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const { 73 0 : return m_db->Exists(CoinEntry(&outpoint)); 74 2 : } 75 : 76 3 : uint256 CCoinsViewDB::GetBestBlock() const { 77 3 : uint256 hashBestChain; 78 3 : if (!m_db->Read(DB_BEST_BLOCK, hashBestChain)) 79 3 : return uint256(); 80 0 : return hashBestChain; 81 3 : } 82 : 83 1 : std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const { 84 1 : std::vector<uint256> vhashHeadBlocks; 85 1 : if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) { 86 1 : return std::vector<uint256>(); 87 : } 88 0 : return vhashHeadBlocks; 89 1 : } 90 : 91 0 : bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase) { 92 0 : CDBBatch batch(*m_db); 93 0 : size_t count = 0; 94 0 : size_t changed = 0; 95 0 : assert(!hashBlock.IsNull()); 96 : 97 0 : uint256 old_tip = GetBestBlock(); 98 0 : if (old_tip.IsNull()) { 99 : // We may be in the middle of replaying. 100 0 : std::vector<uint256> old_heads = GetHeadBlocks(); 101 0 : if (old_heads.size() == 2) { 102 0 : if (old_heads[0] != hashBlock) { 103 0 : 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"); 104 0 : } 105 0 : assert(old_heads[0] == hashBlock); 106 0 : old_tip = old_heads[1]; 107 0 : } 108 0 : } 109 : 110 : // In the first batch, mark the database as being in the middle of a 111 : // transition from old_tip to hashBlock. 112 : // A vector is used for future extensibility, as we may want to support 113 : // interrupting after partial writes from multiple independent reorgs. 114 0 : batch.Erase(DB_BEST_BLOCK); 115 0 : batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip)); 116 : 117 0 : for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { 118 0 : if (it->second.flags & CCoinsCacheEntry::DIRTY) { 119 0 : CoinEntry entry(&it->first); 120 0 : if (it->second.coin.IsSpent()) 121 0 : batch.Erase(entry); 122 : else 123 0 : batch.Write(entry, it->second.coin); 124 0 : changed++; 125 0 : } 126 0 : count++; 127 0 : it = erase ? mapCoins.erase(it) : std::next(it); 128 0 : if (batch.SizeEstimate() > m_options.batch_write_bytes) { 129 0 : LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0)); 130 0 : m_db->WriteBatch(batch); 131 0 : batch.Clear(); 132 0 : if (m_options.simulate_crash_ratio) { 133 0 : static FastRandomContext rng; 134 0 : if (rng.randrange(m_options.simulate_crash_ratio) == 0) { 135 0 : LogPrintf("Simulating a crash. Goodbye.\n"); 136 0 : _Exit(0); 137 : } 138 0 : } 139 0 : } 140 : } 141 : 142 : // In the last batch, mark the database as consistent with hashBlock again. 143 0 : batch.Erase(DB_HEAD_BLOCKS); 144 0 : batch.Write(DB_BEST_BLOCK, hashBlock); 145 : 146 0 : LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0)); 147 0 : bool ret = m_db->WriteBatch(batch); 148 0 : LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); 149 0 : return ret; 150 0 : } 151 : 152 0 : size_t CCoinsViewDB::EstimateSize() const 153 : { 154 0 : return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1)); 155 : } 156 : 157 : /** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */ 158 : class CCoinsViewDBCursor: public CCoinsViewCursor 159 : { 160 : public: 161 : // Prefer using CCoinsViewDB::Cursor() since we want to perform some 162 : // cache warmup on instantiation. 163 0 : CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256&hashBlockIn): 164 0 : CCoinsViewCursor(hashBlockIn), pcursor(pcursorIn) {} 165 0 : ~CCoinsViewDBCursor() = default; 166 : 167 : bool GetKey(COutPoint &key) const override; 168 : bool GetValue(Coin &coin) const override; 169 : 170 : bool Valid() const override; 171 : void Next() override; 172 : 173 : private: 174 : std::unique_ptr<CDBIterator> pcursor; 175 : std::pair<char, COutPoint> keyTmp; 176 : 177 : friend class CCoinsViewDB; 178 : }; 179 : 180 0 : std::unique_ptr<CCoinsViewCursor> CCoinsViewDB::Cursor() const 181 : { 182 0 : auto i = std::make_unique<CCoinsViewDBCursor>( 183 0 : const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock()); 184 : /* It seems that there are no "const iterators" for LevelDB. Since we 185 : only need read operations on it, use a const-cast to get around 186 : that restriction. */ 187 0 : i->pcursor->Seek(DB_COIN); 188 : // Cache key of first record 189 0 : if (i->pcursor->Valid()) { 190 0 : CoinEntry entry(&i->keyTmp.second); 191 0 : i->pcursor->GetKey(entry); 192 0 : i->keyTmp.first = entry.key; 193 0 : } else { 194 0 : i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false 195 : } 196 0 : return i; 197 0 : } 198 : 199 0 : bool CCoinsViewDBCursor::GetKey(COutPoint &key) const 200 : { 201 : // Return cached key 202 0 : if (keyTmp.first == DB_COIN) { 203 0 : key = keyTmp.second; 204 0 : return true; 205 : } 206 0 : return false; 207 0 : } 208 : 209 0 : bool CCoinsViewDBCursor::GetValue(Coin &coin) const 210 : { 211 0 : return pcursor->GetValue(coin); 212 : } 213 : 214 0 : bool CCoinsViewDBCursor::Valid() const 215 : { 216 0 : return keyTmp.first == DB_COIN; 217 : } 218 : 219 0 : void CCoinsViewDBCursor::Next() 220 : { 221 0 : pcursor->Next(); 222 0 : CoinEntry entry(&keyTmp.second); 223 0 : if (!pcursor->Valid() || !pcursor->GetKey(entry)) { 224 0 : keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false 225 0 : } else { 226 0 : keyTmp.first = entry.key; 227 : } 228 0 : }