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 <validationinterface.h>8#include <chain.h>9#include <consensus/validation.h>10#include <kernel/mempool_entry.h>11#include <kernel/mempool_removal_reason.h>12#include <kernel/types.h>13#include <primitives/block.h>14#include <primitives/transaction.h>15#include <util/check.h>16#include <util/log.h>17#include <util/task_runner.h>19#include <future>20#include <memory>21#include <unordered_map>22#include <utility>24using kernel::ChainstateRole;26/**27* ValidationSignalsImpl manages a list of shared_ptr<CValidationInterface> callbacks.28*29* A std::unordered_map is used to track what callbacks are currently30* registered, and a std::list is used to store the callbacks that are31* currently registered as well as any callbacks that are just unregistered32* and about to be deleted when they are done executing.33*/34class ValidationSignalsImpl35{36private:37Mutex m_mutex;38//! List entries consist of a callback pointer and reference count. The39//! count is equal to the number of current executions of that entry, plus 140//! if it's registered. It cannot be 0 because that would imply it is41//! unregistered and also not being executed (so shouldn't exist).42struct ListEntry { std::shared_ptr<CValidationInterface> callbacks; int count = 1; };43std::list<ListEntry> m_list GUARDED_BY(m_mutex);44std::unordered_map<CValidationInterface*, std::list<ListEntry>::iterator> m_map GUARDED_BY(m_mutex);46public:47std::unique_ptr<util::TaskRunnerInterface> m_task_runner;49explicit ValidationSignalsImpl(std::unique_ptr<util::TaskRunnerInterface> task_runner)50: m_task_runner{std::move(Assert(task_runner))} {}52void Register(std::shared_ptr<CValidationInterface> callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)53{54LOCK(m_mutex);55auto inserted = m_map.emplace(callbacks.get(), m_list.end());56if (inserted.second) inserted.first->second = m_list.emplace(m_list.end());57inserted.first->second->callbacks = std::move(callbacks);58}60void Unregister(CValidationInterface* callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)61{62LOCK(m_mutex);63auto it = m_map.find(callbacks);64if (it != m_map.end()) {65if (!--it->second->count) m_list.erase(it->second);66m_map.erase(it);67}68}70//! Clear unregisters every previously registered callback, erasing every71//! map entry. After this call, the list may still contain callbacks that72//! are currently executing, but it will be cleared when they are done73//! executing.74void Clear() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)75{76LOCK(m_mutex);77for (const auto& entry : m_map) {78if (!--entry.second->count) m_list.erase(entry.second);79}80m_map.clear();81}83template<typename F> void Iterate(F&& f) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)84{85WAIT_LOCK(m_mutex, lock);86for (auto it = m_list.begin(); it != m_list.end();) {87++it->count;88{89REVERSE_LOCK(lock, m_mutex);90f(*it->callbacks);91}92it = --it->count ? std::next(it) : m_list.erase(it);93}94}95};97ValidationSignals::ValidationSignals(std::unique_ptr<util::TaskRunnerInterface> task_runner)98: m_internals{std::make_unique<ValidationSignalsImpl>(std::move(task_runner))} {}100ValidationSignals::~ValidationSignals() = default;102void ValidationSignals::FlushBackgroundCallbacks()103{104m_internals->m_task_runner->flush();105}107size_t ValidationSignals::CallbacksPending()108{109return m_internals->m_task_runner->size();110}112void ValidationSignals::RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)113{114// Each connection captures the shared_ptr to ensure that each callback is115// executed before the subscriber is destroyed. For more details see #18338.116m_internals->Register(std::move(callbacks));117}119void ValidationSignals::RegisterValidationInterface(CValidationInterface* callbacks)120{121// Create a shared_ptr with a no-op deleter - CValidationInterface lifecycle122// is managed by the caller.123RegisterSharedValidationInterface({callbacks, [](CValidationInterface*){}});124}126void ValidationSignals::UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)127{128UnregisterValidationInterface(callbacks.get());129}131void ValidationSignals::UnregisterValidationInterface(CValidationInterface* callbacks)132{133m_internals->Unregister(callbacks);134}136void ValidationSignals::UnregisterAllValidationInterfaces()137{138m_internals->Clear();139}141void ValidationSignals::CallFunctionInValidationInterfaceQueue(std::function<void()> func)142{143m_internals->m_task_runner->insert(std::move(func));144}146void ValidationSignals::SyncWithValidationInterfaceQueue()147{148AssertLockNotHeld(cs_main);149// Block until the validation queue drains150std::promise<void> promise;151CallFunctionInValidationInterfaceQueue([&promise] {152promise.set_value();153});154promise.get_future().wait();155}157// Use a macro instead of a function for conditional logging to prevent158// evaluating arguments when logging is not enabled.159#define ENQUEUE_AND_LOG_EVENT(event, log_msg) \160do { \161static_assert(std::is_rvalue_reference_v<decltype((event))>, \162"event must be passed as an rvalue"); \163static_assert(std::is_rvalue_reference_v<decltype((log_msg))>, \164"log_msg must be passed as an rvalue"); \165auto enqueue_log_msg = (log_msg); \166LOG_EVENT("Enqueuing %s", enqueue_log_msg); \167m_internals->m_task_runner->insert([local_log_msg = std::move(enqueue_log_msg), local_event = (event)] { \168LOG_EVENT("%s", local_log_msg); \169local_event(); \170}); \171} while (0)173#define LOG_MSG(fmt, ...) \174(util::log::ShouldDebugLog(BCLog::VALIDATION) ? tfm::format((fmt), __VA_ARGS__) : std::string{})176#define LOG_EVENT(fmt, ...) \177LogDebug(BCLog::VALIDATION, fmt, __VA_ARGS__)179void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {180// Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which181// the chain actually updates. One way to ensure this is for the caller to invoke this signal182// in the same critical section where the chain is updated184auto log_msg = LOG_MSG("%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__,185pindexNew->GetBlockHash().ToString(),186pindexFork ? pindexFork->GetBlockHash().ToString() : "null",187fInitialDownload);188auto event = [pindexNew, pindexFork, fInitialDownload, this] {189m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); });190};191ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));192}194void ValidationSignals::ActiveTipChange(const CBlockIndex& new_tip, bool is_ibd)195{196LOG_EVENT("%s: new block hash=%s block height=%d", __func__, new_tip.GetBlockHash().ToString(), new_tip.nHeight);197m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ActiveTipChange(new_tip, is_ibd); });198}200void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence)201{202auto log_msg = LOG_MSG("%s: txid=%s wtxid=%s", __func__,203tx.info.m_tx->GetHash().ToString(),204tx.info.m_tx->GetWitnessHash().ToString());205auto event = [tx, mempool_sequence, this] {206m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); });207};208ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));209}211void ValidationSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {212auto log_msg = LOG_MSG("%s: txid=%s wtxid=%s reason=%s", __func__,213tx->GetHash().ToString(),214tx->GetWitnessHash().ToString(),215RemovalReasonToString(reason));216auto event = [tx, reason, mempool_sequence, this] {217m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); });218};219ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));220}222void ValidationSignals::BlockConnected(const ChainstateRole& role, std::shared_ptr<const CBlock> pblock, const CBlockIndex* pindex)223{224auto log_msg = LOG_MSG("%s: block hash=%s block height=%d", __func__,225pblock->GetHash().ToString(),226pindex->nHeight);227auto event = [role, pblock = std::move(pblock), pindex, this] {228m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(role, pblock, pindex); });229};230ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));231}233void ValidationSignals::MempoolTransactionsRemovedForBlock(std::shared_ptr<const CBlock> block, std::vector<RemovedMempoolTransactionInfo> txs_removed_for_block, unsigned int block_height)234{235Assume(block);236auto log_msg = LOG_MSG("%s: block hash=%s block height=%s txs removed=%s block txs=%s", __func__,237block->GetHash().ToString(),238block_height,239txs_removed_for_block.size(),240block->vtx.size());241auto event = [block = std::move(block), txs_removed_for_block = std::move(txs_removed_for_block), block_height, this] {242m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.MempoolTransactionsRemovedForBlock(block, txs_removed_for_block, block_height); });243};244ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));245}247void ValidationSignals::BlockDisconnected(std::shared_ptr<const CBlock> pblock, const CBlockIndex* pindex)248{249auto log_msg = LOG_MSG("%s: block hash=%s block height=%d", __func__,250pblock->GetHash().ToString(),
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.