Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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.
253if (!ProcessBlock(pindex)) return; // error logged internally255auto current_time{NodeClock::now()};256if (current_time - last_log_time >= SYNC_LOG_INTERVAL) {257LogInfo("Syncing %s with block chain from height %d", GetName(), pindex->nHeight);258last_log_time = current_time;259}261if (current_time - last_locator_write_time >= SYNC_LOCATOR_WRITE_INTERVAL) {262SetBestBlockIndex(pindex);263last_locator_write_time = current_time;264// No need to handle errors in Commit. See rationale above.265Commit();266}267}268}270if (pindex) {271LogInfo("%s is enabled at height %d", GetName(), pindex->nHeight);272} else {273LogInfo("%s is enabled", GetName());274}275}277void BaseIndex::Commit()278{279// Don't commit anything if we haven't indexed any block yet280// (this could happen if init is interrupted).281bool ok = m_best_block_index != nullptr;282if (ok) {283// Don't commit if the index best block is not an ancestor of the chainstate's last flushed284// block. Otherwise, after an unclean shutdown, the index could be285// persisted ahead of a chainstate it can no longer roll back to, which286// would corrupt indexes with state (e.g. coinstatsindex).287const CBlockIndex* index_tip = m_best_block_index.load();288const CBlockIndex* last_flushed = WITH_LOCK(::cs_main, return m_chainstate->GetLastFlushedBlock());289if (!last_flushed || last_flushed->GetAncestor(index_tip->nHeight) != index_tip) {290LogDebug(BCLog::COINDB, "Skipping commit, index is ahead of flushed chainstate (index height %d, last flush at height %d)",291index_tip->nHeight, last_flushed ? last_flushed->nHeight : -1);292return;293}294CDBBatch batch(GetDB());295ok = CustomCommit(batch);296if (ok) {297GetDB().WriteBestBlock(batch, GetLocator(*m_chain, m_best_block_index.load()->GetBlockHash()));298GetDB().WriteBatch(batch);299}300}301if (!ok) {302LogError("Failed to commit latest %s state", GetName());303}304}306bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)307{308assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);310CBlock block;311CBlockUndo block_undo;313for (const CBlockIndex* iter_tip = current_tip; iter_tip != new_tip; iter_tip = iter_tip->pprev) {314interfaces::BlockInfo block_info = kernel::MakeBlockInfo(iter_tip);315if (CustomOptions().disconnect_data) {316if (!m_chainstate->m_blockman.ReadBlock(block, *iter_tip)) {317LogError("Failed to read block %s from disk",318iter_tip->GetBlockHash().ToString());319return false;320}321block_info.data = █322}323if (CustomOptions().disconnect_undo_data && iter_tip->nHeight > 0) {324if (!m_chainstate->m_blockman.ReadBlockUndo(block_undo, *iter_tip)) {325return false;326}327block_info.undo_data = &block_undo;328}329if (!CustomRemove(block_info)) {330return false;331}332}334// Don't commit here - the committed index state must never be ahead of the335// flushed chainstate, otherwise unclean restarts would lead to index corruption.336// Pruning has a minimum of 288 blocks-to-keep and getting the index337// out of sync may be possible but a users fault.338// In case we reorg beyond the pruned depth, ReadBlock would339// throw and lead to a graceful shutdown340SetBestBlockIndex(new_tip);341return true;342}344void BaseIndex::BlockConnected(const ChainstateRole& role, const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex)345{346// Ignore events from not fully validated chains to avoid out-of-order indexing.347//348// TODO at some point we could parameterize whether a particular index can be349// built out of order, but for now just do the conservative simple thing.350if (!role.validated) {351return;352}354// Ignore BlockConnected signals until we have fully indexed the chain.355if (!m_synced) {356return;357}359const CBlockIndex* best_block_index = m_best_block_index.load();360if (!best_block_index) {361if (pindex->nHeight != 0) {362FatalErrorf("First block connected is not the genesis block (height=%d)",363pindex->nHeight);364return;365}366} else {367// Ensure block connects to an ancestor of the current best block. This should be the case368// most of the time, but may not be immediately after the sync thread catches up and sets369// m_synced. Consider the case where there is a reorg and the blocks on the stale branch are370// in the ValidationInterface queue backlog even after the sync thread has caught up to the371// new chain tip. In this unlikely event, log a warning and let the queue clear.372if (best_block_index->GetAncestor(pindex->nHeight - 1) != pindex->pprev) {373LogWarning("Block %s does not connect to an ancestor of "374"known best chain (tip=%s); not updating index",375pindex->GetBlockHash().ToString(),376best_block_index->GetBlockHash().ToString());377return;378}379if (best_block_index != pindex->pprev && !Rewind(best_block_index, pindex->pprev)) {380FatalErrorf("Failed to rewind %s to a previous chain tip",381GetName());382return;383}384}386// Dispatch block to child class; errors are logged internally and abort the node.387if (ProcessBlock(pindex, block.get())) {388// Setting the best block index is intentionally the last step of this389// function, so BlockUntilSyncedToCurrentChain callers waiting for the390// best block index to be updated can rely on the block being fully391// processed, and the index object being safe to delete.392SetBestBlockIndex(pindex);393}394}396void BaseIndex::ChainStateFlushed(const ChainstateRole& role, const CBlockLocator& locator)397{398// Ignore events from not fully validated chains to avoid out-of-order indexing.399if (!role.validated) {400return;401}403if (!m_synced) {404return;405}407const uint256& locator_tip_hash = locator.vHave.front();408const CBlockIndex* locator_tip_index;409{410LOCK(cs_main);411locator_tip_index = m_chainstate->m_blockman.LookupBlockIndex(locator_tip_hash);412}414if (!locator_tip_index) {415FatalErrorf("First block (hash=%s) in locator was not found",416locator_tip_hash.ToString());417return;418}420// This checks that ChainStateFlushed callbacks are received after BlockConnected. The check may fail421// immediately after the sync thread catches up and sets m_synced. Consider the case where422// there is a reorg and the blocks on the stale branch are in the ValidationInterface queue423// backlog even after the sync thread has caught up to the new chain tip. In this unlikely424// event, log a warning and let the queue clear.425const CBlockIndex* best_block_index = m_best_block_index.load();426if (best_block_index->GetAncestor(locator_tip_index->nHeight) != locator_tip_index) {427LogWarning("Locator contains block (hash=%s) not on known best "428"chain (tip=%s); not writing index locator",429locator_tip_hash.ToString(),430best_block_index->GetBlockHash().ToString());431return;432}434// No need to handle errors in Commit. If it fails, the error will be already be logged. The435// best way to recover is to continue, as index cannot be corrupted by a missed commit to disk436// for an advanced index state.437Commit();438}440bool BaseIndex::BlockUntilSyncedToCurrentChain() const441{442AssertLockNotHeld(cs_main);444if (!m_synced) {445return false;446}448{449// Skip the queue-draining stuff if we know we're caught up with450// m_chain.Tip().451LOCK(cs_main);452const CBlockIndex* chain_tip = m_chainstate->m_chain.Tip();453const CBlockIndex* best_block_index = m_best_block_index.load();454if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {455return true;456}457}459LogInfo("%s is catching up on block notifications", GetName());460m_chain->context()->validation_signals->SyncWithValidationInterfaceQueue();461return true;462}464void BaseIndex::Interrupt()465{466m_interrupt();467}469bool BaseIndex::StartBackgroundSync()470{471if (!m_init) throw std::logic_error("Error: Cannot start a non-initialized index");473m_thread_sync = std::thread(&util::TraceThread, m_thread_name, [this] { Sync(); });474return true;475}477void BaseIndex::Stop()478{479if (m_chain->context()->validation_signals) {480m_chain->context()->validation_signals->UnregisterValidationInterface(this);481}483if (m_thread_sync.joinable()) {484m_thread_sync.join();485}486}488IndexSummary BaseIndex::GetSummary() const489{490IndexSummary summary{};491summary.name = GetName();492summary.synced = m_synced;493if (const auto& pindex = m_best_block_index.load()) {494summary.best_block_height = pindex->nHeight;495summary.best_block_hash = pindex->GetBlockHash();496} else {497summary.best_block_height = 0;498summary.best_block_hash = m_chain->getBlockHash(0);499}500return summary;