Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsNo conflicts as of last run. |
0d56e4d to
ffb2f0c
Compare
90291f8 to
a77b9cc
Compare
a77b9cc to
aaa3af2
Compare
1d0a9fb to
d539d73
Compare
instagibbs
left a comment
There was a problem hiding this comment.
light review, comparing implementation with the BIP, did not review the tests
|
ACK 15ea0ce587a334b647119fe65de2b305720c3eba |
vasild
left a comment
There was a problem hiding this comment.
ACK 15ea0ce587a334b647119fe65de2b305720c3eba
Coverage report for modified lines by this PR and not covered by tests:
./src/test/test_bitcoin
./src/qt/test/test_bitcoin-qt
./test/functional/test_runner.py
FUZZ=p2p_transport_bidirectional_v2 ./src/test/fuzz/fuzz -max_total_time=1800 /tmp/empty
FUZZ=p2p_transport_bidirectional_v1v2 ./src/test/fuzz/fuzz -max_total_time=1800 /tmp/empty
Before this commit, there are only two possibly outcomes for the "more" prediction
in Transport::GetBytesToSend():
* true: the transport itself has more to send, so the answer is certainly yes.
* false: the transport has nothing further to send, but if vSendMsg has more message(s)
left, that still will result in more wire bytes after the next
SetMessageToSend().
For the BIP324 v2 transport, there will arguably be a third state:
* definitely not: the transport has nothing further to send, but even if vSendMsg has
more messages left, they can't be sent (right now). This happens
before the handshake is complete.
To implement this, we move the entire decision logic to the Transport, by adding a
boolean to GetBytesToSend(), called have_next_message, which informs the transport
whether more messages are available. The return values are still true and false, but
they mean "definitely yes" and "definitely no", rather than "yes" and "maybe".
This introduces a V2Transport with a basic subset of BIP324 functionality: * no ability to send garbage (but receiving is supported) * no ability to send decoy packets (but receiving them is supported) * no support for short message id encoding (neither encoding or decoding) * no waiting until 12 non-V1 bytes have been received * (and thus) no detection of V1 connections on the responder side (on the sender side, detecting V1 is not supported either, but that needs to be dealt with at a higher layer, by reconnecting)
theStack
left a comment
There was a problem hiding this comment.
ACK 15ea0ce587a334b647119fe65de2b305720c3eba
Left some nitty follow-up material below.
src/net.cpp
Outdated
There was a problem hiding this comment.
small nit:
| // Transition the sender to AWAITING_KEY state (if not already). | |
| // Transition the sender to AWAITING_KEY state. |
To my understanding, no-op state transitions are not possible anymore (otherwise, one of the assertions in SetSendState would fail), and we could only hit this path if the sender state is MAYBE_V1.
There was a problem hiding this comment.
Agree. Will address if I push again.
|
Relevant changes since 15ea0ce587a334b647119fe65de2b305720c3eba (ignoring rebase): --- a/src/net.cpp
+++ b/src/net.cpp
@@ -919,7 +920,7 @@ namespace {
* Only message types that are actually implemented in this codebase need to be listed, as other
* messages get ignored anyway - whether we know how to decode them or not.
*/
-const std::string V2_MESSAGE_IDS[] = {
+const std::array<std::string, 33> V2_MESSAGE_IDS = {
"", // 12 bytes follow encoding the message type like in V1
NetMsgType::ADDR,
NetMsgType::BLOCK,
@@ -956,8 +957,6 @@ const std::string V2_MESSAGE_IDS[] = {
""
};
-static_assert(std::size(V2_MESSAGE_IDS) == 33);
-
class V2MessageMap
{
std::unordered_map<std::string, uint8_t> m_map;
--- a/src/test/fuzz/p2p_transport_serialization.cpp
+++ b/src/test/fuzz/p2p_transport_serialization.cpp
@@ -339,10 +339,7 @@ template<typename RNG>
std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& rng, FuzzedDataProvider& provider)
{
// Retrieve key
- auto key_data = provider.ConsumeBytes<unsigned char>(32);
- key_data.resize(32);
- CKey key;
- key.Set(key_data.begin(), key_data.end(), true);
+ auto key = ConsumePrivateKey(provider);
if (!key.IsValid()) return {};
// Construct garbage
size_t garb_len = provider.ConsumeIntegralInRange<size_t>(0, V2Transport::MAX_GARBAGE_LEN); |
There was a problem hiding this comment.
Concept ACK.
Overall this looks pretty solid.
Commit c3fad1f looks good; there's no ideal way to do this, but at least it's now pretty well documented.
The state machine looks good too.
Otherwise I only shallowly reviewed up to db9888f, and without looking at test and fuzz code.
To better ensure decoy packages work, maybe you could add a (debug) rpc method to send decoy package to a given peer? Though that can wait for a followup.
| // to MAX_RESERVE_AHEAD above the largest allowed message contents size, and to | ||
| // MAX_RESERVE_AHEAD more than they've actually sent us. | ||
| size_t alloc_add = std::min(max_read, msg_bytes.size() + MAX_RESERVE_AHEAD); | ||
| m_recv_buffer.reserve(m_recv_buffer.size() + alloc_add); |
There was a problem hiding this comment.
297c888: since the code worked before this commit, what happens we don't call reserve? Does that result in de worst case where the operating system allocates one byte at a time?
There was a problem hiding this comment.
Most std::vector implementations double their memory usage whenever they don't have enough, I believe. So without this code, you'd actually get fewer reallocations for large messages actually, but also more easily wasted (allocated but unused) memory.
That's not possible in this PR, as |
src/test/net_tests.cpp
Outdated
There was a problem hiding this comment.
typo: either "ones" or "scenarios"
| // Only wipe the buffer when everything is sent in the READY state. In the AWAITING_KEY state | ||
| // we still need the garbage that's in the send buffer to construct the garbage authentication | ||
| // packet. | ||
| if (m_send_state == SendState::READY && m_send_pos == m_send_buffer.size()) { |
There was a problem hiding this comment.
While this shortcut works, I wonder if it would be conceptually clearer to not have this special case and instead save the garbage in a m_send_garbage vector, similar to the existing m_recv_garbage on the receive side.
There was a problem hiding this comment.
I'll do this if I make further changes to the PR, or as a follow-up. It would also simplify something else: getting rid of the "don't send in MABE_V1 state" exception, by only putting stuff in the send buffer when leaving that state.
|
ACK db9888f |
|
Outstanding/followup comments: |
This is part of #27634.
This implements the BIP324 v2 transport (which implements all of what the BIP calls transport layer and application layer), though in a non-exposed way. It is tested through an extensive fuzz test, which verifies that v2 transports can talk to v2 transports, and v1 transports can talk to v2 transports, and a unit test that exercises a number of unusual scenarios. The transport is functionally complete, including:
getpeerinfooutput (for manual comparison).Things that are not included, left for future PRs, are:
NODE_P2P_V2service flag.