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) 2017-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <index/base.h>7#include <chain.h>8#include <common/args.h>9#include <dbwrapper.h>10#include <interfaces/chain.h>11#include <interfaces/types.h>12#include <kernel/types.h>13#include <node/abort.h>14#include <node/blockstorage.h>15#include <node/context.h>16#include <node/database_args.h>17#include <node/interface_ui.h>18#include <primitives/block.h>19#include <sync.h>20#include <tinyformat.h>21#include <uint256.h>22#include <undo.h>23#include <util/check.h>24#include <util/fs.h>25#include <util/log.h>26#include <util/string.h>27#include <util/thread.h>28#include <util/threadinterrupt.h>29#include <util/time.h>30#include <util/translation.h>31#include <validation.h>32#include <validationinterface.h>34#include <compare>35#include <cstdint>36#include <functional>37#include <memory>38#include <optional>39#include <stdexcept>40#include <string>41#include <thread>42#include <utility>43#include <vector>45using kernel::ChainstateRole;47constexpr uint8_t DB_BEST_BLOCK{'B'};49constexpr auto SYNC_LOG_INTERVAL{30s};50constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};52template <typename... Args>53void BaseIndex::FatalErrorf(util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args)54{55auto message = tfm::format(fmt, args...);56node::AbortNode(m_chain->context()->shutdown_request, m_chain->context()->exit_status, Untranslated(message), m_chain->context()->warnings.get());57}59CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)60{61CBlockLocator locator;62bool found = chain.findBlock(block_hash, interfaces::FoundBlock().locator(locator));63assert(found);64assert(!locator.IsNull());65return locator;66}68BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate, bool f_bloom) :69CDBWrapper{DBParams{70.path = path,71.cache_bytes = n_cache_size,72.memory_only = f_memory,73.wipe_data = f_wipe,74.obfuscate = f_obfuscate,75.bloom_filter = f_bloom,76.options = [] { DBOptions options; node::ReadDatabaseArgs(gArgs, options); return options; }()}}77{}79CBlockLocator BaseIndex::DB::ReadBestBlock() const80{81CBlockLocator locator;83bool success = Read(DB_BEST_BLOCK, locator);84if (!success) {85locator.SetNull();86}88return locator;89}91void BaseIndex::DB::WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator)92{93batch.Write(DB_BEST_BLOCK, locator);94}96BaseIndex::BaseIndex(std::unique_ptr<interfaces::Chain> chain, std::string name, std::string thread_name)97: m_chain{std::move(chain)}, m_name{std::move(name)}, m_thread_name{std::move(thread_name)} {}99BaseIndex::~BaseIndex()100{101Interrupt();102Stop();103}105bool BaseIndex::Init()106{107AssertLockNotHeld(cs_main);109// May need reset if index is being restarted.110m_interrupt.reset();112// m_chainstate member gives indexing code access to node internals. It is113// removed in followup https://github.com/bitcoin/bitcoin/pull/24230114m_chainstate = WITH_LOCK(::cs_main,115return &m_chain->context()->chainman->ValidatedChainstate());116// Register to validation interface before setting the 'm_synced' flag, so that117// callbacks are not missed once m_synced is true.118m_chain->context()->validation_signals->RegisterValidationInterface(this);120const auto locator{GetDB().ReadBestBlock()};122LOCK(cs_main);123CChain& index_chain = m_chainstate->m_chain;125if (locator.IsNull()) {126SetBestBlockIndex(nullptr);127} else {128// Setting the best block to the locator's top block. If it is not part of the129// best chain, we will rewind to the fork point during index sync130const CBlockIndex* locator_index{m_chainstate->m_blockman.LookupBlockIndex(locator.vHave.at(0))};131if (!locator_index) {132return InitError(Untranslated(strprintf("best block of %s not found. Please rebuild the index.", GetName())));133}134SetBestBlockIndex(locator_index);135}137// Child init138const CBlockIndex* start_block = m_best_block_index.load();139if (!CustomInit(start_block ? std::make_optional(interfaces::BlockRef{start_block->GetBlockHash(), start_block->nHeight}) : std::nullopt)) {140return false;141}143// Note: this will latch to true immediately if the user starts up with an empty144// datadir and an index enabled. If this is the case, indexation will happen solely145// via `BlockConnected` signals until, possibly, the next restart.146m_synced = start_block == index_chain.Tip();147m_init = true;148return true;149}151static const CBlockIndex* NextSyncBlock(const CBlockIndex* const pindex_prev, CChain& chain) EXCLUSIVE_LOCKS_REQUIRED(cs_main)152{153AssertLockHeld(cs_main);155if (!pindex_prev) {156return chain.Genesis();157}159if (const auto* pindex{chain.Next(*pindex_prev)}) {160return pindex;161}163// If there is no next block, we might be synced164if (pindex_prev == chain.Tip()) {165return nullptr;166}168// Since block is not in the chain, return the next block in the chain AFTER the last common ancestor.169// Caller will be responsible for rewinding back to the common ancestor.170const auto* fork{chain.FindFork(*pindex_prev)};171// Common ancestor must exist (genesis).172return chain.Next(*Assert(fork));173}175bool BaseIndex::ProcessBlock(const CBlockIndex* pindex, const CBlock* block_data)176{177interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex, block_data);179CBlock block;180if (!block_data) { // disk lookup if block data wasn't provided181if (!m_chainstate->m_blockman.ReadBlock(block, *pindex)) {182FatalErrorf("Failed to read block %s from disk",183pindex->GetBlockHash().ToString());184return false;185}186block_info.data = █187}189CBlockUndo block_undo;190if (CustomOptions().connect_undo_data) {191if (pindex->nHeight > 0 && !m_chainstate->m_blockman.ReadBlockUndo(block_undo, *pindex)) {192FatalErrorf("Failed to read undo block data %s from disk",193pindex->GetBlockHash().ToString());194return false;195}196block_info.undo_data = &block_undo;197}199if (!CustomAppend(block_info)) {200FatalErrorf("Failed to write block %s to index database",201pindex->GetBlockHash().ToString());202return false;203}205return true;206}208void BaseIndex::Sync()209{210const CBlockIndex* pindex = m_best_block_index.load();211if (!m_synced) {212auto last_log_time{NodeClock::now()};213auto last_locator_write_time{last_log_time};214while (true) {215if (m_interrupt) {216LogInfo("%s: m_interrupt set; exiting ThreadSync", GetName());218SetBestBlockIndex(pindex);219// No need to handle errors in Commit. If it fails, the error will be already be220// logged. The best way to recover is to continue, as index cannot be corrupted by221// a missed commit to disk for an advanced index state.222Commit();223return;224}226const CBlockIndex* pindex_next = WITH_LOCK(cs_main, return NextSyncBlock(pindex, m_chainstate->m_chain));227// If pindex_next is null, it means pindex is the chain tip, so228// commit data indexed so far.229if (!pindex_next) {230SetBestBlockIndex(pindex);231// No need to handle errors in Commit. See rationale above.232Commit();234// If pindex is still the chain tip after committing, exit the235// sync loop. It is important for cs_main to be locked while236// setting m_synced = true, otherwise a new block could be237// attached while m_synced is still false, and it would not be238// indexed.239LOCK(::cs_main);240pindex_next = NextSyncBlock(pindex, m_chainstate->m_chain);241if (!pindex_next) {242m_synced = true;243break;244}245}246if (pindex_next->pprev != pindex && !Rewind(pindex, pindex_next->pprev)) {247FatalErrorf("Failed to rewind %s to a previous chain tip", GetName());248return;249}250pindex = pindex_next;