Coverage Report

Created: 2026-08-25 19:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/test/fuzz/p2p_transport_serialization.cpp
Line
Count
Source
1
// Copyright (c) 2019-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
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>
14
15
#include <algorithm>
16
#include <cassert>
17
#include <cstdint>
18
#include <limits>
19
#include <optional>
20
#include <vector>
21
22
namespace {
23
24
auto g_all_messages = ALL_NET_MESSAGE_TYPES;
25
26
void initialize_p2p_transport_serialization()
27
0
{
28
0
    static ECC_Context ecc_context{};
29
0
    SelectParams(ChainType::REGTEST);
30
0
    std::sort(g_all_messages.begin(), g_all_messages.end());
31
0
}
32
33
} // namespace
34
35
FUZZ_TARGET(p2p_transport_serialization, .init = initialize_p2p_transport_serialization)
36
447
{
37
    // Construct transports for both sides, with dummy NodeIds.
38
447
    V1Transport recv_transport{NodeId{0}};
39
447
    V1Transport send_transport{NodeId{1}};
40
41
447
    FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
42
43
447
    auto checksum_assist = fuzzed_data_provider.ConsumeBool();
44
447
    auto magic_bytes_assist = fuzzed_data_provider.ConsumeBool();
45
447
    std::vector<uint8_t> mutable_msg_bytes;
46
47
447
    auto header_bytes_remaining = CMessageHeader::HEADER_SIZE;
48
447
    if (magic_bytes_assist) {
  Branch (48:9): [True: 350, False: 97]
49
350
        auto msg_start = Params().MessageStart();
50
1.75k
        for (size_t i = 0; i < CMessageHeader::MESSAGE_SIZE_SIZE; ++i) {
  Branch (50:28): [True: 1.40k, False: 350]
51
1.40k
            mutable_msg_bytes.push_back(msg_start[i]);
52
1.40k
        }
53
350
        header_bytes_remaining -= CMessageHeader::MESSAGE_SIZE_SIZE;
54
350
    }
55
56
447
    if (checksum_assist) {
  Branch (56:9): [True: 183, False: 264]
57
183
        header_bytes_remaining -= CMessageHeader::CHECKSUM_SIZE;
58
183
    }
59
60
447
    auto header_random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(header_bytes_remaining);
61
447
    mutable_msg_bytes.insert(mutable_msg_bytes.end(), header_random_bytes.begin(), header_random_bytes.end());
62
447
    auto payload_bytes = fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>();
63
64
447
    if (checksum_assist && mutable_msg_bytes.size() == CMessageHeader::CHECKSUM_OFFSET) {
  Branch (64:9): [True: 183, False: 264]
  Branch (64:28): [True: 179, False: 4]
65
179
        CHash256 hasher;
66
179
        unsigned char hsh[32];
67
179
        hasher.Write(payload_bytes);
68
179
        hasher.Finalize(hsh);
69
895
        for (size_t i = 0; i < CMessageHeader::CHECKSUM_SIZE; ++i) {
  Branch (69:28): [True: 716, False: 179]
70
716
           mutable_msg_bytes.push_back(hsh[i]);
71
716
        }
72
179
    }
73
74
447
    mutable_msg_bytes.insert(mutable_msg_bytes.end(), payload_bytes.begin(), payload_bytes.end());
75
447
    std::span<const uint8_t> msg_bytes{mutable_msg_bytes};
76
161k
    while (msg_bytes.size() > 0) {
  Branch (76:12): [True: 161k, False: 303]
77
161k
        if (!recv_transport.ReceivedBytes(msg_bytes)) {
  Branch (77:13): [True: 144, False: 161k]
78
144
            break;
79
144
        }
80
161k
        if (recv_transport.ReceivedMessageComplete()) {
  Branch (80:13): [True: 134k, False: 26.9k]
81
134k
            const auto time{NodeClock::time_point::max()};
82
134k
            bool reject_message{false};
83
134k
            CNetMessage msg = recv_transport.GetReceivedMessage(time, reject_message);
84
134k
            assert(msg.m_type.size() <= CMessageHeader::MESSAGE_TYPE_SIZE);
  Branch (84:13): [True: 134k, False: 0]
85
134k
            assert(msg.m_raw_message_size <= mutable_msg_bytes.size());
  Branch (85:13): [True: 134k, False: 0]
86
134k
            assert(msg.m_raw_message_size == CMessageHeader::HEADER_SIZE + msg.m_message_size);
  Branch (86:13): [True: 134k, False: 0]
87
134k
            assert(msg.m_time == time);
  Branch (87:13): [True: 134k, False: 0]
88
89
134k
            auto msg2 = NetMsg::Make(msg.m_type, std::span{msg.m_recv});
90
134k
            bool queued = send_transport.SetMessageToSend(msg2);
91
134k
            assert(queued);
  Branch (91:13): [True: 134k, False: 0]
92
134k
            std::optional<bool> known_more;
93
295k
            while (true) {
  Branch (93:20): [Folded - Ignored]
94
295k
                const auto& [to_send, more, _msg_type] = send_transport.GetBytesToSend(false);
95
295k
                if (known_more) assert(!to_send.empty() == *known_more);
  Branch (95:21): [True: 160k, False: 134k]
  Branch (95:33): [True: 160k, False: 0]
96
295k
                if (to_send.empty()) break;
  Branch (96:21): [True: 134k, False: 160k]
97
160k
                send_transport.MarkBytesSent(to_send.size());
98
160k
                known_more = more;
99
160k
            }
100
134k
        }
101
161k
    }
102
447
}
103
104
namespace {
105
106
template<RandomNumberGenerator R>
107
void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDataProvider& provider)
108
3.18k
{
109
    // Simulation test with two Transport objects, which send messages to each other, with
110
    // sending and receiving fragmented into multiple pieces that may be interleaved. It primarily
111
    // verifies that the sending and receiving side are compatible with each other, plus a few
112
    // sanity checks. It does not attempt to introduce errors in the communicated data.
113
114
    // Put the transports in an array for by-index access.
115
3.18k
    const std::array<Transport*, 2> transports = {&initiator, &responder};
116
117
    // Two vectors representing in-flight bytes. inflight[i] is from transport[i] to transport[!i].
118
3.18k
    std::array<std::vector<uint8_t>, 2> in_flight;
119
120
    // Two queues with expected messages. expected[i] is expected to arrive in transport[!i].
121
3.18k
    std::array<std::deque<CSerializedNetMsg>, 2> expected;
122
123
    // Vectors with bytes last returned by GetBytesToSend() on transport[i].
124
3.18k
    std::array<std::vector<uint8_t>, 2> to_send;
125
126
    // Last returned 'more' values (if still relevant) by transport[i]->GetBytesToSend(), for
127
    // both have_next_message false and true.
128
3.18k
    std::array<std::optional<bool>, 2> last_more, last_more_next;
129
130
    // Whether more bytes to be sent are expected on transport[i], before and after
131
    // SetMessageToSend().
132
3.18k
    std::array<std::optional<bool>, 2> expect_more, expect_more_next;
133
134
    // Function to consume a message type.
135
100k
    auto msg_type_fn = [&]() {
136
100k
        uint8_t v = provider.ConsumeIntegral<uint8_t>();
137
100k
        if (v == 0xFF) {
  Branch (137:13): [True: 17.2k, False: 82.8k]
138
            // If v is 0xFF, construct a valid (but possibly unknown) message type from the fuzz
139
            // data.
140
17.2k
            std::string ret;
141
72.3k
            while (ret.size() < CMessageHeader::MESSAGE_TYPE_SIZE) {
  Branch (141:20): [True: 70.2k, False: 2.09k]
142
70.2k
                char c = provider.ConsumeIntegral<char>();
143
                // Match the allowed characters in CMessageHeader::IsMessageTypeValid(). Any other
144
                // character is interpreted as end.
145
70.2k
                if (c < ' ' || c > 0x7E) break;
  Branch (145:21): [True: 10.9k, False: 59.2k]
  Branch (145:32): [True: 4.18k, False: 55.1k]
146
55.1k
                ret += c;
147
55.1k
            }
148
17.2k
            return ret;
149
82.8k
        } else {
150
            // Otherwise, use it as index into the list of known messages.
151
82.8k
            return g_all_messages[v % g_all_messages.size()];
152
82.8k
        }
153
100k
    };
154
155
    // Function to construct a CSerializedNetMsg to send.
156
106k
    auto make_msg_fn = [&](bool first) {
157
106k
        CSerializedNetMsg msg;
158
106k
        if (first) {
  Branch (158:13): [True: 6.36k, False: 100k]
159
            // Always send a "version" message as first one.
160
6.36k
            msg.m_type = "version";
161
100k
        } else {
162
100k
            msg.m_type = msg_type_fn();
163
100k
        }
164
        // Determine size of message to send (limited to 75 kB for performance reasons).
165
106k
        size_t size = provider.ConsumeIntegralInRange<uint32_t>(0, 75000);
166
        // Get payload of message from RNG.
167
106k
        msg.data = rng.randbytes(size);
168
        // Return.
169
106k
        return msg;
170
106k
    };
171
172
    // The next message to be sent (initially version messages, but will be replaced once sent).
173
3.18k
    std::array<CSerializedNetMsg, 2> next_msg = {
174
3.18k
        make_msg_fn(/*first=*/true),
175
3.18k
        make_msg_fn(/*first=*/true)
176
3.18k
    };
177
178
    // Wrapper around transport[i]->GetBytesToSend() that performs sanity checks.
179
1.48M
    auto bytes_to_send_fn = [&](int side) -> Transport::BytesToSend {
180
        // Invoke GetBytesToSend twice (for have_next_message = {false, true}). This function does
181
        // not modify state (it's const), and only the "more" return value should differ between
182
        // the calls.
183
1.48M
        const auto& [bytes, more_nonext, msg_type] = transports[side]->GetBytesToSend(false);
184
1.48M
        const auto& [bytes_next, more_next, msg_type_next] = transports[side]->GetBytesToSend(true);
185
        // Compare with expected more.
186
1.48M
        if (expect_more[side].has_value()) assert(!bytes.empty() == *expect_more[side]);
  Branch (186:13): [True: 635k, False: 851k]
  Branch (186:44): [True: 635k, False: 0]
187
        // Verify consistency between the two results.
188
1.48M
        assert(std::ranges::equal(bytes, bytes_next));
  Branch (188:9): [True: 1.48M, False: 0]
189
1.48M
        assert(msg_type == msg_type_next);
  Branch (189:9): [True: 1.48M, False: 0]
190
1.48M
        if (more_nonext) assert(more_next);
  Branch (190:13): [True: 178k, False: 1.30M]
  Branch (190:26): [True: 178k, False: 0]
191
        // Compare with previously reported output.
192
1.48M
        assert(to_send[side].size() <= bytes.size());
  Branch (192:9): [True: 1.48M, False: 0]
193
1.48M
        assert(std::ranges::equal(to_send[side], std::span{bytes}.first(to_send[side].size())));
  Branch (193:9): [True: 1.48M, False: 0]
194
1.48M
        to_send[side].resize(bytes.size());
195
1.48M
        std::copy(bytes.begin(), bytes.end(), to_send[side].begin());
196
        // Remember 'more' results.
197
1.48M
        last_more[side] = {more_nonext};
198
1.48M
        last_more_next[side] = {more_next};
199
        // Return.
200
1.48M
        return {bytes, more_nonext, msg_type};
201
1.48M
    };
202
203
    // Function to make side send a new message.
204
414k
    auto new_msg_fn = [&](int side) {
205
        // Don't do anything if there are too many unreceived messages already.
206
414k
        if (expected[side].size() >= 16) return;
  Branch (206:13): [True: 5.17k, False: 409k]
207
        // Try to send (a copy of) the message in next_msg[side].
208
409k
        CSerializedNetMsg msg = next_msg[side].Copy();
209
409k
        bool queued = transports[side]->SetMessageToSend(msg);
210
        // Update expected more data.
211
409k
        expect_more[side] = expect_more_next[side];
212
409k
        expect_more_next[side] = std::nullopt;
213
        // Verify consistency of GetBytesToSend after SetMessageToSend
214
409k
        bytes_to_send_fn(/*side=*/side);
215
409k
        if (queued) {
  Branch (215:13): [True: 100k, False: 309k]
216
            // Remember that this message is now expected by the receiver.
217
100k
            expected[side].emplace_back(std::move(next_msg[side]));
218
            // Construct a new next message to send.
219
100k
            next_msg[side] = make_msg_fn(/*first=*/false);
220
100k
        }
221
409k
    };
222
223
    // Function to make side send out bytes (if any).
224
526k
    auto send_fn = [&](int side, bool everything = false) {
225
526k
        const auto& [bytes, more, msg_type] = bytes_to_send_fn(/*side=*/side);
226
        // Don't do anything if no bytes to send.
227
526k
        if (bytes.empty()) return false;
  Branch (227:13): [True: 133k, False: 392k]
228
392k
        size_t send_now = everything ? bytes.size() : provider.ConsumeIntegralInRange<size_t>(0, bytes.size());
  Branch (228:27): [True: 6.69k, False: 385k]
229
392k
        if (send_now == 0) return false;
  Branch (229:13): [True: 54.6k, False: 337k]
230
        // Add bytes to the in-flight queue, and mark those bytes as consumed.
231
337k
        in_flight[side].insert(in_flight[side].end(), bytes.begin(), bytes.begin() + send_now);
232
337k
        transports[side]->MarkBytesSent(send_now);
233
        // If all to-be-sent bytes were sent, move last_more data to expect_more data.
234
337k
        if (send_now == bytes.size()) {
  Branch (234:13): [True: 121k, False: 215k]
235
121k
            expect_more[side] = last_more[side];
236
121k
            expect_more_next[side] = last_more_next[side];
237
121k
        }
238
        // Remove the bytes from the last reported to-be-sent vector.
239
337k
        assert(to_send[side].size() >= send_now);
  Branch (239:9): [True: 337k, False: 0]
240
337k
        to_send[side].erase(to_send[side].begin(), to_send[side].begin() + send_now);
241
        // Verify that GetBytesToSend gives a result consistent with earlier.
242
337k
        bytes_to_send_fn(/*side=*/side);
243
        // Return whether anything was sent.
244
337k
        return send_now > 0;
245
337k
    };
246
247
    // Function to make !side receive bytes (if any).
248
160k
    auto recv_fn = [&](int side, bool everything = false) {
249
        // Don't do anything if no bytes in flight.
250
160k
        if (in_flight[side].empty()) return false;
  Branch (250:13): [True: 41.5k, False: 118k]
251
        // Decide span to receive
252
118k
        size_t to_recv_len = in_flight[side].size();
253
118k
        if (!everything) to_recv_len = provider.ConsumeIntegralInRange<size_t>(0, to_recv_len);
  Branch (253:13): [True: 111k, False: 7.01k]
254
118k
        std::span<const uint8_t> to_recv = std::span{in_flight[side]}.first(to_recv_len);
255
        // Process those bytes
256
332k
        while (!to_recv.empty()) {
  Branch (256:16): [True: 214k, False: 118k]
257
214k
            size_t old_len = to_recv.size();
258
214k
            bool ret = transports[!side]->ReceivedBytes(to_recv);
259
            // Bytes must always be accepted, as this test does not introduce any errors in
260
            // communication.
261
214k
            assert(ret);
  Branch (261:13): [True: 214k, False: 0]
262
            // Clear cached expected 'more' information: if certainly no more data was to be sent
263
            // before, receiving bytes makes this uncertain.
264
214k
            if (expect_more[!side] == false) expect_more[!side] = std::nullopt;
  Branch (264:17): [True: 9.53k, False: 204k]
265
214k
            if (expect_more_next[!side] == false) expect_more_next[!side] = std::nullopt;
  Branch (265:17): [True: 646, False: 213k]
266
            // Verify consistency of GetBytesToSend after ReceivedBytes
267
214k
            bytes_to_send_fn(/*side=*/!side);
268
214k
            bool progress = to_recv.size() < old_len;
269
214k
            if (transports[!side]->ReceivedMessageComplete()) {
  Branch (269:17): [True: 100k, False: 113k]
270
100k
                bool reject{false};
271
100k
                auto received = transports[!side]->GetReceivedMessage({}, reject);
272
                // Receiving must succeed.
273
100k
                assert(!reject);
  Branch (273:17): [True: 100k, False: 0]
274
                // There must be a corresponding expected message.
275
100k
                assert(!expected[side].empty());
  Branch (275:17): [True: 100k, False: 0]
276
                // The m_message_size field must be correct.
277
100k
                assert(received.m_message_size == received.m_recv.size());
  Branch (277:17): [True: 100k, False: 0]
278
                // The m_type must match what is expected.
279
100k
                assert(received.m_type == expected[side].front().m_type);
  Branch (279:17): [True: 100k, False: 0]
280
                // The data must match what is expected.
281
100k
                assert(std::ranges::equal(received.m_recv, MakeByteSpan(expected[side].front().data)));
  Branch (281:17): [True: 100k, False: 0]
282
100k
                expected[side].pop_front();
283
100k
                progress = true;
284
100k
            }
285
            // Progress must be made (by processing incoming bytes and/or returning complete
286
            // messages) until all received bytes are processed.
287
214k
            assert(progress);
  Branch (287:13): [True: 214k, False: 0]
288
214k
        }
289
        // Remove the processed bytes from the in_flight buffer.
290
118k
        in_flight[side].erase(in_flight[side].begin(), in_flight[side].begin() + to_recv_len);
291
        // Return whether anything was received.
292
118k
        return to_recv_len > 0;
293
118k
    };
294
295
    // Main loop, interleaving new messages, sends, and receives.
296
1.06M
    LIMITED_WHILE (provider.remaining_bytes(), 1000) {
297
1.06M
        CallOneOf(provider,
298
            // (Try to) give the next message to the transport.
299
1.06M
            [&] { new_msg_fn(/*side=*/0); },
300
1.06M
            [&] { new_msg_fn(/*side=*/1); },
301
            // (Try to) send some bytes from the transport to the network.
302
1.06M
            [&] { send_fn(/*side=*/0); },
303
1.06M
            [&] { send_fn(/*side=*/1); },
304
            // (Try to) receive bytes from the network, converting to messages.
305
1.06M
            [&] { recv_fn(/*side=*/0); },
306
1.06M
            [&] { recv_fn(/*side=*/1); }
307
1.06M
        );
308
1.06M
    }
309
310
    // When we're done, perform sends and receives of existing messages to flush anything already
311
    // in flight.
312
7.90k
    while (true) {
  Branch (312:12): [Folded - Ignored]
313
7.90k
        bool any = false;
314
7.90k
        if (send_fn(/*side=*/0, /*everything=*/true)) any = true;
  Branch (314:13): [True: 3.83k, False: 4.07k]
315
7.90k
        if (send_fn(/*side=*/1, /*everything=*/true)) any = true;
  Branch (315:13): [True: 2.86k, False: 5.04k]
316
7.90k
        if (recv_fn(/*side=*/0, /*everything=*/true)) any = true;
  Branch (316:13): [True: 3.94k, False: 3.95k]
317
7.90k
        if (recv_fn(/*side=*/1, /*everything=*/true)) any = true;
  Branch (317:13): [True: 3.06k, False: 4.83k]
318
7.90k
        if (!any) break;
  Branch (318:13): [True: 3.18k, False: 4.72k]
319
7.90k
    }
320
321
    // Make sure nothing is left in flight.
322
3.18k
    assert(in_flight[0].empty());
  Branch (322:5): [True: 3.18k, False: 0]
323
3.18k
    assert(in_flight[1].empty());
  Branch (323:5): [True: 3.18k, False: 0]
324
325
    // Make sure all expected messages were received.
326
3.18k
    assert(expected[0].empty());
  Branch (326:5): [True: 3.18k, False: 0]
327
3.18k
    assert(expected[1].empty());
  Branch (327:5): [True: 3.18k, False: 0]
328
329
    // Compare session IDs.
330
3.18k
    assert(transports[0]->GetInfo().session_id == transports[1]->GetInfo().session_id);
  Branch (330:5): [True: 3.18k, False: 0]
331
3.18k
}
332
333
std::unique_ptr<Transport> MakeV1Transport(NodeId nodeid) noexcept
334
2.25k
{
335
2.25k
    return std::make_unique<V1Transport>(nodeid);
336
2.25k
}
337
338
template<RandomNumberGenerator RNG>
339
std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& rng, FuzzedDataProvider& provider)
340
4.64k
{
341
    // Retrieve key
342
4.64k
    auto key = ConsumePrivateKey(provider);
343
4.64k
    if (!key.IsValid()) return {};
  Branch (343:9): [True: 281, False: 4.36k]
344
    // Construct garbage
345
4.36k
    size_t garb_len = provider.ConsumeIntegralInRange<size_t>(0, V2Transport::MAX_GARBAGE_LEN);
346
4.36k
    std::vector<uint8_t> garb;
347
4.36k
    if (garb_len <= 64) {
  Branch (347:9): [True: 1.51k, False: 2.84k]
348
        // When the garbage length is up to 64 bytes, read it directly from the fuzzer input.
349
1.51k
        garb = provider.ConsumeBytes<uint8_t>(garb_len);
350
1.51k
        garb.resize(garb_len);
351
2.84k
    } else {
352
        // If it's longer, generate it from the RNG. This avoids having large amounts of
353
        // (hopefully) irrelevant data needing to be stored in the fuzzer data.
354
2.84k
        garb = rng.randbytes(garb_len);
355
2.84k
    }
356
    // Retrieve entropy
357
4.36k
    auto ent = provider.ConsumeBytes<std::byte>(32);
358
4.36k
    ent.resize(32);
359
    // Use as entropy SHA256(ent || garbage). This prevents a situation where the fuzzer manages to
360
    // 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 choose
362
    // both non-randomly and dependently. Since the entropy is hashed anyway inside the ellswift
363
    // computation, no coverage should be lost by using a hash as entropy, and it removes the
364
    // possibility of garbage that happens to contain what is effectively a hash of the keys.
365
4.36k
    CSHA256().Write(UCharCast(ent.data()), ent.size())
366
4.36k
             .Write(garb.data(), garb.size())
367
4.36k
             .Finalize(UCharCast(ent.data()));
368
369
4.36k
    return std::make_unique<V2Transport>(nodeid, initiator, key, ent, std::move(garb));
370
4.64k
}
371
372
} // namespace
373
374
FUZZ_TARGET(p2p_transport_bidirectional, .init = initialize_p2p_transport_serialization)
375
603
{
376
    // Test with two V1 transports talking to each other.
377
603
    FuzzedDataProvider provider{buffer.data(), buffer.size()};
378
603
    InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
379
603
    auto t1 = MakeV1Transport(NodeId{0});
380
603
    auto t2 = MakeV1Transport(NodeId{1});
381
603
    if (!t1 || !t2) return;
  Branch (381:9): [True: 0, False: 603]
  Branch (381:16): [True: 0, False: 603]
382
603
    SimulationTest(*t1, *t2, rng, provider);
383
603
}
384
385
FUZZ_TARGET(p2p_transport_bidirectional_v2, .init = initialize_p2p_transport_serialization)
386
1.79k
{
387
    // Test with two V2 transports talking to each other.
388
1.79k
    FuzzedDataProvider provider{buffer.data(), buffer.size()};
389
1.79k
    InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
390
1.79k
    auto t1 = MakeV2Transport(NodeId{0}, true, rng, provider);
391
1.79k
    auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);
392
1.79k
    if (!t1 || !t2) return;
  Branch (392:9): [True: 19, False: 1.77k]
  Branch (392:16): [True: 232, False: 1.54k]
393
1.54k
    SimulationTest(*t1, *t2, rng, provider);
394
1.54k
}
395
396
FUZZ_TARGET(p2p_transport_bidirectional_v1v2, .init = initialize_p2p_transport_serialization)
397
1.05k
{
398
    // Test with a V1 initiator talking to a V2 responder.
399
1.05k
    FuzzedDataProvider provider{buffer.data(), buffer.size()};
400
1.05k
    InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
401
1.05k
    auto t1 = MakeV1Transport(NodeId{0});
402
1.05k
    auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);
403
1.05k
    if (!t1 || !t2) return;
  Branch (403:9): [True: 0, False: 1.05k]
  Branch (403:16): [True: 18, False: 1.03k]
404
1.03k
    SimulationTest(*t1, *t2, rng, provider);
405
1.03k
}