@@ -22,6 +22,7 @@ std::vector<std::string> g_all_messages;
2222
2323void initialize_p2p_transport_serialization ()
2424{
25+ ECC_Start ();
2526 SelectParams (CBaseChainParams::REGTEST);
2627 g_all_messages = getAllNetMessageTypes ();
2728 std::sort (g_all_messages.begin (), g_all_messages.end ());
@@ -87,7 +88,7 @@ FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serializa
8788 assert (queued);
8889 std::optional<bool > known_more;
8990 while (true ) {
90- const auto & [to_send, more, _msg_type] = send_transport.GetBytesToSend ();
91+ const auto & [to_send, more, _msg_type] = send_transport.GetBytesToSend (false );
9192 if (known_more) assert (!to_send.empty () == *known_more);
9293 if (to_send.empty ()) break ;
9394 send_transport.MarkBytesSent (to_send.size ());
@@ -119,11 +120,13 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa
119120 // Vectors with bytes last returned by GetBytesToSend() on transport[i].
120121 std::array<std::vector<uint8_t >, 2 > to_send;
121122
122- // Last returned 'more' values (if still relevant) by transport[i]->GetBytesToSend().
123- std::array<std::optional<bool >, 2 > last_more;
123+ // Last returned 'more' values (if still relevant) by transport[i]->GetBytesToSend(), for
124+ // both have_next_message false and true.
125+ std::array<std::optional<bool >, 2 > last_more, last_more_next;
124126
125- // Whether more bytes to be sent are expected on transport[i].
126- std::array<std::optional<bool >, 2 > expect_more;
127+ // Whether more bytes to be sent are expected on transport[i], before and after
128+ // SetMessageToSend().
129+ std::array<std::optional<bool >, 2 > expect_more, expect_more_next;
127130
128131 // Function to consume a message type.
129132 auto msg_type_fn = [&]() {
@@ -172,18 +175,27 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa
172175
173176 // Wrapper around transport[i]->GetBytesToSend() that performs sanity checks.
174177 auto bytes_to_send_fn = [&](int side) -> Transport::BytesToSend {
175- const auto & [bytes, more, msg_type] = transports[side]->GetBytesToSend ();
178+ // Invoke GetBytesToSend twice (for have_next_message = {false, true}). This function does
179+ // not modify state (it's const), and only the "more" return value should differ between
180+ // the calls.
181+ const auto & [bytes, more_nonext, msg_type] = transports[side]->GetBytesToSend (false );
182+ const auto & [bytes_next, more_next, msg_type_next] = transports[side]->GetBytesToSend (true );
176183 // Compare with expected more.
177184 if (expect_more[side].has_value ()) assert (!bytes.empty () == *expect_more[side]);
185+ // Verify consistency between the two results.
186+ assert (bytes == bytes_next);
187+ assert (msg_type == msg_type_next);
188+ if (more_nonext) assert (more_next);
178189 // Compare with previously reported output.
179190 assert (to_send[side].size () <= bytes.size ());
180191 assert (to_send[side] == Span{bytes}.first (to_send[side].size ()));
181192 to_send[side].resize (bytes.size ());
182193 std::copy (bytes.begin (), bytes.end (), to_send[side].begin ());
183- // Remember 'more' result.
184- last_more[side] = {more};
194+ // Remember 'more' results.
195+ last_more[side] = {more_nonext};
196+ last_more_next[side] = {more_next};
185197 // Return.
186- return {bytes, more , msg_type};
198+ return {bytes, more_nonext , msg_type};
187199 };
188200
189201 // Function to make side send a new message.
@@ -194,7 +206,8 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa
194206 CSerializedNetMsg msg = next_msg[side].Copy ();
195207 bool queued = transports[side]->SetMessageToSend (msg);
196208 // Update expected more data.
197- expect_more[side] = std::nullopt ;
209+ expect_more[side] = expect_more_next[side];
210+ expect_more_next[side] = std::nullopt ;
198211 // Verify consistency of GetBytesToSend after SetMessageToSend
199212 bytes_to_send_fn (/* side=*/ side);
200213 if (queued) {
@@ -218,6 +231,7 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa
218231 // If all to-be-sent bytes were sent, move last_more data to expect_more data.
219232 if (send_now == bytes.size ()) {
220233 expect_more[side] = last_more[side];
234+ expect_more_next[side] = last_more_next[side];
221235 }
222236 // Remove the bytes from the last reported to-be-sent vector.
223237 assert (to_send[side].size () >= send_now);
@@ -246,6 +260,7 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa
246260 // Clear cached expected 'more' information: if certainly no more data was to be sent
247261 // before, receiving bytes makes this uncertain.
248262 if (expect_more[!side] == false ) expect_more[!side] = std::nullopt ;
263+ if (expect_more_next[!side] == false ) expect_more_next[!side] = std::nullopt ;
249264 // Verify consistency of GetBytesToSend after ReceivedBytes
250265 bytes_to_send_fn (/* side=*/ !side);
251266 bool progress = to_recv.size () < old_len;
@@ -315,6 +330,40 @@ std::unique_ptr<Transport> MakeV1Transport(NodeId nodeid) noexcept
315330 return std::make_unique<V1Transport>(nodeid, SER_NETWORK, INIT_PROTO_VERSION);
316331}
317332
333+ template <typename RNG>
334+ std::unique_ptr<Transport> MakeV2Transport (NodeId nodeid, bool initiator, RNG& rng, FuzzedDataProvider& provider)
335+ {
336+ // Retrieve key
337+ auto key = ConsumePrivateKey (provider);
338+ if (!key.IsValid ()) return {};
339+ // Construct garbage
340+ size_t garb_len = provider.ConsumeIntegralInRange <size_t >(0 , V2Transport::MAX_GARBAGE_LEN);
341+ std::vector<uint8_t > garb;
342+ if (garb_len <= 64 ) {
343+ // When the garbage length is up to 64 bytes, read it directly from the fuzzer input.
344+ garb = provider.ConsumeBytes <uint8_t >(garb_len);
345+ garb.resize (garb_len);
346+ } else {
347+ // If it's longer, generate it from the RNG. This avoids having large amounts of
348+ // (hopefully) irrelevant data needing to be stored in the fuzzer data.
349+ for (auto & v : garb) v = uint8_t (rng ());
350+ }
351+ // Retrieve entropy
352+ auto ent = provider.ConsumeBytes <std::byte>(32 );
353+ ent.resize (32 );
354+ // Use as entropy SHA256(ent || garbage). This prevents a situation where the fuzzer manages to
355+ // include the garbage terminator (which is a function of both ellswift keys) in the garbage.
356+ // This is extremely unlikely (~2^-116) with random keys/garbage, but the fuzzer can choose
357+ // both non-randomly and dependently. Since the entropy is hashed anyway inside the ellswift
358+ // computation, no coverage should be lost by using a hash as entropy, and it removes the
359+ // possibility of garbage that happens to contain what is effectively a hash of the keys.
360+ CSHA256 ().Write (UCharCast (ent.data ()), ent.size ())
361+ .Write (garb.data (), garb.size ())
362+ .Finalize (UCharCast (ent.data ()));
363+
364+ return std::make_unique<V2Transport>(nodeid, initiator, SER_NETWORK, INIT_PROTO_VERSION, key, ent, garb);
365+ }
366+
318367} // namespace
319368
320369FUZZ_TARGET_INIT (p2p_transport_bidirectional, initialize_p2p_transport_serialization)
@@ -327,3 +376,25 @@ FUZZ_TARGET_INIT(p2p_transport_bidirectional, initialize_p2p_transport_serializa
327376 if (!t1 || !t2) return ;
328377 SimulationTest (*t1, *t2, rng, provider);
329378}
379+
380+ FUZZ_TARGET_INIT (p2p_transport_bidirectional_v2, initialize_p2p_transport_serialization)
381+ {
382+ // Test with two V2 transports talking to each other.
383+ FuzzedDataProvider provider{buffer.data (), buffer.size ()};
384+ XoRoShiRo128PlusPlus rng (provider.ConsumeIntegral <uint64_t >());
385+ auto t1 = MakeV2Transport (NodeId{0 }, true , rng, provider);
386+ auto t2 = MakeV2Transport (NodeId{1 }, false , rng, provider);
387+ if (!t1 || !t2) return ;
388+ SimulationTest (*t1, *t2, rng, provider);
389+ }
390+
391+ FUZZ_TARGET_INIT (p2p_transport_bidirectional_v1v2, initialize_p2p_transport_serialization)
392+ {
393+ // Test with a V1 initiator talking to a V2 responder.
394+ FuzzedDataProvider provider{buffer.data (), buffer.size ()};
395+ XoRoShiRo128PlusPlus rng (provider.ConsumeIntegral <uint64_t >());
396+ auto t1 = MakeV1Transport (NodeId{0 });
397+ auto t2 = MakeV2Transport (NodeId{1 }, false , rng, provider);
398+ if (!t1 || !t2) return ;
399+ SimulationTest (*t1, *t2, rng, provider);
400+ }
0 commit comments