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.
1// Copyright (c) 2024-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 <arith_uint256.h>6#include <blockencodings.h>7#include <net.h>8#include <net_processing.h>9#include <netmessagemaker.h>10#include <node/peerman_args.h>11#include <test/fuzz/FuzzedDataProvider.h>12#include <test/fuzz/fuzz.h>13#include <test/fuzz/util.h>14#include <test/util/net.h>15#include <test/util/script.h>16#include <test/util/setup_common.h>17#include <test/util/time.h>18#include <uint256.h>19#include <validation.h>21namespace {22constexpr uint32_t FUZZ_MAX_HEADERS_RESULTS{16};24class HeadersSyncSetup : public TestingSetup25{26std::vector<CNode*> m_connections;28public:29HeadersSyncSetup(const ChainType chain_type, TestOpts opts) : TestingSetup(chain_type, opts)30{31PeerManager::Options peerman_opts;32node::ApplyArgsManOptions(*m_node.args, peerman_opts);33peerman_opts.max_headers_result = FUZZ_MAX_HEADERS_RESULTS;34// The peerman's rng is a global that is reused, so it will be reused35// and may cause non-determinism between runs. This may even influence36// the global RNG, because seeding may be done from the global one. For37// now, avoid it influencing the global RNG, and initialize it with a38// constant instead.39peerman_opts.deterministic_rng = true;40// No txs are relayed. Disable irrelevant and possibly41// non-deterministic code paths.42peerman_opts.ignore_incoming_txs = true;43m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman,44m_node.banman.get(), *m_node.chainman,45*m_node.mempool, *m_node.warnings, peerman_opts);47CConnman::Options options;48options.m_msgproc = m_node.peerman.get();49m_node.connman->Init(options);50}52void ResetAndInitialize() EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);53void SendMessage(FuzzedDataProvider& fuzzed_data_provider, CSerializedNetMsg&& msg)54EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);55};57void HeadersSyncSetup::ResetAndInitialize()58{59m_connections.clear();60auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);61connman.StopNodes();63static NodeId id{0};64std::vector<ConnectionType> conn_types = {65ConnectionType::OUTBOUND_FULL_RELAY,66ConnectionType::BLOCK_RELAY,67ConnectionType::INBOUND68};70for (auto conn_type : conn_types) {71CAddress addr{};72m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false, 0));73CNode& p2p_node = *m_connections.back();75connman.Handshake(76/*node=*/p2p_node,77/*successfully_connected=*/true,78/*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS),79/*local_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS),80/*version=*/PROTOCOL_VERSION,81/*relay_txs=*/true);83connman.AddTestNode(p2p_node);84}85}87void HeadersSyncSetup::SendMessage(FuzzedDataProvider& fuzzed_data_provider, CSerializedNetMsg&& msg)88{89auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);90CNode& connection = *PickValue(fuzzed_data_provider, m_connections);92connman.FlushSendBuffer(connection);93(void)connman.ReceiveMsgFrom(connection, std::move(msg));94connection.fPauseSend = false;95try {96connman.ProcessMessagesOnce(connection);97} catch (const std::ios_base::failure&) {98}99m_node.peerman->SendMessages(connection);100}102CBlockHeader ConsumeHeader(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits)103{104CBlockHeader header;105header.nNonce = 0;106// Either use the previous difficulty or let the fuzzer choose. The upper target in the107// range comes from the bits value of the genesis block, which is 0x1d00ffff. The lower108// target comes from the bits value of mainnet block 840000, which is 0x17034219.109// Calling lower_target.SetCompact(0x17034219) and upper_target.SetCompact(0x1d00ffff)110// should return the values below.111//112// RPC commands to verify:113// getblockheader 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f114// getblockheader 0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5115if (fuzzed_data_provider.ConsumeBool()) {116header.nBits = prev_nbits;117} else {118arith_uint256 lower_target = UintToArith256(uint256{"0000000000000000000342190000000000000000000000000000000000000000"});119arith_uint256 upper_target = UintToArith256(uint256{"00000000ffff0000000000000000000000000000000000000000000000000000"});120arith_uint256 target = ConsumeArithUInt256InRange(fuzzed_data_provider, lower_target, upper_target);121header.nBits = target.GetCompact();122}123header.nTime = TicksSinceEpoch<std::chrono::seconds>(ConsumeTime(fuzzed_data_provider));124header.hashPrevBlock = prev_hash;125header.nVersion = fuzzed_data_provider.ConsumeIntegral<int32_t>();126return header;127}129CBlock ConsumeBlock(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits)130{131auto header = ConsumeHeader(fuzzed_data_provider, prev_hash, prev_nbits);132// In order to reach the headers acceptance logic, the block is133// constructed in a way that will pass the mutation checks.134CBlock block{header};135CMutableTransaction tx;136tx.vin.resize(1);137tx.vout.resize(1);138tx.vout[0].nValue = 0;139tx.vin[0].scriptSig.resize(2);140block.vtx.push_back(MakeTransactionRef(tx));141block.hashMerkleRoot = block.vtx[0]->GetHash().ToUint256();142return block;143}145// Global setup works for this test as state modification (specifically in the146// block index) would indicate a bug.147HeadersSyncSetup* g_testing_setup;149void initialize()150{151static auto setup{152MakeNoLogFileContext<HeadersSyncSetup>(ChainType::MAIN,153{154.setup_validation_interface = false,155}),156};157g_testing_setup = setup.get();158}159} // namespace161FUZZ_TARGET(p2p_headers_presync, .init = initialize)162{163SeedRandomStateForTest(SeedRand::ZEROS);164FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};165// The steady clock is currently only used for logging, so a constant166// time-point seems acceptable for now.167FakeSteadyClock steady_ctx{};169ChainstateManager& chainman = *g_testing_setup->m_node.chainman;170CBlockHeader base{chainman.GetParams().GenesisBlock()};171const FakeNodeClock clock{base.Time()};173LOCK(NetEventsInterface::g_msgproc_mutex);175g_testing_setup->ResetAndInitialize();177// The chain is just a single block, so this is equal to 1178size_t original_index_size{WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size())};179arith_uint256 total_work{WITH_LOCK(cs_main, return chainman.m_best_header->nChainWork)};181std::vector<CBlockHeader> all_headers;183LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 100) {184auto finalized_block = [&]() {185CBlock block = ConsumeBlock(fuzzed_data_provider, base.GetHash(), base.nBits);186FinalizeHeader(block, chainman);187return block;188};190// Send low-work headers, compact blocks, and blocks191CallOneOf(192fuzzed_data_provider,193[&]() NO_THREAD_SAFETY_ANALYSIS {194// Send FUZZ_MAX_HEADERS_RESULTS headers195std::vector<CBlock> headers;196headers.resize(FUZZ_MAX_HEADERS_RESULTS);197for (CBlock& header : headers) {198header = ConsumeHeader(fuzzed_data_provider, base.GetHash(), base.nBits);199FinalizeHeader(header, chainman);200base = header;201}203all_headers.insert(all_headers.end(), headers.begin(), headers.end());205auto headers_msg = NetMsg::Make(NetMsgType::HEADERS, TX_WITH_WITNESS(headers));206g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));207},208[&]() NO_THREAD_SAFETY_ANALYSIS {209// Send a compact block210auto block = finalized_block();211CBlockHeaderAndShortTxIDs cmpct_block{block, fuzzed_data_provider.ConsumeIntegral<uint64_t>()};213all_headers.push_back(block);215auto headers_msg = NetMsg::Make(NetMsgType::CMPCTBLOCK, TX_WITH_WITNESS(cmpct_block));216g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));217},218[&]() NO_THREAD_SAFETY_ANALYSIS {219// Send a block220auto block = finalized_block();222all_headers.push_back(block);224auto headers_msg = NetMsg::Make(NetMsgType::BLOCK, TX_WITH_WITNESS(block));225g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));226});227}229// This is a conservative overestimate, as base is only moved forward when sending headers. In theory,230// the longest chain generated by this test is 1600 (FUZZ_MAX_HEADERS_RESULTS * 100) headers. In that case,231// this variable will accurately reflect the chain's total work.232total_work += CalculateClaimedHeadersWork(all_headers);234// This test should never create a chain with more work than MinimumChainWork.235assert(total_work < chainman.MinimumChainWork());237// The headers/blocks sent in this test should never be stored, as the chains don't have the work required238// to meet the anti-DoS work threshold. So, if at any point the block index grew in size, then there's a bug239// in the headers pre-sync logic.240assert(WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size()) == original_index_size);241}
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.