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.
251// Decide span to receive252size_t to_recv_len = in_flight[side].size();253if (!everything) to_recv_len = provider.ConsumeIntegralInRange<size_t>(0, to_recv_len);254std::span<const uint8_t> to_recv = std::span{in_flight[side]}.first(to_recv_len);255// Process those bytes256while (!to_recv.empty()) {257size_t old_len = to_recv.size();258bool ret = transports[!side]->ReceivedBytes(to_recv);259// Bytes must always be accepted, as this test does not introduce any errors in260// communication.261assert(ret);262// Clear cached expected 'more' information: if certainly no more data was to be sent263// before, receiving bytes makes this uncertain.264if (expect_more[!side] == false) expect_more[!side] = std::nullopt;265if (expect_more_next[!side] == false) expect_more_next[!side] = std::nullopt;266// Verify consistency of GetBytesToSend after ReceivedBytes267bytes_to_send_fn(/*side=*/!side);268bool progress = to_recv.size() < old_len;269if (transports[!side]->ReceivedMessageComplete()) {270bool reject{false};271auto received = transports[!side]->GetReceivedMessage({}, reject);272// Receiving must succeed.273assert(!reject);274// There must be a corresponding expected message.275assert(!expected[side].empty());276// The m_message_size field must be correct.277assert(received.m_message_size == received.m_recv.size());278// The m_type must match what is expected.279assert(received.m_type == expected[side].front().m_type);280// The data must match what is expected.281assert(std::ranges::equal(received.m_recv, MakeByteSpan(expected[side].front().data)));282expected[side].pop_front();283progress = true;284}285// Progress must be made (by processing incoming bytes and/or returning complete286// messages) until all received bytes are processed.287assert(progress);288}289// Remove the processed bytes from the in_flight buffer.290in_flight[side].erase(in_flight[side].begin(), in_flight[side].begin() + to_recv_len);291// Return whether anything was received.292return to_recv_len > 0;293};295// Main loop, interleaving new messages, sends, and receives.296LIMITED_WHILE (provider.remaining_bytes(), 1000) {297CallOneOf(provider,298// (Try to) give the next message to the transport.299[&] { new_msg_fn(/*side=*/0); },300[&] { new_msg_fn(/*side=*/1); },301// (Try to) send some bytes from the transport to the network.302[&] { send_fn(/*side=*/0); },303[&] { send_fn(/*side=*/1); },304// (Try to) receive bytes from the network, converting to messages.305[&] { recv_fn(/*side=*/0); },306[&] { recv_fn(/*side=*/1); }307);308}310// When we're done, perform sends and receives of existing messages to flush anything already311// in flight.312while (true) {313bool any = false;314if (send_fn(/*side=*/0, /*everything=*/true)) any = true;315if (send_fn(/*side=*/1, /*everything=*/true)) any = true;316if (recv_fn(/*side=*/0, /*everything=*/true)) any = true;317if (recv_fn(/*side=*/1, /*everything=*/true)) any = true;318if (!any) break;319}321// Make sure nothing is left in flight.322assert(in_flight[0].empty());323assert(in_flight[1].empty());325// Make sure all expected messages were received.326assert(expected[0].empty());327assert(expected[1].empty());329// Compare session IDs.330assert(transports[0]->GetInfo().session_id == transports[1]->GetInfo().session_id);331}333std::unique_ptr<Transport> MakeV1Transport(NodeId nodeid) noexcept334{335return std::make_unique<V1Transport>(nodeid);336}338template<RandomNumberGenerator RNG>339std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& rng, FuzzedDataProvider& provider)340{341// Retrieve key342auto key = ConsumePrivateKey(provider);343if (!key.IsValid()) return {};344// Construct garbage345size_t garb_len = provider.ConsumeIntegralInRange<size_t>(0, V2Transport::MAX_GARBAGE_LEN);346std::vector<uint8_t> garb;347if (garb_len <= 64) {348// When the garbage length is up to 64 bytes, read it directly from the fuzzer input.349garb = provider.ConsumeBytes<uint8_t>(garb_len);350garb.resize(garb_len);351} else {352// If it's longer, generate it from the RNG. This avoids having large amounts of353// (hopefully) irrelevant data needing to be stored in the fuzzer data.354garb = rng.randbytes(garb_len);355}356// Retrieve entropy357auto ent = provider.ConsumeBytes<std::byte>(32);358ent.resize(32);359// Use as entropy SHA256(ent || garbage). This prevents a situation where the fuzzer manages to360// include the garbage terminator (which is a function of both ellswift keys) in the garbage.361// This is extremely unlikely (~2^-116) with random keys/garbage, but the fuzzer can choose362// both non-randomly and dependently. Since the entropy is hashed anyway inside the ellswift363// computation, no coverage should be lost by using a hash as entropy, and it removes the364// possibility of garbage that happens to contain what is effectively a hash of the keys.365CSHA256().Write(UCharCast(ent.data()), ent.size())366.Write(garb.data(), garb.size())367.Finalize(UCharCast(ent.data()));369return std::make_unique<V2Transport>(nodeid, initiator, key, ent, std::move(garb));370}372} // namespace374FUZZ_TARGET(p2p_transport_bidirectional, .init = initialize_p2p_transport_serialization)375{376// Test with two V1 transports talking to each other.377FuzzedDataProvider provider{buffer.data(), buffer.size()};378InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());379auto t1 = MakeV1Transport(NodeId{0});380auto t2 = MakeV1Transport(NodeId{1});381if (!t1 || !t2) return;382SimulationTest(*t1, *t2, rng, provider);383}385FUZZ_TARGET(p2p_transport_bidirectional_v2, .init = initialize_p2p_transport_serialization)386{387// Test with two V2 transports talking to each other.388FuzzedDataProvider provider{buffer.data(), buffer.size()};389InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());390auto t1 = MakeV2Transport(NodeId{0}, true, rng, provider);391auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);392if (!t1 || !t2) return;393SimulationTest(*t1, *t2, rng, provider);394}396FUZZ_TARGET(p2p_transport_bidirectional_v1v2, .init = initialize_p2p_transport_serialization)397{398// Test with a V1 initiator talking to a V2 responder.399FuzzedDataProvider provider{buffer.data(), buffer.size()};400InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());401auto t1 = MakeV1Transport(NodeId{0});402auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);403if (!t1 || !t2) return;404SimulationTest(*t1, *t2, rng, provider);405}
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.