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) 2019-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 <chainparams.h>6#include <hash.h>7#include <net.h>8#include <netmessagemaker.h>9#include <protocol.h>10#include <test/fuzz/FuzzedDataProvider.h>11#include <test/fuzz/fuzz.h>12#include <test/fuzz/util.h>13#include <util/chaintype.h>15#include <algorithm>16#include <cassert>17#include <cstdint>18#include <limits>19#include <optional>20#include <vector>22namespace {24auto g_all_messages = ALL_NET_MESSAGE_TYPES;26void initialize_p2p_transport_serialization()27{28static ECC_Context ecc_context{};29SelectParams(ChainType::REGTEST);30std::sort(g_all_messages.begin(), g_all_messages.end());31}33} // namespace35FUZZ_TARGET(p2p_transport_serialization, .init = initialize_p2p_transport_serialization)36{37// Construct transports for both sides, with dummy NodeIds.38V1Transport recv_transport{NodeId{0}};39V1Transport send_transport{NodeId{1}};41FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};43auto checksum_assist = fuzzed_data_provider.ConsumeBool();44auto magic_bytes_assist = fuzzed_data_provider.ConsumeBool();45std::vector<uint8_t> mutable_msg_bytes;47auto header_bytes_remaining = CMessageHeader::HEADER_SIZE;48if (magic_bytes_assist) {49auto msg_start = Params().MessageStart();50for (size_t i = 0; i < CMessageHeader::MESSAGE_SIZE_SIZE; ++i) {51mutable_msg_bytes.push_back(msg_start[i]);52}53header_bytes_remaining -= CMessageHeader::MESSAGE_SIZE_SIZE;54}56if (checksum_assist) {57header_bytes_remaining -= CMessageHeader::CHECKSUM_SIZE;58}60auto header_random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(header_bytes_remaining);61mutable_msg_bytes.insert(mutable_msg_bytes.end(), header_random_bytes.begin(), header_random_bytes.end());62auto payload_bytes = fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>();64if (checksum_assist && mutable_msg_bytes.size() == CMessageHeader::CHECKSUM_OFFSET) {65CHash256 hasher;66unsigned char hsh[32];67hasher.Write(payload_bytes);68hasher.Finalize(hsh);69for (size_t i = 0; i < CMessageHeader::CHECKSUM_SIZE; ++i) {70mutable_msg_bytes.push_back(hsh[i]);71}72}74mutable_msg_bytes.insert(mutable_msg_bytes.end(), payload_bytes.begin(), payload_bytes.end());75std::span<const uint8_t> msg_bytes{mutable_msg_bytes};76while (msg_bytes.size() > 0) {77if (!recv_transport.ReceivedBytes(msg_bytes)) {78break;79}80if (recv_transport.ReceivedMessageComplete()) {81const auto time{NodeClock::time_point::max()};82bool reject_message{false};83CNetMessage msg = recv_transport.GetReceivedMessage(time, reject_message);84assert(msg.m_type.size() <= CMessageHeader::MESSAGE_TYPE_SIZE);85assert(msg.m_raw_message_size <= mutable_msg_bytes.size());86assert(msg.m_raw_message_size == CMessageHeader::HEADER_SIZE + msg.m_message_size);87assert(msg.m_time == time);89auto msg2 = NetMsg::Make(msg.m_type, std::span{msg.m_recv});90bool queued = send_transport.SetMessageToSend(msg2);91assert(queued);92std::optional<bool> known_more;93while (true) {94const auto& [to_send, more, _msg_type] = send_transport.GetBytesToSend(false);95if (known_more) assert(!to_send.empty() == *known_more);96if (to_send.empty()) break;97send_transport.MarkBytesSent(to_send.size());98known_more = more;99}100}101}102}104namespace {106template<RandomNumberGenerator R>107void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDataProvider& provider)108{109// Simulation test with two Transport objects, which send messages to each other, with110// sending and receiving fragmented into multiple pieces that may be interleaved. It primarily111// verifies that the sending and receiving side are compatible with each other, plus a few112// sanity checks. It does not attempt to introduce errors in the communicated data.114// Put the transports in an array for by-index access.115const std::array<Transport*, 2> transports = {&initiator, &responder};117// Two vectors representing in-flight bytes. inflight[i] is from transport[i] to transport[!i].118std::array<std::vector<uint8_t>, 2> in_flight;120// Two queues with expected messages. expected[i] is expected to arrive in transport[!i].121std::array<std::deque<CSerializedNetMsg>, 2> expected;123// Vectors with bytes last returned by GetBytesToSend() on transport[i].124std::array<std::vector<uint8_t>, 2> to_send;126// Last returned 'more' values (if still relevant) by transport[i]->GetBytesToSend(), for127// both have_next_message false and true.128std::array<std::optional<bool>, 2> last_more, last_more_next;130// Whether more bytes to be sent are expected on transport[i], before and after131// SetMessageToSend().132std::array<std::optional<bool>, 2> expect_more, expect_more_next;134// Function to consume a message type.135auto msg_type_fn = [&]() {136uint8_t v = provider.ConsumeIntegral<uint8_t>();137if (v == 0xFF) {138// If v is 0xFF, construct a valid (but possibly unknown) message type from the fuzz139// data.140std::string ret;141while (ret.size() < CMessageHeader::MESSAGE_TYPE_SIZE) {142char c = provider.ConsumeIntegral<char>();143// Match the allowed characters in CMessageHeader::IsMessageTypeValid(). Any other144// character is interpreted as end.145if (c < ' ' || c > 0x7E) break;146ret += c;147}148return ret;149} else {150// Otherwise, use it as index into the list of known messages.151return g_all_messages[v % g_all_messages.size()];152}153};155// Function to construct a CSerializedNetMsg to send.156auto make_msg_fn = [&](bool first) {157CSerializedNetMsg msg;158if (first) {159// Always send a "version" message as first one.160msg.m_type = "version";161} else {162msg.m_type = msg_type_fn();163}164// Determine size of message to send (limited to 75 kB for performance reasons).165size_t size = provider.ConsumeIntegralInRange<uint32_t>(0, 75000);166// Get payload of message from RNG.167msg.data = rng.randbytes(size);168// Return.169return msg;170};172// The next message to be sent (initially version messages, but will be replaced once sent).173std::array<CSerializedNetMsg, 2> next_msg = {174make_msg_fn(/*first=*/true),175make_msg_fn(/*first=*/true)176};178// Wrapper around transport[i]->GetBytesToSend() that performs sanity checks.179auto bytes_to_send_fn = [&](int side) -> Transport::BytesToSend {180// Invoke GetBytesToSend twice (for have_next_message = {false, true}). This function does181// not modify state (it's const), and only the "more" return value should differ between182// the calls.183const auto& [bytes, more_nonext, msg_type] = transports[side]->GetBytesToSend(false);184const auto& [bytes_next, more_next, msg_type_next] = transports[side]->GetBytesToSend(true);185// Compare with expected more.186if (expect_more[side].has_value()) assert(!bytes.empty() == *expect_more[side]);187// Verify consistency between the two results.188assert(std::ranges::equal(bytes, bytes_next));189assert(msg_type == msg_type_next);190if (more_nonext) assert(more_next);191// Compare with previously reported output.192assert(to_send[side].size() <= bytes.size());193assert(std::ranges::equal(to_send[side], std::span{bytes}.first(to_send[side].size())));194to_send[side].resize(bytes.size());195std::copy(bytes.begin(), bytes.end(), to_send[side].begin());196// Remember 'more' results.197last_more[side] = {more_nonext};198last_more_next[side] = {more_next};199// Return.200return {bytes, more_nonext, msg_type};201};203// Function to make side send a new message.204auto new_msg_fn = [&](int side) {205// Don't do anything if there are too many unreceived messages already.206if (expected[side].size() >= 16) return;207// Try to send (a copy of) the message in next_msg[side].208CSerializedNetMsg msg = next_msg[side].Copy();209bool queued = transports[side]->SetMessageToSend(msg);210// Update expected more data.211expect_more[side] = expect_more_next[side];212expect_more_next[side] = std::nullopt;213// Verify consistency of GetBytesToSend after SetMessageToSend214bytes_to_send_fn(/*side=*/side);215if (queued) {216// Remember that this message is now expected by the receiver.217expected[side].emplace_back(std::move(next_msg[side]));218// Construct a new next message to send.219next_msg[side] = make_msg_fn(/*first=*/false);220}221};223// Function to make side send out bytes (if any).224auto send_fn = [&](int side, bool everything = false) {225const auto& [bytes, more, msg_type] = bytes_to_send_fn(/*side=*/side);226// Don't do anything if no bytes to send.227if (bytes.empty()) return false;228size_t send_now = everything ? bytes.size() : provider.ConsumeIntegralInRange<size_t>(0, bytes.size());229if (send_now == 0) return false;230// Add bytes to the in-flight queue, and mark those bytes as consumed.231in_flight[side].insert(in_flight[side].end(), bytes.begin(), bytes.begin() + send_now);232transports[side]->MarkBytesSent(send_now);233// If all to-be-sent bytes were sent, move last_more data to expect_more data.234if (send_now == bytes.size()) {235expect_more[side] = last_more[side];236expect_more_next[side] = last_more_next[side];237}238// Remove the bytes from the last reported to-be-sent vector.239assert(to_send[side].size() >= send_now);240to_send[side].erase(to_send[side].begin(), to_send[side].begin() + send_now);241// Verify that GetBytesToSend gives a result consistent with earlier.242bytes_to_send_fn(/*side=*/side);243// Return whether anything was sent.244return send_now > 0;245};247// Function to make !side receive bytes (if any).248auto recv_fn = [&](int side, bool everything = false) {249// Don't do anything if no bytes in flight.250if (in_flight[side].empty()) return false;
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.