Skip to content

Commit f982516

Browse files
committed
merge bitcoin#28196: BIP324 connection support
1 parent 3087275 commit f982516

File tree

8 files changed

+1559
-61
lines changed

8 files changed

+1559
-61
lines changed

src/net.cpp

Lines changed: 693 additions & 20 deletions
Large diffs are not rendered by default.

src/net.h

Lines changed: 248 additions & 19 deletions
Large diffs are not rendered by default.

src/pubkey.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
258258
return true;
259259
}
260260

261+
EllSwiftPubKey::EllSwiftPubKey(Span<const std::byte> ellswift) noexcept
262+
{
263+
assert(ellswift.size() == SIZE);
264+
std::copy(ellswift.begin(), ellswift.end(), m_pubkey.begin());
265+
}
266+
261267
CPubKey EllSwiftPubKey::Decode() const
262268
{
263269
secp256k1_pubkey pubkey;

src/pubkey.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,7 @@ struct EllSwiftPubKey
217217
EllSwiftPubKey() noexcept = default;
218218

219219
/** Construct a new ellswift public key from a given serialization. */
220-
EllSwiftPubKey(const std::array<std::byte, SIZE>& ellswift) :
221-
m_pubkey(ellswift) {}
220+
EllSwiftPubKey(Span<const std::byte> ellswift) noexcept;
222221

223222
/** Decode to normal compressed CPubKey (for debugging purposes). */
224223
CPubKey Decode() const;

src/test/bip324_tests.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,8 @@ void TestBIP324PacketVector(
3737
{
3838
// Convert input from hex to char/byte vectors/arrays.
3939
const auto in_priv_ours = ParseHex(in_priv_ours_hex);
40-
const auto in_ellswift_ours_vec = ParseHex<std::byte>(in_ellswift_ours_hex);
41-
assert(in_ellswift_ours_vec.size() == 64);
42-
std::array<std::byte, 64> in_ellswift_ours;
43-
std::copy(in_ellswift_ours_vec.begin(), in_ellswift_ours_vec.end(), in_ellswift_ours.begin());
44-
const auto in_ellswift_theirs_vec = ParseHex<std::byte>(in_ellswift_theirs_hex);
45-
assert(in_ellswift_theirs_vec.size() == 64);
46-
std::array<std::byte, 64> in_ellswift_theirs;
47-
std::copy(in_ellswift_theirs_vec.begin(), in_ellswift_theirs_vec.end(), in_ellswift_theirs.begin());
40+
const auto in_ellswift_ours = ParseHex<std::byte>(in_ellswift_ours_hex);
41+
const auto in_ellswift_theirs = ParseHex<std::byte>(in_ellswift_theirs_hex);
4842
const auto in_contents = ParseHex<std::byte>(in_contents_hex);
4943
const auto in_aad = ParseHex<std::byte>(in_aad_hex);
5044
const auto mid_send_garbage = ParseHex<std::byte>(mid_send_garbage_hex);

src/test/fuzz/p2p_transport_serialization.cpp

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ std::vector<std::string> g_all_messages;
2222

2323
void 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

320369
FUZZ_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

Comments
 (0)