Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2009-2010 Satoshi Nakamoto2// Copyright (c) 2009-present The Bitcoin Core developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <txdb.h>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/threadnames.h>18#include <util/vector.h>20#include <cassert>21#include <chrono>22#include <cstdlib>23#include <exception>24#include <future>25#include <iterator>26#include <utility>28static constexpr uint8_t DB_COIN{'C'};29static constexpr uint8_t DB_BEST_BLOCK{'B'};30static constexpr uint8_t DB_HEAD_BLOCKS{'H'};31// Keys used in previous version that might still be found in the DB:32static constexpr uint8_t DB_COINS{'c'};34// Threshold for warning when writing this many dirty cache entries to disk.35static constexpr size_t WARN_FLUSH_COINS_COUNT{10'000'000};37bool CCoinsViewDB::NeedsUpgrade()38{39std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()};40// DB_COINS was deprecated in v0.15.0, commit41// 1088b02f0ccd7358d2b7076bb9e122d59d502d0242cursor->Seek(std::make_pair(DB_COINS, uint256{}));43return cursor->Valid();44}46namespace {48struct CoinEntry {49COutPoint* outpoint;50uint8_t key{DB_COIN};51explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)) {}53SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }54};56} // namespace58CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) :59m_db_params{std::move(db_params)},60m_options{std::move(options)},61m_db{std::make_unique<CDBWrapper>(m_db_params)} { }63CCoinsViewDB::~CCoinsViewDB()64{65if (m_compaction.valid()) {66if (m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) {67LogInfo("Waiting for background chainstate compaction of %s", fs::PathToString(m_db_params.path));68}69m_compaction.wait();70}71}73void CCoinsViewDB::ResizeCache(size_t new_cache_size)74{75// We can't do this operation with an in-memory DB since we'll lose all the coins upon76// reset.77if (!m_db_params.memory_only) {78LOCK(m_db_mutex);79// Have to do a reset first to get the original `m_db` state to release its80// filesystem lock.81m_db.reset();82m_db_params.cache_bytes = new_cache_size;83m_db_params.wipe_data = false;84m_db = std::make_unique<CDBWrapper>(m_db_params);85}86}88std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint) const89{90Coin coin;91const CDBWrapper::ReadStatus res = m_db->TryRead(CoinEntry(&outpoint), coin);92if (!res) {93// Propagate errors so CCoinsViewErrorCatcher triggers a clean shutdown.94switch (const auto& [err_code, err_msg] = res.error(); err_code) {95case CDBWrapper::ReadFailure::Code::DeserializationError:96throw dbwrapper_error{strprintf("Coin deserialization failure: %s", err_msg)};97case CDBWrapper::ReadFailure::Code::DatabaseError:98throw dbwrapper_error{strprintf("Coin DB read failure: %s", err_msg)};99} // no default case, so the compiler can warn about missing cases100std::abort(); // unreachable101}103// Check whether the coin exists104if (!res.value()) return std::nullopt;105// Coin found, ensure UTXO database never contains spent coins106Assert(!coin.IsSpent());107return coin;108}110std::optional<Coin> CCoinsViewDB::PeekCoin(const COutPoint& outpoint) const111{112return GetCoin(outpoint);113}115bool CCoinsViewDB::HaveCoin(const COutPoint& outpoint) const116{117return m_db->Exists(CoinEntry(&outpoint));118}120uint256 CCoinsViewDB::GetBestBlock() const {121uint256 hashBestChain;122if (!m_db->Read(DB_BEST_BLOCK, hashBestChain))123return uint256();124return hashBestChain;125}127std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {128std::vector<uint256> vhashHeadBlocks;129if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {130return std::vector<uint256>();131}132return vhashHeadBlocks;133}135void CCoinsViewDB::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& block_hash)136{137CDBBatch batch(*m_db);138size_t count = 0;139const size_t dirty_count{cursor.GetDirtyCount()};140assert(!block_hash.IsNull());142uint256 old_tip = GetBestBlock();143if (old_tip.IsNull()) {144// We may be in the middle of replaying.145std::vector<uint256> old_heads = GetHeadBlocks();146if (old_heads.size() == 2) {147if (old_heads[0] != block_hash) {148LogError("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");149}150assert(old_heads[0] == block_hash);151old_tip = old_heads[1];152}153}155if (dirty_count > WARN_FLUSH_COINS_COUNT) LogWarning("Flushing large (%d entries) UTXO set to disk, it may take several minutes", dirty_count);156LOG_TIME_MILLIS_WITH_CATEGORY(strprintf("write coins cache to disk (%d out of %d cached coins)",157dirty_count, cursor.GetTotalCount()), BCLog::BENCH);159// In the first batch, mark the database as being in the middle of a160// transition from old_tip to block_hash.161// A vector is used for future extensibility, as we may want to support162// interrupting after partial writes from multiple independent reorgs.163batch.Erase(DB_BEST_BLOCK);164batch.Write(DB_HEAD_BLOCKS, Vector(block_hash, old_tip));166for (auto it{cursor.Begin()}; it != cursor.End();) {167if (it->second.IsDirty()) {168CoinEntry entry(&it->first);169if (it->second.coin.IsSpent()) {170batch.Erase(entry);171} else {172batch.Write(entry, it->second.coin);173}174}175count++;176it = cursor.NextAndMaybeErase(*it);177if (batch.ApproximateSize() > m_options.batch_write_bytes) {178LogDebug(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.ApproximateSize() / double(1_MiB));180m_db->WriteBatch(batch);181batch.Clear();182if (m_options.simulate_crash_ratio) {183static FastRandomContext rng;184if (rng.randrange(m_options.simulate_crash_ratio) == 0) {185LogError("Simulating a crash. Goodbye.");186_Exit(0);187}188}189}190}192// In the last batch, mark the database as consistent with block_hash again.193batch.Erase(DB_HEAD_BLOCKS);194batch.Write(DB_BEST_BLOCK, block_hash);196LogDebug(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.ApproximateSize() / double(1_MiB));197m_db->WriteBatch(batch);198LogDebug(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...", (unsigned int)dirty_count, (unsigned int)count);199}201size_t CCoinsViewDB::EstimateSize() const202{203return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));204}206std::optional<std::string> CCoinsViewDB::GetDBProperty(const std::string& property)207{208return m_db->GetProperty(property);209}211std::shared_future<void> CCoinsViewDB::CompactFullAsync()212{213AssertLockHeld(::cs_main);214if (m_compaction.valid() && m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) return m_compaction;215m_compaction = std::async(std::launch::async, [this] {216try {217util::ThreadRename("utxocompact");218LOCK(m_db_mutex);220LogDebug(BCLog::COINDB, "Starting chainstate compaction of %s", fs::PathToString(m_db_params.path));221m_db->CompactFull();222LogDebug(BCLog::COINDB, "Finished chainstate compaction of %s", fs::PathToString(m_db_params.path));223} catch (const std::exception& e) {224LogWarning("Failed chainstate compaction (%s)", e.what());225}226}).share();227return m_compaction;228}230/** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */231class CCoinsViewDBCursor: public CCoinsViewCursor232{233public:234// Prefer using CCoinsViewDB::Cursor() since we want to perform some235// cache warmup on instantiation.236CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256& in_block_hash):237CCoinsViewCursor(in_block_hash), pcursor(pcursorIn) {}238~CCoinsViewDBCursor() = default;240bool GetKey(COutPoint &key) const override;241bool GetValue(Coin &coin) const override;243bool Valid() const override;244void Next() override;246private:247std::unique_ptr<CDBIterator> pcursor;248std::pair<char, COutPoint> keyTmp;250friend class CCoinsViewDB;