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) 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 <banman.h>6#include <net.h>7#include <net_processing.h>8#include <protocol.h>9#include <sync.h>10#include <test/fuzz/FuzzedDataProvider.h>11#include <test/fuzz/fuzz.h>12#include <test/fuzz/util.h>13#include <test/fuzz/util/net.h>14#include <test/util/net.h>15#include <test/util/setup_common.h>16#include <test/util/time.h>17#include <test/util/validation.h>18#include <util/time.h>19#include <validationinterface.h>21#include <array>22#include <ios>23#include <memory>24#include <vector>26namespace {27TestingSetup* g_setup;29void initialize()30{31static const auto testing_setup = MakeNoLogFileContext<TestingSetup>(32/*chain_type=*/ChainType::REGTEST);33g_setup = testing_setup.get();34}36// Inbound message types with private broadcast specific handling.37// Used as the guided path in the CallOneOf() below.38constexpr std::array INBOUND_MSG_TYPES{39NetMsgType::VERSION,40NetMsgType::VERACK,41NetMsgType::GETDATA,42NetMsgType::PONG,43};44} // namespace46FUZZ_TARGET(p2p_private_broadcast, .init = ::initialize)47{48SeedRandomStateForTest(SeedRand::ZEROS);49FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());51auto& node{g_setup->m_node};52auto& connman{static_cast<ConnmanTestMsg&>(*node.connman)};53connman.Reset();54auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};56FakeNodeClock clock_ctx{1610000000s}; // 2021-01-07, arbitrary57FakeSteadyClock steady_clock;58chainman.ResetIbd();59// Sometimes leave IBD: incoming TX processing (the broadcast-abort path)60// returns early during IBD.61if (fuzzed_data_provider.ConsumeBool()) chainman.JumpOutOfIbd();63// Reset, so that dangling pointers can be detected by sanitizers.64node.banman.reset();65node.addrman.reset();66node.peerman.reset();67node.addrman = std::make_unique<AddrMan>(68*node.netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0);69node.peerman = PeerManager::make(connman, *node.addrman,70/*banman=*/nullptr, chainman,71*node.mempool, *node.warnings,72PeerManager::Options{73.reconcile_txs = true,74.deterministic_rng = true,75});76connman.SetMsgProc(node.peerman.get());77connman.SetAddrman(*node.addrman);79// Seed with 0-3 transactions to test multiple pending broadcasts; zero exercises80// the connected-in-vain disconnect in PushPrivateBroadcastTx().81const int num_txs{fuzzed_data_provider.ConsumeIntegralInRange(0, 3)};82std::vector<CTransactionRef> seeded_txs;83for (int i = 0; i < num_txs; ++i) {84auto tx{MakeTransactionRef(ConsumeTransaction(fuzzed_data_provider, /*prevout_txids=*/std::nullopt))};85(void)node.peerman->InitiateTxBroadcastPrivate(tx);86seeded_txs.push_back(tx);87}89LOCK(NetEventsInterface::g_msgproc_mutex);91static NodeId node_id{0};92// Create at least one PRIVATE_BROADCAST peer, optionally add others of random types.93std::vector<CNode*> peers;95CNode* pb_node = new CNode(96/*id=*/node_id++,97/*sock=*/std::make_shared<FuzzedSock>(fuzzed_data_provider, steady_clock),98/*addrIn=*/ConsumeAddress(fuzzed_data_provider),99/*nKeyedNetGroupIn=*/0,100/*nLocalHostNonceIn=*/0,101/*addrBindIn=*/CService{},102/*addrNameIn=*/"",103/*conn_type_in=*/ConnectionType::PRIVATE_BROADCAST,104/*inbound_onion=*/false,105/*network_key=*/0);107peers.push_back(pb_node);108connman.AddTestNode(*pb_node);109// Capture outbound messages to verify if well formed (and to learn the PING110// nonce), before SocketSendData drains vSendMsg.111connman.SetCaptureMessages(true);112const auto CaptureMessageOrig = CaptureMessage;113const CAddress pb_addr = pb_node->addr;114std::optional<uint64_t> pb_ping_nonce;115CaptureMessage = [&](const CAddress& addr, const std::string& msg_type,116std::span<const unsigned char> data, bool is_incoming) {117if (is_incoming || addr != pb_addr) return;118if (msg_type == NetMsgType::PING) {119Assert(data.size() == sizeof(uint64_t));120uint64_t nonce;121SpanReader{data} >> nonce;122pb_ping_nonce = nonce;123return;124}125if (msg_type == NetMsgType::VERSION) {126SpanReader ds{data};127int32_t version;128uint64_t my_services, your_services, my_services_dup, nonce;129int64_t my_time;130CService your_addr, my_addr;131std::string user_agent;132int32_t height;133bool relay;134ds >> version >> my_services >> my_time >>135your_services >> CNetAddr::V1(your_addr) >>136my_services_dup >> CNetAddr::V1(my_addr) >>137nonce >> user_agent >> height >> relay;138Assert(version == WTXID_RELAY_VERSION);139Assert(my_services == NODE_NONE && my_services_dup == NODE_NONE);140Assert(my_time == 0);141Assert(your_services == NODE_NONE);142Assert(your_addr == CService{});143Assert(user_agent == "/pynode:0.0.1/");144Assert(height == 0);145Assert(!relay);146return;147}148if (msg_type != NetMsgType::INV) return;149SpanReader ds{data};150std::vector<CInv> invs;151ds >> invs;152Assert(invs.size() == 1);153Assert(invs[0].IsMsgTx());154};156// Complete handshake so PushPrivateBroadcastTx runs.157connman.Handshake(158/*node=*/*pb_node,159/*successfully_connected=*/true,160/*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS),161/*local_services=*/NODE_NONE,162/*version=*/PROTOCOL_VERSION,163/*relay_txs=*/true);165// Optionally add extra peers of random connection types.166const int extra_peers{fuzzed_data_provider.ConsumeIntegralInRange(0, 2)};167for (int i = 0; i < extra_peers; ++i) {168auto extra_peer{ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock, node_id++)};169// An address collision would match the capture hook's filter and fail170// its assertions on this peer's (legitimate) other-typed messages.171if (extra_peer->addr == pb_addr) continue;172peers.push_back(extra_peer.release());173connman.AddTestNode(*peers.back());174node.peerman->InitializeNode(175*peers.back(),176static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()));177}179LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)180{181// Pick any random peer to test interleaved message handling.182CNode& p2p_node = *PickValue(fuzzed_data_provider, peers);183if (p2p_node.fDisconnect) continue;185clock_ctx += ConsumeDuration<std::chrono::seconds>(fuzzed_data_provider, 0s, 600s);187std::optional<CSerializedNetMsg> net_msg;188CallOneOf(189fuzzed_data_provider,190[&] {191net_msg.emplace();192net_msg->m_type = std::string{PickValue(fuzzed_data_provider, INBOUND_MSG_TYPES)};193},194[&] {195net_msg.emplace();196net_msg->m_type = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::MESSAGE_TYPE_SIZE);197},198[&] {199(void)node.peerman->InitiateTxBroadcastPrivate(200MakeTransactionRef(ConsumeTransaction(fuzzed_data_provider, /*prevout_txids=*/std::nullopt)));201},202[&] {203// Construct a valid GETDATA for a seeded tx to exercise the TX send path.204if (p2p_node.IsPrivateBroadcastConn() &&205p2p_node.fSuccessfullyConnected &&206!seeded_txs.empty()) {207const auto& tx{PickValue(fuzzed_data_provider, seeded_txs)};208net_msg.emplace(NetMsg::Make(209NetMsgType::GETDATA,210std::vector<CInv>{{MSG_TX, tx->GetHash().ToUint256()}}));211}212},213[&] {214// Confirm reception of the pushed TX with a PONG matching the captured PING nonce.215if (&p2p_node == pb_node && pb_ping_nonce) {216net_msg.emplace(NetMsg::Make(NetMsgType::PONG, *pb_ping_nonce));217}218},219[&] {220// Echo a seeded tx back from a non-private-broadcast peer to exercise221// the received-from-network broadcast-abort path.222if (!p2p_node.IsPrivateBroadcastConn() &&223p2p_node.fSuccessfullyConnected &&224!seeded_txs.empty()) {225const auto& tx{PickValue(fuzzed_data_provider, seeded_txs)};226net_msg.emplace(NetMsg::Make(NetMsgType::TX, TX_WITH_WITNESS(*tx)));227}228});230if (net_msg) {231if (net_msg->data.empty()) {232net_msg->data = ConsumeRandomLengthByteVector(fuzzed_data_provider, MAX_PROTOCOL_MESSAGE_LENGTH);233}234connman.FlushSendBuffer(p2p_node);236// ConsumeTransaction() can produce messages larger than the237// maximum payload accepted by the P2P transport.238if (net_msg->data.size() > MAX_PROTOCOL_MESSAGE_LENGTH) continue;240(void)connman.ReceiveMsgFrom(p2p_node, std::move(*net_msg));242bool more_work{true};243while (more_work) {244p2p_node.fPauseSend = false;245try {246more_work = connman.ProcessMessagesOnce(p2p_node);247} catch (const std::ios_base::failure&) {248}249node.peerman->SendMessages(p2p_node);250}
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.