Branch data Line data Source code
# 1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto
# 2 : : // Copyright (c) 2009-2021 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 <chain.h>
# 9 : : #include <pow.h>
# 10 : : #include <random.h>
# 11 : : #include <shutdown.h>
# 12 : : #include <uint256.h>
# 13 : : #include <util/system.h>
# 14 : : #include <util/translation.h>
# 15 : : #include <util/vector.h>
# 16 : :
# 17 : : #include <stdint.h>
# 18 : :
# 19 : : static constexpr uint8_t DB_COIN{'C'};
# 20 : : static constexpr uint8_t DB_BLOCK_FILES{'f'};
# 21 : : static constexpr uint8_t DB_BLOCK_INDEX{'b'};
# 22 : :
# 23 : : static constexpr uint8_t DB_BEST_BLOCK{'B'};
# 24 : : static constexpr uint8_t DB_HEAD_BLOCKS{'H'};
# 25 : : static constexpr uint8_t DB_FLAG{'F'};
# 26 : : static constexpr uint8_t DB_REINDEX_FLAG{'R'};
# 27 : : static constexpr uint8_t DB_LAST_BLOCK{'l'};
# 28 : :
# 29 : : // Keys used in previous version that might still be found in the DB:
# 30 : : static constexpr uint8_t DB_COINS{'c'};
# 31 : : static constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
# 32 : : // uint8_t DB_TXINDEX{'t'}
# 33 : :
# 34 : : std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db)
# 35 : 19 : {
# 36 : 19 : CBlockLocator ignored{};
# 37 [ - + ]: 19 : if (block_tree_db.Read(DB_TXINDEX_BLOCK, ignored)) {
# 38 : 0 : return _("The -txindex upgrade started by a previous version cannot be completed. Restart with the previous version or run a full -reindex.");
# 39 : 0 : }
# 40 : 19 : bool txindex_legacy_flag{false};
# 41 : 19 : block_tree_db.ReadFlag("txindex", txindex_legacy_flag);
# 42 [ - + ]: 19 : if (txindex_legacy_flag) {
# 43 : : // Disable legacy txindex and warn once about occupied disk space
# 44 [ # # ]: 0 : if (!block_tree_db.WriteFlag("txindex", false)) {
# 45 : 0 : return Untranslated("Failed to write block index db flag 'txindex'='0'");
# 46 : 0 : }
# 47 : 0 : return _("The block index db contains a legacy 'txindex'. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.");
# 48 : 0 : }
# 49 : 19 : return std::nullopt;
# 50 : 19 : }
# 51 : :
# 52 : : bool CCoinsViewDB::NeedsUpgrade()
# 53 : 934 : {
# 54 : 934 : std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()};
# 55 : : // DB_COINS was deprecated in v0.15.0, commit
# 56 : : // 1088b02f0ccd7358d2b7076bb9e122d59d502d02
# 57 : 934 : cursor->Seek(std::make_pair(DB_COINS, uint256{}));
# 58 : 934 : return cursor->Valid();
# 59 : 934 : }
# 60 : :
# 61 : : namespace {
# 62 : :
# 63 : : struct CoinEntry {
# 64 : : COutPoint* outpoint;
# 65 : : uint8_t key;
# 66 : 10360747 : explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)), key(DB_COIN) {}
# 67 : :
# 68 : 10360658 : SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
# 69 : : };
# 70 : :
# 71 : : } // namespace
# 72 : :
# 73 : : CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe) :
# 74 : : m_db(std::make_unique<CDBWrapper>(ldb_path, nCacheSize, fMemory, fWipe, true)),
# 75 : : m_ldb_path(ldb_path),
# 76 : 951 : m_is_memory(fMemory) { }
# 77 : :
# 78 : : void CCoinsViewDB::ResizeCache(size_t new_cache_size)
# 79 : 23 : {
# 80 : : // We can't do this operation with an in-memory DB since we'll lose all the coins upon
# 81 : : // reset.
# 82 [ - + ]: 23 : if (!m_is_memory) {
# 83 : : // Have to do a reset first to get the original `m_db` state to release its
# 84 : : // filesystem lock.
# 85 : 0 : m_db.reset();
# 86 : 0 : m_db = std::make_unique<CDBWrapper>(
# 87 : 0 : m_ldb_path, new_cache_size, m_is_memory, /*fWipe=*/false, /*obfuscate=*/true);
# 88 : 0 : }
# 89 : 23 : }
# 90 : :
# 91 : 10062758 : bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const {
# 92 : 10062758 : return m_db->Read(CoinEntry(&outpoint), coin);
# 93 : 10062758 : }
# 94 : :
# 95 : 0 : bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const {
# 96 : 0 : return m_db->Exists(CoinEntry(&outpoint));
# 97 : 0 : }
# 98 : :
# 99 : 4338 : uint256 CCoinsViewDB::GetBestBlock() const {
# 100 : 4338 : uint256 hashBestChain;
# 101 [ + + ]: 4338 : if (!m_db->Read(DB_BEST_BLOCK, hashBestChain))
# 102 : 1664 : return uint256();
# 103 : 2674 : return hashBestChain;
# 104 : 4338 : }
# 105 : :
# 106 : 1214 : std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
# 107 : 1214 : std::vector<uint256> vhashHeadBlocks;
# 108 [ + - ]: 1214 : if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
# 109 : 1214 : return std::vector<uint256>();
# 110 : 1214 : }
# 111 : 0 : return vhashHeadBlocks;
# 112 : 1214 : }
# 113 : :
# 114 : 1908 : bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
# 115 : 1908 : CDBBatch batch(*m_db);
# 116 : 1908 : size_t count = 0;
# 117 : 1908 : size_t changed = 0;
# 118 : 1908 : size_t batch_size = (size_t)gArgs.GetIntArg("-dbbatchsize", nDefaultDbBatchSize);
# 119 : 1908 : int crash_simulate = gArgs.GetIntArg("-dbcrashratio", 0);
# 120 : 1908 : assert(!hashBlock.IsNull());
# 121 : :
# 122 : 0 : uint256 old_tip = GetBestBlock();
# 123 [ + + ]: 1908 : if (old_tip.IsNull()) {
# 124 : : // We may be in the middle of replaying.
# 125 : 280 : std::vector<uint256> old_heads = GetHeadBlocks();
# 126 [ - + ]: 280 : if (old_heads.size() == 2) {
# 127 : 0 : assert(old_heads[0] == hashBlock);
# 128 : 0 : old_tip = old_heads[1];
# 129 : 0 : }
# 130 : 280 : }
# 131 : :
# 132 : : // In the first batch, mark the database as being in the middle of a
# 133 : : // transition from old_tip to hashBlock.
# 134 : : // A vector is used for future extensibility, as we may want to support
# 135 : : // interrupting after partial writes from multiple independent reorgs.
# 136 : 0 : batch.Erase(DB_BEST_BLOCK);
# 137 : 1908 : batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip));
# 138 : :
# 139 [ + + ]: 405630 : for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
# 140 [ + + ]: 403722 : if (it->second.flags & CCoinsCacheEntry::DIRTY) {
# 141 : 282233 : CoinEntry entry(&it->first);
# 142 [ + + ]: 282233 : if (it->second.coin.IsSpent())
# 143 : 38872 : batch.Erase(entry);
# 144 : 243361 : else
# 145 : 243361 : batch.Write(entry, it->second.coin);
# 146 : 282233 : changed++;
# 147 : 282233 : }
# 148 : 403722 : count++;
# 149 : 403722 : CCoinsMap::iterator itOld = it++;
# 150 : 403722 : mapCoins.erase(itOld);
# 151 [ - + ]: 403722 : if (batch.SizeEstimate() > batch_size) {
# 152 [ # # ]: 0 : LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
# 153 : 0 : m_db->WriteBatch(batch);
# 154 : 0 : batch.Clear();
# 155 [ # # ]: 0 : if (crash_simulate) {
# 156 : 0 : static FastRandomContext rng;
# 157 [ # # ]: 0 : if (rng.randrange(crash_simulate) == 0) {
# 158 : 0 : LogPrintf("Simulating a crash. Goodbye.\n");
# 159 : 0 : _Exit(0);
# 160 : 0 : }
# 161 : 0 : }
# 162 : 0 : }
# 163 : 403722 : }
# 164 : :
# 165 : : // In the last batch, mark the database as consistent with hashBlock again.
# 166 : 1908 : batch.Erase(DB_HEAD_BLOCKS);
# 167 : 1908 : batch.Write(DB_BEST_BLOCK, hashBlock);
# 168 : :
# 169 [ + - ]: 1908 : LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
# 170 : 1908 : bool ret = m_db->WriteBatch(batch);
# 171 [ + - ]: 1908 : LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
# 172 : 1908 : return ret;
# 173 : 1908 : }
# 174 : :
# 175 : : size_t CCoinsViewDB::EstimateSize() const
# 176 : 27 : {
# 177 : 27 : return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
# 178 : 27 : }
# 179 : :
# 180 : 1145 : CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(gArgs.GetDataDirNet() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
# 181 : 1145 : }
# 182 : :
# 183 : 1880 : bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
# 184 : 1880 : return Read(std::make_pair(DB_BLOCK_FILES, nFile), info);
# 185 : 1880 : }
# 186 : :
# 187 : 20 : bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
# 188 [ + + ]: 20 : if (fReindexing)
# 189 : 10 : return Write(DB_REINDEX_FLAG, uint8_t{'1'});
# 190 : 10 : else
# 191 : 10 : return Erase(DB_REINDEX_FLAG);
# 192 : 20 : }
# 193 : :
# 194 : 928 : void CBlockTreeDB::ReadReindexing(bool &fReindexing) {
# 195 : 928 : fReindexing = Exists(DB_REINDEX_FLAG);
# 196 : 928 : }
# 197 : :
# 198 : 929 : bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
# 199 : 929 : return Read(DB_LAST_BLOCK, nFile);
# 200 : 929 : }
# 201 : :
# 202 : : /** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
# 203 : : class CCoinsViewDBCursor: public CCoinsViewCursor
# 204 : : {
# 205 : : public:
# 206 : : // Prefer using CCoinsViewDB::Cursor() since we want to perform some
# 207 : : // cache warmup on instantiation.
# 208 : : CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256&hashBlockIn):
# 209 : 132 : CCoinsViewCursor(hashBlockIn), pcursor(pcursorIn) {}
# 210 : 132 : ~CCoinsViewDBCursor() {}
# 211 : :
# 212 : : bool GetKey(COutPoint &key) const override;
# 213 : : bool GetValue(Coin &coin) const override;
# 214 : : unsigned int GetValueSize() const override;
# 215 : :
# 216 : : bool Valid() const override;
# 217 : : void Next() override;
# 218 : :
# 219 : : private:
# 220 : : std::unique_ptr<CDBIterator> pcursor;
# 221 : : std::pair<char, COutPoint> keyTmp;
# 222 : :
# 223 : : friend class CCoinsViewDB;
# 224 : : };
# 225 : :
# 226 : : std::unique_ptr<CCoinsViewCursor> CCoinsViewDB::Cursor() const
# 227 : 132 : {
# 228 : 132 : auto i = std::make_unique<CCoinsViewDBCursor>(
# 229 : 132 : const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock());
# 230 : : /* It seems that there are no "const iterators" for LevelDB. Since we
# 231 : : only need read operations on it, use a const-cast to get around
# 232 : : that restriction. */
# 233 : 132 : i->pcursor->Seek(DB_COIN);
# 234 : : // Cache key of first record
# 235 [ + + ]: 132 : if (i->pcursor->Valid()) {
# 236 : 131 : CoinEntry entry(&i->keyTmp.second);
# 237 : 131 : i->pcursor->GetKey(entry);
# 238 : 131 : i->keyTmp.first = entry.key;
# 239 : 131 : } else {
# 240 : 1 : i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false
# 241 : 1 : }
# 242 : 132 : return i;
# 243 : 132 : }
# 244 : :
# 245 : : bool CCoinsViewDBCursor::GetKey(COutPoint &key) const
# 246 : 15625 : {
# 247 : : // Return cached key
# 248 [ + - ]: 15625 : if (keyTmp.first == DB_COIN) {
# 249 : 15625 : key = keyTmp.second;
# 250 : 15625 : return true;
# 251 : 15625 : }
# 252 : 0 : return false;
# 253 : 15625 : }
# 254 : :
# 255 : : bool CCoinsViewDBCursor::GetValue(Coin &coin) const
# 256 : 15625 : {
# 257 : 15625 : return pcursor->GetValue(coin);
# 258 : 15625 : }
# 259 : :
# 260 : : unsigned int CCoinsViewDBCursor::GetValueSize() const
# 261 : 0 : {
# 262 : 0 : return pcursor->GetValueSize();
# 263 : 0 : }
# 264 : :
# 265 : : bool CCoinsViewDBCursor::Valid() const
# 266 : 15715 : {
# 267 : 15715 : return keyTmp.first == DB_COIN;
# 268 : 15715 : }
# 269 : :
# 270 : : void CCoinsViewDBCursor::Next()
# 271 : 15625 : {
# 272 : 15625 : pcursor->Next();
# 273 : 15625 : CoinEntry entry(&keyTmp.second);
# 274 [ + + ][ - + ]: 15625 : if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
# 275 : 89 : keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
# 276 : 15536 : } else {
# 277 : 15536 : keyTmp.first = entry.key;
# 278 : 15536 : }
# 279 : 15625 : }
# 280 : :
# 281 : 1623 : bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) {
# 282 : 1623 : CDBBatch batch(*this);
# 283 [ + + ]: 2220 : for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) {
# 284 : 597 : batch.Write(std::make_pair(DB_BLOCK_FILES, it->first), *it->second);
# 285 : 597 : }
# 286 : 1623 : batch.Write(DB_LAST_BLOCK, nLastFile);
# 287 [ + + ]: 64541 : for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) {
# 288 : 62918 : batch.Write(std::make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it));
# 289 : 62918 : }
# 290 : 1623 : return WriteBatch(batch, true);
# 291 : 1623 : }
# 292 : :
# 293 : 1 : bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
# 294 [ + - ]: 1 : return Write(std::make_pair(DB_FLAG, name), fValue ? uint8_t{'1'} : uint8_t{'0'});
# 295 : 1 : }
# 296 : :
# 297 : 947 : bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
# 298 : 947 : uint8_t ch;
# 299 [ + + ]: 947 : if (!Read(std::make_pair(DB_FLAG, name), ch))
# 300 : 943 : return false;
# 301 : 4 : fValue = ch == uint8_t{'1'};
# 302 : 4 : return true;
# 303 : 947 : }
# 304 : :
# 305 : : bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
# 306 : 929 : {
# 307 : 929 : AssertLockHeld(::cs_main);
# 308 : 929 : std::unique_ptr<CDBIterator> pcursor(NewIterator());
# 309 : 929 : pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256()));
# 310 : :
# 311 : : // Load m_block_index
# 312 [ + + ]: 79603 : while (pcursor->Valid()) {
# 313 [ - + ]: 79146 : if (ShutdownRequested()) return false;
# 314 : 79146 : std::pair<uint8_t, uint256> key;
# 315 [ + + ][ + - ]: 79146 : if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
# 316 : 78674 : CDiskBlockIndex diskindex;
# 317 [ + - ]: 78674 : if (pcursor->GetValue(diskindex)) {
# 318 : : // Construct block index object
# 319 : 78674 : CBlockIndex* pindexNew = insertBlockIndex(diskindex.GetBlockHash());
# 320 : 78674 : pindexNew->pprev = insertBlockIndex(diskindex.hashPrev);
# 321 : 78674 : pindexNew->nHeight = diskindex.nHeight;
# 322 : 78674 : pindexNew->nFile = diskindex.nFile;
# 323 : 78674 : pindexNew->nDataPos = diskindex.nDataPos;
# 324 : 78674 : pindexNew->nUndoPos = diskindex.nUndoPos;
# 325 : 78674 : pindexNew->nVersion = diskindex.nVersion;
# 326 : 78674 : pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
# 327 : 78674 : pindexNew->nTime = diskindex.nTime;
# 328 : 78674 : pindexNew->nBits = diskindex.nBits;
# 329 : 78674 : pindexNew->nNonce = diskindex.nNonce;
# 330 : 78674 : pindexNew->nStatus = diskindex.nStatus;
# 331 : 78674 : pindexNew->nTx = diskindex.nTx;
# 332 : :
# 333 [ - + ]: 78674 : if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams)) {
# 334 : 0 : return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString());
# 335 : 0 : }
# 336 : :
# 337 : 78674 : pcursor->Next();
# 338 : 78674 : } else {
# 339 : 0 : return error("%s: failed to read value", __func__);
# 340 : 0 : }
# 341 : 78674 : } else {
# 342 : 472 : break;
# 343 : 472 : }
# 344 : 79146 : }
# 345 : :
# 346 : 929 : return true;
# 347 : 929 : }
|