p2p: Move all header verification into the network layer, extend logging #19107

pull troygiorshev wants to merge 6 commits into bitcoin:master from troygiorshev:p2p-refactor-header changing 8 files +101 −111
  1. troygiorshev commented at 3:53 pm on May 29, 2020: contributor

    Inspired by #15206 and #15197, this PR moves all message header verification from the message processing layer and into the network/transport layer.

    In the previous PRs there is a change in behavior, where we would disconnect from peers upon a single failed checksum check. In various discussions there was concern over whether this was the right choice, and some expressed a desire to see how this would look if it was made to be a pure refactor.

    For more context, see https://bitcoincore.reviews/15206.html#l-81.

    This PR improves the separation between the p2p layers, helping improvements like BIP324 and #18989.

  2. DrahtBot added the label P2P on May 29, 2020
  3. DrahtBot added the label Tests on May 29, 2020
  4. MarcoFalke removed the label Tests on May 29, 2020
  5. troygiorshev force-pushed on May 29, 2020
  6. troygiorshev force-pushed on May 29, 2020
  7. DrahtBot commented at 4:29 pm on May 29, 2020: member

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #19998 (Net: Add CNode::ConnectedViaTor() by hebasto)
    • #19988 (Overhaul transaction request logic by sipa)
    • #19911 (net: guard vRecvGetData with cs_vRecv and orphan_work_set with g_cs_orphans by narula)
    • #19757 (net/net_processing: Convert net std::list buffers to std::forward_list by JeremyRubin)
    • #19509 (Per-Peer Message Logging by troygiorshev)
    • #18985 (bloom: use Span instead of std::vector for insert and contains [ZAP3] by jb55)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  8. troygiorshev force-pushed on May 29, 2020
  9. in src/net.cpp:723 in d5db6ad764 outdated
    723-    msg.m_valid_netmagic = (memcmp(hdr.pchMessageStart, message_start, CMessageHeader::MESSAGE_START_SIZE) == 0);
    724-    uint256 hash = GetMessageHash();
    725+    // Check the header
    726+    if (!hdr.IsValid()){
    727+        Reset();
    728+        return boost::none;
    


    jonasschnelli commented at 5:39 pm on May 29, 2020:
    use nullopt?

    troygiorshev commented at 6:37 pm on May 29, 2020:
    thanks, fixed
  10. troygiorshev force-pushed on May 29, 2020
  11. troygiorshev marked this as a draft on May 29, 2020
  12. jnewbery commented at 10:56 pm on May 29, 2020: member
    Concept ACK
  13. troygiorshev marked this as ready for review on Jun 1, 2020
  14. in src/net.cpp:598 in 598e6d3f2c outdated
    581+ *                          ready to be processed
    582+ * @return  True if the peer should stay connected,
    583+ *          False if the peer should be disconnected from.
    584+ */
    585 bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete)
    586 {
    


    rajarshimaitra commented at 9:23 am on June 1, 2020:
    It seems ReceiveMsgBytes returns two bool, (the function return and bool& complete), and both of them will have the same value at any execution (if I am not reading wrong). Is there any specific reason to make a function return the same boolean value twice?

    troygiorshev commented at 2:29 pm on June 1, 2020:
    There are a few cases where ReceiveMsgBytes will return true and complete will stay false. The most common is when Read finishes reading all of the bytes out of the buffer (so nBytes == 0), but we haven’t reached the end of a single message. (Remember the socket buffer is max 64KiB but a bitcoin message can be up to 4MB). In that case complete will still be false, but the method will return true, as we should stay connected to the peer to keep receiving the rest of the message. Another case, added by this PR, is when a message is found to be invalid when deserialized (and when there isn’t the end of a valid message in the buffer). Again complete will stay false, but the method will return true.

    rajarshimaitra commented at 9:04 am on June 2, 2020:
    Ya that makes sense. Thanks for clarifying.
  15. in src/net.cpp:687 in 598e6d3f2c outdated
    670+        return -1;
    671+    }
    672+
    673+    // Check start string, network magic
    674+    if (memcmp(hdr.pchMessageStart, chain_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
    675+        LogPrint(BCLog::NET, "HEADER ERROR - MESSAGESTART (%s, %u bytes), got %s\n", hdr.GetCommand(), hdr.nMessageSize + CMessageHeader::HEADER_SIZE, hdr.GetMessageStart());
    


    rajarshimaitra commented at 9:45 am on June 1, 2020:

    As it is a network magic error, the error code can be a little verbose for better understanding, something like this? Especially the command and size seems redundant.

    0        LogPrint(BCLog::NET, "HEADER ERROR - NETMAGIC : (%s, %u bytes), expected %s got %s\n", hdr.GetCommand(), hdr.nMessageSize + CMessageHeader::HEADER_SIZE chain_params.MessageStart(), hdr.GetMessageStart());
    

    troygiorshev commented at 2:37 pm on June 1, 2020:
    The command and size outputs are being kept to stay consistent with other error messages. Both the codebase and documentation aren’t consistent between the terms “Message Start String” and “Network Magic”, I’m preferring “Message Start String” here as I think it’s the older one, and neither are sufficiently descriptive out of context anyways.

    troygiorshev commented at 2:40 pm on June 1, 2020:
    I don’t think it’s necessary to print out the expected MessageStart in the debug message. The MessageStart is set when bitcoind starts up and doesn’t change after that. No one should be confused as to what the expected value is. Additionally I don’t print out the message’s MessageStart here, because I can’t find a situation where it would be relevant. I’m open to changing my stance on that if someone can propose when that would be useful.
  16. in src/net.cpp:693 in 598e6d3f2c outdated
    676         return -1;
    677     }
    678 
    679     // reject messages larger than MAX_SIZE or MAX_PROTOCOL_MESSAGE_LENGTH
    680     if (hdr.nMessageSize > MAX_SIZE || hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
    681+        LogPrint(BCLog::NET, "HEADER ERROR - SIZE (%s, %u bytes)\n", hdr.GetCommand(), hdr.nMessageSize + CMessageHeader::HEADER_SIZE);
    


    rajarshimaitra commented at 9:51 am on June 1, 2020:

    Should it be called HEADER ERROR given the error is due to the bigger message size? I think the error code can also be improved a little here specifying the message size is bigger the max permissible size. Something like

    0        LogPrint(BCLog::NET, "HEADER ERROR - MESSAGESIZE (%s, %u bytes), size %u bigger than max message size\n", hdr.GetCommand(), hdr.nMessageSize + CMessageHeader::HEADER_SIZE, hdr.nMessageSize);
    

    troygiorshev commented at 2:43 pm on June 1, 2020:
    The error is that the header is stating the message is larger than the maximum size. That’s orthogonal to whether the message is actually larger than the maximum size. (a priori we have no way of determining that). The error is in the construction of the header, and the error is brought up when checking the header.

    troygiorshev commented at 2:45 pm on June 1, 2020:
    I don’t think it’s necessary to print out the current size, as it’s already being done in the brackets. (That said, the error logs I’ve found aren’t consistent between printing the “message size” and the “raw message size” (including the header). I’ve chosen the conservative approach and not changed that, though in this case I think you’re right, it really should be the “message” or “payload” size.)

    jnewbery commented at 5:25 pm on June 1, 2020:
    These new logs don’t include the peer id, which makes them a lot less useful than the existing logs. Are you able to pass the failure reason up to CNode::ReceiveMsgBytes() so the peer id can be logged?

    troygiorshev commented at 2:00 pm on June 9, 2020:
    I’ve added a second LogPrint to ReceiveMsgBytes that will always print after an initial error. I think this is a better solution than having Read and Getmessage pass out a string. If we really want them on the same line, I could remove the trailing newline from the initial errors. (But that feels fragile)

    jnewbery commented at 3:33 pm on June 12, 2020:

    This seems good enough. I think ideally Read and Getmessages would pass out an enum indicating deserialization failure reason, and ReceiveMsgBytes would know how to log based on the enum.

    Definitely don’t remove the newline from the initial errors! bitcoind is multithreaded, so another thread could log between the two messages.


    troygiorshev commented at 2:41 am on June 16, 2020:
    I’ve found a way to consolidate these logs
  17. in src/net.cpp:727 in 598e6d3f2c outdated
    712@@ -692,29 +713,35 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
    713     return data_hash;
    714 }
    715 
    716-CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageStartChars& message_start, int64_t time) {
    717+Optional<CNetMessage> V1TransportDeserializer::GetMessage(int64_t time) {
    


    rajarshimaitra commented at 9:58 am on June 1, 2020:
    A comment here specifying GetMessage can fail due to invalid header might help here.
  18. in src/net.cpp:721 in 598e6d3f2c outdated
    721-    // store state about valid header, netmagic and checksum
    722-    msg.m_valid_header = hdr.IsValid(message_start);
    723-    msg.m_valid_netmagic = (memcmp(hdr.pchMessageStart, message_start, CMessageHeader::MESSAGE_START_SIZE) == 0);
    724-    uint256 hash = GetMessageHash();
    725+    // Check the header
    726+    if (!hdr.IsValid()){
    


    rajarshimaitra commented at 10:02 am on June 1, 2020:
    Seems like an error message here might be appropriate similar to the checksum check?

    troygiorshev commented at 2:52 pm on June 1, 2020:
    The logging is done in IsValid and in ReadHeader. I agree, I’d love to have it in GetMessage instead, just like the checksum check, but that would require IsValid to pass the information about why it failed up to GetMessage. Ideally all of the logging would be in ReceiveMsgBytes (and indeed I had a version of this PR where that was the case) but seeing the complexity that added to Read, ReadHeader, IsValid, and GetHeader I didn’t think it was worth it.

    jnewbery commented at 5:53 pm on June 1, 2020:
    Since the only check in IsValid() is that the command name in the header is a valid ascii string, and the only place it’s called is from here, perhaps that function should be moved to net.cpp and its name updated to IsCommandNameValid() or similar.

    troygiorshev commented at 1:57 pm on June 9, 2020:
    Chose IsCommandValid() as not to confuse the reader with the command name check done in ReceiveMsgBytes
  19. in src/net_processing.cpp:3830 in 598e6d3f2c outdated
    3541-    //  (4) message start
    3542-    //  (12) command
    3543-    //  (4) size
    3544-    //  (4) checksum
    3545-    //  (x) data
    3546-    //
    


    rajarshimaitra commented at 10:03 am on June 1, 2020:
    Why remove this comment block?

    troygiorshev commented at 2:53 pm on June 1, 2020:
    Net Processing is no longer aware of the format of the header. That’s all dealt with in net, so that net processing can just process the payload.

    jonatack commented at 9:19 am on June 18, 2020:
    3a9d277 Is there somewhere suitable this documentation can be moved to rather than be tossed out?
  20. in src/protocol.cpp:131 in 598e6d3f2c outdated
    127         {
    128             // Must be all zeros after the first zero
    129             for (; p1 < pchCommand + COMMAND_SIZE; p1++)
    130-                if (*p1 != 0)
    131+                if (*p1 != 0){
    132+                    LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes)\n", GetCommand(), nMessageSize + HEADER_SIZE);
    


    rajarshimaitra commented at 10:07 am on June 1, 2020:
    A little more info in error message might help?

    troygiorshev commented at 2:54 pm on June 1, 2020:
    I don’t see any more that we could add here.
  21. in src/protocol.cpp:136 in 598e6d3f2c outdated
    133                     return false;
    134+                }
    135         }
    136-        else if (*p1 < ' ' || *p1 > 0x7E)
    137+        else if (*p1 < ' ' || *p1 > 0x7E){
    138+            LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes)\n", GetCommand(), nMessageSize + HEADER_SIZE);
    


    rajarshimaitra commented at 10:07 am on June 1, 2020:
    Same comment for a little more info in the error message.

    troygiorshev commented at 2:54 pm on June 1, 2020:
    Same as above
  22. MarcoFalke commented at 10:28 am on June 1, 2020: member
     0Run p2p_transport_deserializer with args ['/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer', '-runs=1', '/home/travis/build/bitcoin/bitcoin/ci/scratch/qa-assets/fuzz_seed_corpus/p2p_transport_deserializer']INFO: Seed: 2855084377
     1
     2INFO: Loaded 1 modules   (412040 inline 8-bit counters): 412040 [0x560bb09480d8, 0x560bb09aca60), 
     3
     4INFO: Loaded 1 PC tables (412040 PCs): 412040 [0x560bb09aca60,0x560bb0ff62e0), 
     5
     6INFO:      128 files found in /home/travis/build/bitcoin/bitcoin/ci/scratch/qa-assets/fuzz_seed_corpus/p2p_transport_deserializer
     7
     8INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 1048576 bytes
     9
    10INFO: seed corpus: files: 128 min: 1b max: 1048576b total: 8594313b rss: 88Mb
    11
    12p2p_transport_deserializer: test/fuzz/p2p_transport_deserializer.cpp:35: void test_one_input(const std::vector<uint8_t> &): Assertion `result' failed.
    13
    14==31678== ERROR: libFuzzer: deadly signal
    15
    16    [#0](/bitcoin-bitcoin/0/) 0x560bae1f1881 in __sanitizer_print_stack_trace (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x198a881)
    17
    18    [#1](/bitcoin-bitcoin/1/) 0x560bae13c9d8 in fuzzer::PrintStackTrace() (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x18d59d8)
    19
    20    [#2](/bitcoin-bitcoin/2/) 0x560bae121b23 in fuzzer::Fuzzer::CrashCallback() (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x18bab23)
    21
    22    [#3](/bitcoin-bitcoin/3/) 0x7f38b4a473bf  (/lib/x86_64-linux-gnu/libpthread.so.0+0x153bf)
    23
    24    [#4](/bitcoin-bitcoin/4/) 0x7f38b46ae18a in raise (/lib/x86_64-linux-gnu/libc.so.6+0x4618a)
    25
    26    [#5](/bitcoin-bitcoin/5/) 0x7f38b468d858 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x25858)
    27
    28    [#6](/bitcoin-bitcoin/6/) 0x7f38b468d728  (/lib/x86_64-linux-gnu/libc.so.6+0x25728)
    29
    30    [#7](/bitcoin-bitcoin/7/) 0x7f38b469ef35 in __assert_fail (/lib/x86_64-linux-gnu/libc.so.6+0x36f35)
    31
    32    [#8](/bitcoin-bitcoin/8/) 0x560bae21b71f in test_one_input(std::vector<unsigned char, std::allocator<unsigned char> > const&) /home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer.cpp:35:13
    33
    34    [#9](/bitcoin-bitcoin/9/) 0x560baf33d9e1 in LLVMFuzzerTestOneInput /home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/fuzz.cpp:36:5
    35
    36    [#10](/bitcoin-bitcoin/10/) 0x560bae1231e1 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x18bc1e1)
    37
    38    [#11](/bitcoin-bitcoin/11/) 0x560bae122925 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x18bb925)
    39
    40    [#12](/bitcoin-bitcoin/12/) 0x560bae125247 in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x18be247)
    41
    42    [#13](/bitcoin-bitcoin/13/) 0x560bae1255a9 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x18be5a9)
    43
    44    [#14](/bitcoin-bitcoin/14/) 0x560bae11427e in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x18ad27e)
    45
    46    [#15](/bitcoin-bitcoin/15/) 0x560bae13d0c2 in main (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x18d60c2)
    47
    48    [#16](/bitcoin-bitcoin/16/) 0x7f38b468f0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    49
    50    [#17](/bitcoin-bitcoin/17/) 0x560bae0e901d in _start (/home/travis/build/bitcoin/bitcoin/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/p2p_transport_deserializer+0x188201d)
    51
    52NOTE: libFuzzer has rudimentary signal handlers.
    53
    54      Combine libFuzzer with AddressSanitizer or similar for better crash reports.
    55
    56SUMMARY: libFuzzer: deadly signal
    57
    58MS: 0 ; base unit: 0000000000000000000000000000000000000000
    59
    600xfa,0xbf,0xb5,0xda,0x40,0x6e,0x6e,0x6e,0x6e,0x7f,0x6f,0x3,0x34,0x64,0x6e,0x0,0x0,0x0,0x0,0x0,0x28,0xf6,0x6e,0x6e,
    61
    62\xfa\xbf\xb5\xda@nnnn\x7fo\x034dn\x00\x00\x00\x00\x00(\xf6nn
    63
    64artifact_prefix='./'; Test unit written to ./crash-fc30b01e02b9667a2f09968f8a88c869ab056464
    65
    66Base64: +r+12kBubm5uf28DNGRuAAAAAAAo9m5u
    
  23. in test/functional/p2p_invalid_messages.py:146 in 598e6d3f2c outdated
    141+            # If we take 77 bytes, then there are 101 - 77 = 24 left
    142+            # That's exactly the size of a header
    143+            # So, bitcoind deserializes the header and rejects it for some other reason
    144+            # But, if we take 78 bytes, then there are 101 - 78 = 23 left
    145+            # That's not enough to fill a header, so, what happens?
    146+
    


    rajarshimaitra commented at 10:30 am on June 1, 2020:
    This seems like a very interesting observation, especially for new reviewers. Is it possible to combine the explanation with the question below and elucidate it more clearly? It took me some time to figure out what these statements were referring to.

    troygiorshev commented at 3:24 pm on June 4, 2020:
    Good point. This PR is getting a little crowded, I’m going to move the test changes to a new one and I’ll clear things up there.
  24. rajarshimaitra commented at 10:39 am on June 1, 2020: contributor
    Concept ACK. Verified unit and functional tests are passing. Thanks for adding the doxy comment on ReceiveMsgBytes, that cleared some of my earlier doubts. I found the commits to be a little difficult to parse as the same function is being changed in different commits. This can be good for demonstrating the stepwise changes, but in this case, it resulted in reviewing some lines which later got removed in later commits. So maybe a little consolidation of commits can help future review. Overall strong concept ACK as this refactor has been discussed in review club and generally accepted as a good idea except the connection dropping behavior. Below are few comments on the code which shows some probable scope of improvement.
  25. in src/protocol.h:40 in 598e6d3f2c outdated
    35@@ -36,15 +36,16 @@ class CMessageHeader
    36     static constexpr size_t HEADER_SIZE = MESSAGE_START_SIZE + COMMAND_SIZE + MESSAGE_SIZE_SIZE + CHECKSUM_SIZE;
    37     typedef unsigned char MessageStartChars[MESSAGE_START_SIZE];
    38 
    39-    explicit CMessageHeader(const MessageStartChars& pchMessageStartIn);
    40+    explicit CMessageHeader();
    


    jnewbery commented at 5:07 pm on June 1, 2020:
    This change to the CMessageHeader ctor should be pulled out into its own commit, with the commit message explaining why it’s ok to initialize a CMessageHeader without the start bytes.

    troygiorshev commented at 1:54 pm on June 9, 2020:
    Done
  26. in src/net.h:678 in 598e6d3f2c outdated
    674@@ -675,7 +675,7 @@ class V1TransportDeserializer final : public TransportDeserializer
    675 
    676 public:
    677 
    678-    V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) {
    679+    V1TransportDeserializer(const CChainParams& chain_params_in, int nTypeIn, int nVersionIn) : chain_params(chain_params_in), hdrbuf(nTypeIn, nVersionIn), hdr(), vRecv(nTypeIn, nVersionIn) {
    


    jnewbery commented at 5:31 pm on June 1, 2020:
    No need for hdr() in the initializer list. It’ll use the default construction anyway if you leave it out.

    troygiorshev commented at 1:54 pm on June 9, 2020:
    Done
  27. in src/net.cpp:713 in 156fc71215 outdated
    712 
    713     // store state about valid header, netmagic and checksum
    714-    msg.m_valid_header = hdr.IsValid(message_start);
    715-    msg.m_valid_netmagic = (memcmp(hdr.pchMessageStart, message_start, CMessageHeader::MESSAGE_START_SIZE) == 0);
    716-    uint256 hash = GetMessageHash();
    717+    msg.m_valid_header = hdr.IsValid(Params().MessageStart());
    


    jnewbery commented at 5:33 pm on June 1, 2020:
    in commit Give V1TransportDeserializer a ref to CChainParams member, and use it in GetMessage, you should switch out the calls to the global Params for the local chain_params

    troygiorshev commented at 1:55 pm on June 9, 2020:
    Missed this one, good eye. Done
  28. in src/net.h:651 in 598e6d3f2c outdated
    647 };
    648 
    649 class V1TransportDeserializer final : public TransportDeserializer
    650 {
    651 private:
    652+    const CChainParams& chain_params;
    


    jnewbery commented at 5:34 pm on June 1, 2020:
    Member variables in new code should be named m_name. See https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#coding-style-c for our style guide.

    jnewbery commented at 5:36 pm on June 1, 2020:
    What’s the reason for storing a reference to the chain params, rather than the message start bytes themselves, which is all we actually need?

    troygiorshev commented at 9:47 pm on June 8, 2020:
    I don’t have a great justification for this. The chain_params is a singleton object and it somehow feels better to have a reference to a singleton object than a reference to a member of it. Maybe one day V1TransportDeserializer will need another parameter, if even only for a test. If anyone can propose even a weak reason why this is a bad idea than I’m happy to change it.

    troygiorshev commented at 1:55 pm on June 9, 2020:
    Done
  29. in src/protocol.h:48 in 0ad9dabdc7 outdated
    43@@ -44,7 +44,8 @@ class CMessageHeader
    44     CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn);
    45 
    46     std::string GetCommand() const;
    47-    bool IsValid(const MessageStartChars& messageStart) const;
    48+    std::string GetMessageStart() const;
    49+    bool IsValid() const;
    


    jnewbery commented at 5:41 pm on June 1, 2020:
    I don’t think commit Create getter CMessageHeader::GetMessageStart will build. You’re changing the IsValid() signature in this commit, but don’t change the callers until a later commit.

    troygiorshev commented at 1:55 pm on June 9, 2020:
    Fixed
  30. in test/functional/p2p_invalid_messages.py:127 in 598e6d3f2c outdated
    123@@ -124,7 +124,8 @@ def run_test(self):
    124         #
    125         # Send messages with an incorrect data size in the header.
    126         #
    127-        actual_size = 100
    128+        # These all fail with a checksum error!  That's not what this test is supposed to test.
    


    jnewbery commented at 6:08 pm on June 1, 2020:
    Did you mean to commit all of this? It seems unfinished.

    troygiorshev commented at 1:36 pm on June 5, 2020:
    Test changes moved to a separate PR
  31. in src/test/fuzz/deserialize.cpp:192 in 598e6d3f2c outdated
    188@@ -189,10 +189,9 @@ void test_one_input(const std::vector<uint8_t>& buffer)
    189         DeserializeFromFuzzingInput(buffer, s);
    190         AssertEqualAfterSerializeDeserialize(s);
    191 #elif MESSAGEHEADER_DESERIALIZE
    192-        const CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00};
    193-        CMessageHeader mh(pchMessageStart);
    194+        CMessageHeader mh;
    


    jnewbery commented at 6:10 pm on June 1, 2020:
    This commit should be squashed with the code changes.

    troygiorshev commented at 1:57 pm on June 9, 2020:
    Done
  32. jnewbery commented at 6:14 pm on June 1, 2020: member

    Great stuff so far @troygiorshev !

    I try to format my commit logs as:

    0[component]: short summary (50 chars or less if possible)
    1
    2Reason for the change (why not what or how) (wrapped at 72 or 80 columns)
    

    See https://chris.beams.io/posts/git-commit/ for more tips on writing great commit logs.

  33. DrahtBot added the label Needs rebase on Jun 4, 2020
  34. troygiorshev force-pushed on Jun 9, 2020
  35. troygiorshev commented at 3:45 pm on June 9, 2020: contributor
    Rebased, fixed failing fuzz tests. Removed some of the unneeded test changes, they’re now in #19177. Overall the commits are cleaned up. They are all atomic now, with full commit messages. I’ve also implemented many of the suggestions, thanks again to everyone for the review so far!
  36. DrahtBot removed the label Needs rebase on Jun 9, 2020
  37. in src/net.cpp:603 in d8cbdc4adf outdated
    598@@ -589,14 +599,22 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
    599     while (nBytes > 0) {
    600         // absorb network data
    601         int handled = m_deserializer->Read(pch, nBytes);
    602-        if (handled < 0) return false;
    603+        if (handled < 0){
    604+            LogPrint(BCLog::NET, "HEADER ERROR peer=%d\n", id);
    


    rajarshimaitra commented at 3:09 pm on June 11, 2020:
    I understand you are trying to repeat the errors here after the initial error from the actual function call, but can this make the errors clumsy? Especially when errors will occur from multiple peers simultaneously. It’s expected that an entire error will be in a single line for any context, instead here the error with peer id is printing on a new line.

    troygiorshev commented at 2:42 am on June 16, 2020:
    I agree. I’ve found a way to consolidate these logs, let me know what you think
  38. in src/net.cpp:616 in d8cbdc4adf outdated
    607 
    608         pch += handled;
    609         nBytes -= handled;
    610 
    611         if (m_deserializer->Complete()) {
    612             // decompose a transport agnostic CNetMessage from the deserializer
    


    rajarshimaitra commented at 3:17 pm on June 11, 2020:
    Indication of checksum check already happening inside GetMessage() in this comment might be helpful?
  39. in src/net.h:733 in d8cbdc4adf outdated
    640@@ -642,13 +641,14 @@ class TransportDeserializer {
    641     // read and deserialize data
    642     virtual int Read(const char *data, unsigned int bytes) = 0;
    643     // decomposes a message from the context
    


    rajarshimaitra commented at 3:42 pm on June 11, 2020:
    Nit: Mentioning it “optionally” decomposes the message might make the comment complete. Feel free to ignore.
  40. in src/net.cpp:609 in d8cbdc4adf outdated
    598@@ -589,14 +599,22 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
    599     while (nBytes > 0) {
    600         // absorb network data
    601         int handled = m_deserializer->Read(pch, nBytes);
    602-        if (handled < 0) return false;
    603+        if (handled < 0){
    604+            LogPrint(BCLog::NET, "HEADER ERROR peer=%d\n", id);
    605+            return false;
    


    rajarshimaitra commented at 3:58 pm on June 11, 2020:
    Referring back to the review club discussion. Previously we were dropping connections here because of any header error. Going by the logic of not dropping the connection due to wrong checksum (naughty firewall) cant those same things happen in other parts of the header too? Orthogonal to this PR, but curious about the rationale here.

    jnewbery commented at 5:33 pm on June 17, 2020:
    This PR doesn’t change behaviour at all, so I think questions about whether or not to drop connections for different errors is off-topic and should be saved for PRs that change behaviour!
  41. in src/protocol.cpp:118 in d8cbdc4adf outdated
    117+std::string CMessageHeader::GetMessageStart() const
    118 {
    119-    // Check start string
    120-    if (memcmp(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE) != 0)
    121-        return false;
    122+    return std::string(pchMessageStart, pchMessageStart + strnlen(pchMessageStart, MESSAGE_START_SIZE));
    


    rajarshimaitra commented at 5:03 pm on June 11, 2020:
    Nit: Keeping in the spirit of the original comment here, maybe add a comment saying returning staring string?

    troygiorshev commented at 0:38 am on June 16, 2020:
    The diff algorithm is confused, there wasn’t ever a comment here. This check is now in ReadHeader, where I’ve preserved the comment.
  42. rajarshimaitra commented at 5:53 pm on June 11, 2020: contributor
    The PR looks better and more well organized. Compiled without issue, all tests passing. Strong concept ACK on refactoring. The approach seems fine to me. The followings are a few non-blocking comments and nits.
  43. in src/net.cpp:750 in d8cbdc4adf outdated
    752 
    753-    msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0);
    754-    if (!msg.m_valid_checksum) {
    755+    // Check checksum
    756+    bool valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0);
    757+    if (!valid_checksum) {
    


    jnewbery commented at 3:25 pm on June 12, 2020:

    no need for this use-once local variable. Combine the lines:

    0    if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
    1        // Invalid checksum. Log and exit
    2        ...
    

    troygiorshev commented at 2:42 am on June 16, 2020:
    Done
  44. in src/net.cpp:758 in d8cbdc4adf outdated
    757+    if (!valid_checksum) {
    758         LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n",
    759                  SanitizeString(msg.m_command), msg.m_message_size,
    760                  HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE),
    761                  HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE));
    762+        Reset();
    


    jnewbery commented at 3:30 pm on June 12, 2020:

    It’s a slight smell that you’re calling Reset() for both exit branches here. I think this would be safer if this branch just set msg to be a nullopt and an else branch set it to be the Optional<CNetMessage>.

    I’d also prefer to avoid the implicit conversion from CNetMessage to Optional<CNetMessage> in the return statement below.


    troygiorshev commented at 2:43 am on June 16, 2020:
    Consolidated all of the checks to the bottom

    MarcoFalke commented at 1:51 pm on June 19, 2020:

    in commit 5ad7ce1:

    I still see Reset in both exit branches. Has this been addressed?

  45. in src/net.cpp:614 in d8cbdc4adf outdated
    611         if (m_deserializer->Complete()) {
    612             // decompose a transport agnostic CNetMessage from the deserializer
    613-            CNetMessage msg = m_deserializer->GetMessage(Params().MessageStart(), nTimeMicros);
    614+            Optional<CNetMessage> result = m_deserializer->GetMessage(nTimeMicros);
    615+            if(!result){
    616+                LogPrint(BCLog::NET, "HEADER ERROR peer=%d\n", id);
    


    jnewbery commented at 3:34 pm on June 12, 2020:
    I think a comment here to say “Message deserialization failed. Drop the message and log the failure, but don’t disconnect the peer” would be useful

    troygiorshev commented at 2:44 am on June 16, 2020:
    Done
  46. in src/net.h:13 in d8cbdc4adf outdated
     9@@ -10,12 +10,14 @@
    10 #include <addrman.h>
    11 #include <amount.h>
    12 #include <bloom.h>
    13+#include <chainparams.h>
    


    jnewbery commented at 3:44 pm on June 12, 2020:
    You can remove this include from net.cpp now that it’s included in the header file.

    troygiorshev commented at 2:44 am on June 16, 2020:
    Good catch, done
  47. in src/protocol.cpp:130 in d8cbdc4adf outdated
    130         if (*p1 == 0)
    131         {
    132             // Must be all zeros after the first zero
    133             for (; p1 < pchCommand + COMMAND_SIZE; p1++)
    134-                if (*p1 != 0)
    135+                if (*p1 != 0){
    


    jnewbery commented at 3:50 pm on June 12, 2020:

    nit: space between ) and { please.

    Since you’re touching this line, can you also give the surrounding for loop braces to comply with current code style guide?


    troygiorshev commented at 2:44 am on June 16, 2020:
    Gladly! done
  48. in src/protocol.cpp:135 in d8cbdc4adf outdated
    136+                    LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes)\n", GetCommand(), nMessageSize + HEADER_SIZE);
    137                     return false;
    138+                }
    139         }
    140-        else if (*p1 < ' ' || *p1 > 0x7E)
    141+        else if (*p1 < ' ' || *p1 > 0x7E){
    


    jnewbery commented at 3:50 pm on June 12, 2020:
    nit: space between ) and { please.
  49. in src/protocol.cpp:131 in d8cbdc4adf outdated
    131         {
    132             // Must be all zeros after the first zero
    133             for (; p1 < pchCommand + COMMAND_SIZE; p1++)
    134-                if (*p1 != 0)
    135+                if (*p1 != 0){
    136+                    LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes)\n", GetCommand(), nMessageSize + HEADER_SIZE);
    


    jnewbery commented at 3:53 pm on June 12, 2020:
    Is it possible to move this logging up into V1TransportDeserializer::GetMessage() so you don’t need to duplicate it?

    troygiorshev commented at 2:45 am on June 16, 2020:
    I agree, done. Logging is now exclusively in GetMessage and ReadHeader
  50. jnewbery commented at 3:55 pm on June 12, 2020: member

    Looking really good. A few comments inline, and some comments on the commit logs:

    • typo in commit message: “This commit removes the single-paramter” -> “single-parameter”
    • typo in commit message: “Give V1TransportDeserializer CChainParams& Memeber” -> “member”

    I think this should be merged after #19177, which cleans up the tests.

  51. MarcoFalke referenced this in commit f154071ec8 on Jun 12, 2020
  52. DrahtBot added the label Needs rebase on Jun 12, 2020
  53. sidhujag referenced this in commit d5007c520a on Jun 13, 2020
  54. troygiorshev force-pushed on Jun 16, 2020
  55. troygiorshev commented at 2:47 am on June 16, 2020: contributor

    Rebased and made suggested changes. When making the new log messages, I picked the “wrong” message size. I chose the size including the header whereas I should have chosen the payload size to stay consistent with other logging. That’s now fixed. (Search for (%s, %u bytes))

    Additionally I’ve found a solution to the logging issue. Given that V1TransportDeserializer is always a member of a peer, I’ve given it a new node_id member. Now all of the logging is done in methods of V1TransportDeserializer. This should minimize future maintenance (other solutions would have had logging split through multiple methods), and puts the logging on one line.

  56. DrahtBot removed the label Needs rebase on Jun 16, 2020
  57. in src/test/fuzz/p2p_transport_deserializer.cpp:36 in f070a97fe5 outdated
    41-                assert(msg.m_valid_netmagic);
    42-            }
    43-            if (!msg.m_valid_netmagic) {
    44-                assert(!msg.m_valid_header);
    45+            auto result = deserializer.GetMessage(m_time);
    46+            if (result){
    


    jonatack commented at 8:27 am on June 16, 2020:
    0            if (result) {
    
  58. in src/net.cpp:612 in f070a97fe5 outdated
    609 
    610         if (m_deserializer->Complete()) {
    611             // decompose a transport agnostic CNetMessage from the deserializer
    612-            CNetMessage msg = m_deserializer->GetMessage(Params().MessageStart(), nTimeMicros);
    613+            Optional<CNetMessage> result = m_deserializer->GetMessage(nTimeMicros);
    614+            if(!result){
    


    jonatack commented at 8:28 am on June 16, 2020:
    0            if (!result) {
    
  59. in src/net.cpp:601 in f070a97fe5 outdated
    597@@ -589,14 +598,22 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
    598     while (nBytes > 0) {
    599         // absorb network data
    600         int handled = m_deserializer->Read(pch, nBytes);
    601-        if (handled < 0) return false;
    602+        if (handled < 0){
    


    jonatack commented at 8:29 am on June 16, 2020:
    0        if (handled < 0) {
    
  60. jonatack commented at 8:36 am on June 16, 2020: member

    Concept ACK, will review.

    Don’t hesitate to go through doc/developer-notes.md and run clang formatting on your code changes.

    I’ll admit that I’ve never dared to refactor critical code like this because of the warning in CONTRIBUTING.md. TBH I wish I’d paid less attention to it:

    Pull requests that refactor the code should not be made by new contributors. It requires a certain level of experience to know where the code belongs to and to understand the full ramification (including rebase effort of open pull requests).

  61. troygiorshev force-pushed on Jun 16, 2020
  62. troygiorshev commented at 1:15 pm on June 16, 2020: contributor
    Ran through clang-format
  63. in src/net.h:681 in ab5fa4fe92 outdated
    674@@ -674,8 +675,16 @@ class V1TransportDeserializer final : public TransportDeserializer
    675     }
    676 
    677 public:
    678+<<<<<<< HEAD
    679+    V1TransportDeserializer(const CChainParams& chain_params_in, int nTypeIn, int nVersionIn) : m_chain_params(chain_params_in), hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn)
    680+    {
    681+||||||| parent of f070a97fe5... Remove header checks out of net_processing
    


    fanquake commented at 1:16 pm on June 16, 2020:
    I think something has gone wrong here

    troygiorshev commented at 1:30 pm on June 16, 2020:
    oof, thanks

    troygiorshev commented at 1:36 pm on June 16, 2020:

    @jonatack the correct fix here is below but I’ll hold off on touching anything until you’re ready.

    0    V1TransportDeserializer(const CChainParams& chain_params_in, const NodeId node_id_in, int nTypeIn, int nVersionIn) : m_chain_params(chain_params_in), node_id(node_id_in), hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn)
    1    {
    

    jonatack commented at 5:52 pm on June 16, 2020:
    thanks @troygiorshev
  64. jonatack commented at 1:21 pm on June 16, 2020: member
    @troygiorshev reviewing, will have a few more suggestions if you want to wait before retouching. Also, avoid rebasing to master if you don’t need to.
  65. troygiorshev force-pushed on Jun 17, 2020
  66. troygiorshev commented at 5:12 pm on June 17, 2020: contributor
    I realized I had left this in a terribly broken state. Fixed now, just with clang-format (done correctly this time). Compare between f070a97fe5abc40f93f98e241dd81c5bb43f7de5 and 3a9d277fc0cb9247ec14350d43ecc9f7825aadd2. @jonatack I think you were reviewing on f070a97fe5abc40f93f98e241dd81c5bb43f7de5, feel free to continue there and I’ll integrate your suggestions.
  67. in src/net.cpp:586 in 3a9d277fc0 outdated
    577@@ -579,6 +578,16 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
    578 }
    579 #undef X
    580 
    581+/**
    582+ * Receive bytes from the buffer and deserialize them into messages.
    583+ *
    584+ * @param[in]   pch         Socket buffer
    585+ * @param[in]   nBytes      Size of the socket buffer
    586+ * @param[out]  complete    Set True if a message has been deserialized and is
    


    jonatack commented at 7:32 am on June 18, 2020:

    e0d316d nit: s/Set True/True|Set to true/

    Could squash this commit into one of the others.

  68. in src/net.cpp:611 in 3a9d277fc0 outdated
    608         nBytes -= handled;
    609 
    610         if (m_deserializer->Complete()) {
    611             // decompose a transport agnostic CNetMessage from the deserializer
    612-            CNetMessage msg = m_deserializer->GetMessage(Params().MessageStart(), nTimeMicros);
    613+            Optional<CNetMessage> result = m_deserializer->GetMessage(nTimeMicros);
    


    jonatack commented at 8:29 am on June 18, 2020:

    1596652 style nit, feel free to ignore

    0            Optional<CNetMessage> result{m_deserializer->GetMessage(nTimeMicros)};
    
  69. in src/protocol.cpp:130 in 3a9d277fc0 outdated
    136-                if (*p1 != 0)
    137+            for (; p1 < pchCommand + COMMAND_SIZE; ++p1) {
    138+                if (*p1 != 0) {
    139                     return false;
    140-        }
    141-        else if (*p1 < ' ' || *p1 > 0x7E)
    


    jonatack commented at 8:51 am on June 18, 2020:
    3a9d277f All this reformatting of the section seems ok but I’m not sure if it’s a good idea to change the formatting of critical code that’s not being directly changed.

    troygiorshev commented at 5:52 pm on June 18, 2020:
    This is done in response to #19107 (review)
  70. in test/functional/p2p_invalid_messages.py:76 in 3a9d277fc0 outdated
    72@@ -73,7 +73,7 @@ def test_checksum(self):
    73 
    74     def test_size(self):
    75         conn = self.nodes[0].add_p2p_connection(P2PDataStore())
    76-        with self.nodes[0].assert_debug_log(['']):
    77+        with self.nodes[0].assert_debug_log(['HEADER ERROR - SIZE']):
    


    jonatack commented at 9:15 am on June 18, 2020:

    3a9d277

    0        with self.nodes[0].assert_debug_log(['HEADER ERROR - SIZE (badmsg, 4000001 bytes)']):
    
  71. in test/functional/p2p_invalid_messages.py:54 in 3a9d277fc0 outdated
    50@@ -51,7 +51,7 @@ def run_test(self):
    51 
    52     def test_magic_bytes(self):
    53         conn = self.nodes[0].add_p2p_connection(P2PDataStore())
    54-        with self.nodes[0].assert_debug_log(['PROCESSMESSAGE: INVALID MESSAGESTART badmsg']):
    55+        with self.nodes[0].assert_debug_log(['HEADER ERROR - MESSAGESTART']):
    


    jonatack commented at 9:46 am on June 18, 2020:

    3a9d277

    0        with self.nodes[0].assert_debug_log(['HEADER ERROR - MESSAGESTART (badmsg, 2 bytes), got ffffffff']):
    
  72. in test/functional/p2p_invalid_messages.py:118 in 3a9d277fc0 outdated
    82@@ -83,9 +83,8 @@ def test_size(self):
    83 
    84     def test_msgtype(self):
    85         conn = self.nodes[0].add_p2p_connection(P2PDataStore())
    86-        with self.nodes[0].assert_debug_log(['PROCESSMESSAGE: ERRORS IN HEADER']):
    87+        with self.nodes[0].assert_debug_log(['HEADER ERROR - COMMAND']):
    


    jonatack commented at 9:49 am on June 18, 2020:

    3a9d277

    0        with self.nodes[0].assert_debug_log(['HEADER ERROR - COMMAND (bad, 2 bytes)']):
    

    troygiorshev commented at 1:52 am on June 19, 2020:
    I’m going to leave this one. The reason the command is wrong is not because it’s an unrecognized command, it’s because it isn’t validly constructed. It ends up being bad\x00sg\x00\x00\x00\x00\x00\x00 which doesn’t pass the “once you have a \x00 all remaining chars must be \x00” test. One day it might be nice to change all of these tests so that they send recognized commands, and then check for that separately.
  73. in src/net.cpp:616 in 3a9d277fc0 outdated
    613+            Optional<CNetMessage> result = m_deserializer->GetMessage(nTimeMicros);
    614+            if (!result) {
    615+                // Message deserialization failed.  Drop the message but don't disconnect the peer.
    616+                continue;
    617+            }
    618+            CNetMessage msg = *result;
    


    jonatack commented at 9:54 am on June 18, 2020:

    style nit, feel free to ignore

    0            CNetMessage msg{*result};
    
  74. jonatack commented at 9:58 am on June 18, 2020: member
    Concept ACK. First pass through the changes; a few comments below. Since this is critical code, I wonder if any changes not strictly related to moving header verification into the network layer could be in a different PR to ease review and minimise the chance of introducing a regression or vulnerability.
  75. in src/net.cpp:751 in 3a9d277fc0 outdated
    759+        LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d\n",
    760+            SanitizeString(msg.m_command), msg.m_message_size,
    761+            HexStr(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE),
    762+            HexStr(hdr.pchChecksum, hdr.pchChecksum + CMessageHeader::CHECKSUM_SIZE),
    763+            node_id);
    764+        Reset();
    


    jonatack commented at 10:02 am on June 18, 2020:

    5ad7ce1 and 3a9d277 I agree with @jnewbery here about the multiple Resets. Maybe something like this would be cleaner (tested example):

     0-    // Check checksum
     1-    // Always reset the network deserializer (prepare for the next message)
     2+    Optional<CNetMessage> result{};
     3     if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
     4         LogPrint(BCLog::NET, "CHECKSUM ERROR ... ;
     5-        Reset();
     6-        return nullopt;
     7-    }
     8-    // Check header command string
     9-    if (!hdr.IsCommandValid()) {
    10+    } else if (!hdr.IsCommandValid()) {
    11         LogPrint(BCLog::NET, "HEADER ERROR - COMMAND ... ;
    12-        Reset();
    13-        return nullopt;
    14+    } else {
    15+        result = msg;
    16     }
    17-
    18+    // Reset the network deserializer (prepare for the next message).
    19     Reset();
    20-    return msg;
    21+    return result;
    

    troygiorshev commented at 6:29 pm on June 18, 2020:

    Thanks for the tested example! I’m looking deeper into how Optional is implemented. It looks like we don’t have move semantics until before Boost 1.56.0. I’m worried about the performance hit if we’re copying a CNetMessage every time.

    If I don’t find anything important there, then I’ll use this suggestion for sure.


    MarcoFalke commented at 6:51 pm on June 18, 2020:
    IIRC return result; should always move if it is possible. All recent OS as well as the binaries on the website come with a version of boost greater than 1.56. I think we don’t care about reduced performance when someone compiles on a historic OS.

    MarcoFalke commented at 6:52 pm on June 18, 2020:
    See also commit ff9c671b11d40e5d0623eff3dd12e48cbaafb34e

    troygiorshev commented at 8:37 pm on June 18, 2020:
    Sounds good to me!
  76. in src/net.cpp:686 in 3a9d277fc0 outdated
    681+        return -1;
    682+    }
    683+
    684+    // Check start string, network magic
    685+    if (memcmp(hdr.pchMessageStart, m_chain_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
    686+        LogPrint(BCLog::NET, "HEADER ERROR - MESSAGESTART (%s, %u bytes), got %s, peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, HexStr(hdr.GetMessageStart()), node_id);
    


    jonatack commented at 10:21 am on June 18, 2020:
    0        LogPrint(BCLog::NET, "HEADER ERROR - MESSAGESTART (%s, %u bytes), received %s, peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, HexStr(hdr.GetMessageStart()), node_id);
    
  77. DrahtBot added the label Needs rebase on Jun 19, 2020
  78. in src/net.cpp:605 in 5ad7ce1102 outdated
    602-            CNetMessage msg = m_deserializer->GetMessage(Params().MessageStart(), nTimeMicros);
    603+            Optional<CNetMessage> result = m_deserializer->GetMessage(Params().MessageStart(), nTimeMicros);
    604+            if (!result) {
    605+                continue;
    606+            }
    607+            CNetMessage msg = *result;
    


    MarcoFalke commented at 1:48 pm on June 19, 2020:

    in commit 5ad7ce11027536fb6e48098db9659744514a7fc2:

    Why is this copy and alias of the message needed?

    • msg. occurs only twice, so I think simply replacing msg. with msg-> should be preferable to copying the whole message (or even adding a convenience alias (reference) to it).

    troygiorshev commented at 1:10 pm on June 22, 2020:
    In my mind this was the safe way to use an optional (immediately check the optional and resolve it to a real object). In this case though I’ve checked and what you’ve suggested both works and looks clear, so I’ll go with it.
  79. MarcoFalke commented at 1:52 pm on June 19, 2020: member
    Concept ACK. Only looked at the first commit, but I think I’ll start review after rebase.
  80. troygiorshev force-pushed on Jun 19, 2020
  81. troygiorshev commented at 2:59 pm on June 19, 2020: contributor
    Rebased. Changes coming soon.
  82. DrahtBot removed the label Needs rebase on Jun 19, 2020
  83. troygiorshev force-pushed on Jun 22, 2020
  84. troygiorshev commented at 1:27 pm on June 22, 2020: contributor
    Made suggested changes. (Note Optional justification here). Sorry for the mess with the rebasing. Everything is nice and ready for review
  85. in src/net.cpp:754 in e62961887a outdated
    762+            node_id);
    763+    } else if (!hdr.IsCommandValid()) {
    764+        LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes), peer=%d\n",
    765+            hdr.GetCommand(), msg.m_message_size, node_id);
    766+    } else {
    767+        result = msg;
    


    jnewbery commented at 2:54 pm on June 22, 2020:

    Since msg is not an rvalue, I think this will always do a copy assignment. Have you tried:

    result = std::move(msg)

    ?


    troygiorshev commented at 4:42 pm on June 22, 2020:
    Tested and I can confirm that you’re right on both accounts. Additionally, since std::is_move_constructible<CNetMessage>::value == true we can be reasonably sure that the Optional is being move constructed (from this boost doc)

    troygiorshev commented at 7:35 pm on June 22, 2020:

    Furthermore, by checking msg.m_recv.data() and result->m_recv.data() before the assignment, after the assignment, and after the method return, we can be absolutely sure that it’s move constructing.

    (and indeed I have, and indeed it is!)

  86. jnewbery commented at 3:08 pm on June 22, 2020: member
    Generally looks good. Only one question about whether changing the return type to an optional incurs an additional copy.
  87. troygiorshev force-pushed on Jun 22, 2020
  88. troygiorshev commented at 4:46 pm on June 22, 2020: contributor

    GetMessage now move constructs the optional.

    I’ve also finally figured out why I keep inadvertently rebasing. Sorry everyone, wont happen again 😅

  89. in src/net.h:652 in ca824dd3d8 outdated
    648 
    649 class V1TransportDeserializer final : public TransportDeserializer
    650 {
    651 private:
    652+    const CChainParams& m_chain_params;
    653+    const NodeId node_id; // Only for logging
    


    jnewbery commented at 7:08 pm on June 22, 2020:
    nit: name m_node_id to comply with style guidelines

    troygiorshev commented at 2:09 pm on June 24, 2020:
    One day I’ll remember, thanks!
  90. in src/net.cpp:730 in ca824dd3d8 outdated
    722@@ -699,37 +723,40 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
    723     return data_hash;
    724 }
    725 
    726-CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageStartChars& message_start, int64_t time) {
    727+Optional<CNetMessage> V1TransportDeserializer::GetMessage(int64_t time)
    728+{
    729     // decompose a single CNetMessage from the TransportDeserializer
    730     CNetMessage msg(std::move(vRecv));
    731+    Optional<CNetMessage> result{};
    


    jnewbery commented at 7:26 pm on June 22, 2020:

    Alternatively, rather than having both a CNetMessage and Optional<CNetMessage>:

     0diff --git a/src/net.cpp b/src/net.cpp
     1index c23cf54c75..7cc5068a75 100644
     2--- a/src/net.cpp
     3+++ b/src/net.cpp
     4@@ -726,14 +726,13 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
     5 Optional<CNetMessage> V1TransportDeserializer::GetMessage(int64_t time)
     6 {
     7     // decompose a single CNetMessage from the TransportDeserializer
     8-    CNetMessage msg(std::move(vRecv));
     9-    Optional<CNetMessage> result{};
    10+    Optional<CNetMessage> msg(std::move(vRecv));
    11 
    12     // store command string, time, and sizes
    13-    msg.m_command = hdr.GetCommand();
    14-    msg.m_time = time;
    15-    msg.m_message_size = hdr.nMessageSize;
    16-    msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
    17+    msg->m_command = hdr.GetCommand();
    18+    msg->m_time = time;
    19+    msg->m_message_size = hdr.nMessageSize;
    20+    msg->m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
    21 
    22     uint256 hash = GetMessageHash();
    23 
    24@@ -743,20 +742,20 @@ Optional<CNetMessage> V1TransportDeserializer::GetMessage(int64_t time)
    25     // Check checksum and header command string
    26     if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
    27         LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d\n",
    28-            SanitizeString(msg.m_command), msg.m_message_size,
    29+            SanitizeString(msg->m_command), msg->m_message_size,
    30             HexStr(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE),
    31             HexStr(hdr.pchChecksum, hdr.pchChecksum + CMessageHeader::CHECKSUM_SIZE),
    32             node_id);
    33+        msg = nullopt;
    34     } else if (!hdr.IsCommandValid()) {
    35         LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes), peer=%d\n",
    36-            hdr.GetCommand(), msg.m_message_size, node_id);
    37-    } else {
    38-        result = std::move(msg);
    39+            hdr.GetCommand(), msg->m_message_size, node_id);
    40+        msg = nullopt;
    41     }
    42 
    43     // Always reset the network deserializer (prepare for the next message)
    44     Reset();
    45-    return result;
    46+    return msg;
    47 }
    

    troygiorshev commented at 7:48 pm on June 22, 2020:
    I’m happy with this pattern if everyone else is (@jonatack). It’s the “reverse” of before. Now we’re assuming success and have to set failure. I think this is clearer.

    jonatack commented at 4:22 am on June 24, 2020:
    LGTM

    MarcoFalke commented at 10:57 am on June 24, 2020:
    LGTM
  91. troygiorshev force-pushed on Jun 24, 2020
  92. troygiorshev force-pushed on Jun 24, 2020
  93. troygiorshev commented at 2:36 pm on June 24, 2020: contributor
    Made suggested changes and two renames. Should be easy to to re-review with git range-diff master ca824dd 20f4da6
  94. MarcoFalke referenced this in commit 67881de0e3 on Jun 24, 2020
  95. troygiorshev force-pushed on Jun 24, 2020
  96. troygiorshev commented at 9:30 pm on June 24, 2020: contributor
    Trivial rebase git range-diff master 20f4da6 2ac16b1
  97. jnewbery commented at 7:07 pm on June 25, 2020: member
    Code review ACK 2ac16b110b
  98. in src/net_processing.cpp:3893 in cecb5e329a outdated
    3659@@ -3660,17 +3660,8 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
    3660     // Message size
    3661     unsigned int nMessageSize = msg.m_message_size;
    3662 
    3663-    // Checksum
    3664-    CDataStream& vRecv = msg.m_recv;
    3665-    if (!msg.m_valid_checksum)
    3666-    {
    3667-        LogPrint(BCLog::NET, "%s(%s, %u bytes): CHECKSUM ERROR peer=%d\n", __func__,
    


    MarcoFalke commented at 2:00 pm on June 26, 2020:

    in commit cecb5e329a26455f44dde39a4ffac82c4869ce2a

    It appears that the peer id is no longer logged for invalid checksums. Doesn’t this make it harder to debug checksum issues because it is now impossible to determine the peer id?

    Previous log:

    0[net] CHECKSUM ERROR (msg_type, baz bytes), expected foo was bar
    1[msghand] ProcessMessages(msg_type, baz bytes): CHECKSUM ERROR peer=id
    

    MarcoFalke commented at 3:13 pm on June 26, 2020:

    I see in the last commit you add + const NodeId m_node_id; // Only for logging

    Maybe this addition could be moved to the first commit? It will be slightly painful due to the conflicts that will need to be solved, but I think it will be worth it


    troygiorshev commented at 7:27 pm on June 29, 2020:
    Moving it to the beginning was well worth it. The peer id is now present in all of this logging (and everything is just a little cleaner).
  99. in src/test/fuzz/p2p_transport_deserializer.cpp:36 in cecb5e329a outdated
    41-            }
    42-            if (!msg.m_valid_netmagic) {
    43-                assert(!msg.m_valid_header);
    44+            auto result = deserializer.GetMessage(Params().MessageStart(), m_time);
    45+            if (result) {
    46+                const CNetMessage msg = *result;
    


    MarcoFalke commented at 2:12 pm on June 26, 2020:

    nit in commit cecb5e329a26455f44dde39a4ffac82c4869ce2a

    0                const CNetMessage& msg = *result;
    

    can be a reference to a avoid the copy


    troygiorshev commented at 7:28 pm on June 29, 2020:
    I’ve changed this so that it uses list initialization instead and then reaches inside the optional. Now it’s consistent with the “real” code in net.cpp.
  100. in src/net.cpp:585 in 9d5ba871fc outdated
    578@@ -579,6 +579,16 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
    579 }
    580 #undef X
    581 
    582+/**
    583+ * Receive bytes from the buffer and deserialize them into messages.
    584+ *
    585+ * @param[in]   pch         Socket buffer
    


    MarcoFalke commented at 2:22 pm on June 26, 2020:

    in commit 9d5ba871fcba17038236a660e231022d11b769bc:

    I don’t think this method cares where the data is from. It can be any pointer that points to initialized data.

    0 * [@param](/bitcoin-bitcoin/contributor/param/)[in]   pch         A pointer to the raw data
    

    Side note: (in the future) this could probably be changed to a Span, so that the pointer and size are passed in the same argument for clarity


    troygiorshev commented at 7:28 pm on June 29, 2020:
    +1 to one day making this a span
  101. in src/net.cpp:587 in 9d5ba871fc outdated
    578@@ -579,6 +579,16 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
    579 }
    580 #undef X
    581 
    582+/**
    583+ * Receive bytes from the buffer and deserialize them into messages.
    584+ *
    585+ * @param[in]   pch         Socket buffer
    586+ * @param[in]   nBytes      Size of the socket buffer
    587+ * @param[out]  complete    Set True if a message has been deserialized and is
    


    MarcoFalke commented at 2:26 pm on June 26, 2020:

    in commit 9d5ba87:

    0 * [@param](/bitcoin-bitcoin/contributor/param/)[out]  complete    Set True if at least one message has been deserialized and is
    

    Could make this a bit more verbose for clarity?

  102. in src/net.h:677 in 8a492c8aef outdated
    673@@ -674,8 +674,8 @@ class V1TransportDeserializer final : public TransportDeserializer
    674     }
    675 
    676 public:
    677-
    678-    V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) {
    679+    V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn)
    


    MarcoFalke commented at 2:32 pm on June 26, 2020:

    in commit 8a492c8aef

    This is unused, no?

    0    V1TransportDeserializer(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn)
    

    MarcoFalke commented at 3:26 pm on June 26, 2020:

    Also, if you decide to change this, maybe this very long line could be split up into two (or more) lines:

    E.g.

     0diff --git a/src/net.h b/src/net.h
     1index b461470f1f..ba2d2fad27 100644
     2--- a/src/net.h
     3+++ b/src/net.h
     4@@ -674,8 +674,11 @@ private:
     5     }
     6 
     7 public:
     8-
     9-    V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) {
    10+    V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn)
    11+        : hdrbuf(nTypeIn, nVersionIn),
    12+          hdr(pchMessageStartIn),
    13+          vRecv(nTypeIn, nVersionIn)
    14+    {
    15         Reset();
    16     }
    17 
    

    or

     0diff --git a/src/net.h b/src/net.h
     1index b461470f1f..1d99f2608b 100644
     2--- a/src/net.h
     3+++ b/src/net.h
     4@@ -674,8 +674,9 @@ private:
     5     }
     6 
     7 public:
     8-
     9-    V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) {
    10+    V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn)
    11+        : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn)
    12+    {
    13         Reset();
    14     }
    15 
    

    troygiorshev commented at 7:28 pm on June 29, 2020:
    Good eye. Fixed and cleaned.
  103. in src/net.cpp:13 in ef99b3bfbc outdated
     9@@ -10,7 +10,6 @@
    10 #include <net.h>
    11 
    12 #include <banman.h>
    13-#include <chainparams.h>
    


    MarcoFalke commented at 2:37 pm on June 26, 2020:

    nit In commit ef99b3bfbca87ef727c34f9ffedf50139a223086

    Why is this removed? I think every file needs to include everything it uses. Otherwise, removing it from the header file might break compilation in the cpp file.

  104. in src/protocol.cpp:118 in 3f5a7306e9 outdated
    112@@ -113,6 +113,11 @@ std::string CMessageHeader::GetCommand() const
    113     return std::string(pchCommand, pchCommand + strnlen(pchCommand, COMMAND_SIZE));
    114 }
    115 
    116+std::string CMessageHeader::GetMessageStart() const
    117+{
    118+    return std::string(pchMessageStart, pchMessageStart + strnlen(pchMessageStart, MESSAGE_START_SIZE));
    


    MarcoFalke commented at 2:55 pm on June 26, 2020:

    in commit 3f5a7306e941bd9bcd50afeb0e0a9fce46f22fd1

    It seems confusing to make the magic bytes variable length. Unlike the message type they are not padded with zeros. See also https://btcinformation.org/en/developer-reference#message-headers

    What do you think about returning a span (or a raw byte vector if there are concerns about life-time)?


    troygiorshev commented at 1:40 pm on July 1, 2020:

    Thanks for bringing this up, I agree. I’ve removed this commit and changed the logging to use HexStr(hdr.pchMessageStart, hdr.pchMessageStart + CMessageHeader::MESSAGE_START_SIZE), like how the checksum is logged later in the file.

    I think a span would be a great idea, and a future PR should continue the work that #19373 started. (Maybe once someone converts HexStr to use span, as suggested there)

  105. in src/net.cpp:662 in 2ac16b110b outdated
    675@@ -674,11 +676,19 @@ int V1TransportDeserializer::readHeader(const char *pch, unsigned int nBytes)
    676         hdrbuf >> hdr;
    677     }
    678     catch (const std::exception&) {
    679+        LogPrint(BCLog::NET, "HEADER ERROR - UNABLE TO DESERIALIZE, peer=%d\n", m_node_id);
    


    MarcoFalke commented at 3:09 pm on June 26, 2020:

    in commit 2ac16b110b

    Adding logging is great, but I don’t think this counts as a refactor. Also, the tests seem to require some changes, so maybe change the “without changing behavior” to something like “add logging” in the pull request title?


    troygiorshev commented at 1:44 pm on July 1, 2020:
    I agree, this has grown and there is enough context in the PR description. Changed.
  106. MarcoFalke commented at 3:11 pm on June 26, 2020: member

    Concept ACK 2ac16b110b, have not reviewed the last commit closely, though 💤

    Signature:

     0-----BEGIN PGP SIGNED MESSAGE-----
     1Hash: SHA512
     2
     3Concept ACK 2ac16b110b, have not reviewed the last commit closely, though 💤
     4-----BEGIN PGP SIGNATURE-----
     5
     6iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
     7pUgZrAwAkSf9mUvVVNaESgSEPbSJ7kdWCdMS8uGNZT5O7DTU6RTYDipsKrDQYw5A
     8j3+8WnKERcUhnpKdbBlCdKrqfUMHwXsyiN8CyTiIV0zNolQhyGPaqjP9JjVNprra
     9JZas4VrReozgD9AUQGwbX2tt03oflwFrx1VTPqN+EfEO6PUaAygXD4m4YVg3ds6U
    10c5kfwrk2Qb6fT/qkc9B9WQoXee20ZcflSZw0zPU6Vd9hXfepZlSyeE1lLfm7H7OI
    11xCg87Glp56I0y1Yo4hxDb3SPOsfC4tKDtUyZVUbi5GFyLL4aIzA1MSijKBv3fLuz
    12QfeACGJy9r6RXh96e20kx2uNn3wNthGOWiX5SQQqhr6xtk7XtSJUCY9seyphUd5v
    13jqm9V8d2eRbFaDM6/JXM3LsiYkJF7FRiBzQ2UMIGQAAlj4n1ZIYfm9ouAjfwPRle
    14u5r2fpBIfE7GCjBqofWZRq204vbxL1AUjix6dqJLTuWiGCyvnvlZ4P8yQ5B+N/wy
    15meVwrbR0
    16=6DvQ
    17-----END PGP SIGNATURE-----
    

    Timestamp of file with hash de78ffc6eeed0e9e43763f5155a32c4afcddca965281598fe478abf92266c9e8 -

  107. MarcoFalke commented at 3:27 pm on June 26, 2020: member
    As always, feel free to ignore the part of my feedback that is of stylistic nature only
  108. PastaPastaPasta commented at 4:03 pm on June 28, 2020: contributor

    can you run clang-format-diff?

     0diff --git a/src/net.cpp b/src/net.cpp
     1index eaa903b9c..acbc209c7 100644
     2--- a/src/net.cpp
     3+++ b/src/net.cpp
     4@@ -744,12 +744,12 @@ Optional<CNetMessage> V1TransportDeserializer::GetMessage(int64_t time)
     5         LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d\n",
     6                  SanitizeString(msg->m_command), msg->m_message_size,
     7                  HexStr(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE),
     8-                 HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE),
     9+                 HexStr(hdr.pchChecksum, hdr.pchChecksum + CMessageHeader::CHECKSUM_SIZE),
    10                  m_node_id);
    11         msg = nullopt;
    12     } else if (!hdr.IsCommandValid()) {
    13         LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes), peer=%d\n",
    14-            hdr.GetCommand(), msg->m_message_size, m_node_id);
    15+                 hdr.GetCommand(), msg->m_message_size, m_node_id);
    16         msg = nullopt;
    17     }
    
  109. troygiorshev force-pushed on Jun 29, 2020
  110. troygiorshev force-pushed on Jun 29, 2020
  111. troygiorshev commented at 7:56 pm on June 29, 2020: contributor

    6b33f25ff9 is a new commit, then the rest should be ok to re-review with git range-diff master 2ac16b1 da5bb91

    This (remove unneeded parameter to V1TransportDeserializer constructor in one commit) is dealt with, and the line cleaned up.

    I see in the last commit you add const NodeId m_node_id; // Only for logging Maybe this addition could be moved to the first commit? It will be slightly painful due to the conflicts that will need to be solved, but I think it will be worth it

    Done, in its own new commit right at the beginning. This cleans things up nicely.

    nit in commit cecb5e3

    • const CNetMessage msg = *result;
    • const CNetMessage& msg = *result; To avoid the copy

    I’ve changed this so that it uses list initialization instead and then reaches inside the optional. Now it’s consistent with the “real” code in net.cpp.

    Also, clarified the new doxygen comment.

  112. troygiorshev force-pushed on Jun 29, 2020
  113. troygiorshev force-pushed on Jun 29, 2020
  114. troygiorshev commented at 8:11 pm on June 29, 2020: contributor
    git range-diff master b77c159 bc89f1f ran clang-format only.
  115. in src/net.cpp:727 in 2616399cca outdated
    717@@ -718,10 +718,11 @@ CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageSta
    718 
    719     msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0);
    720     if (!msg.m_valid_checksum) {
    721-        LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n",
    722+        LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d\n",
    


    MarcoFalke commented at 11:55 pm on June 29, 2020:

    test-nit in commit 2616399cca2e9c1a4c03605241c7964d53b360bf

    Could extend the test?

     0diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
     1index d9a9ae5188..04d86d3bc5 100755
     2--- a/test/functional/p2p_invalid_messages.py
     3+++ b/test/functional/p2p_invalid_messages.py
     4@@ -93,7 +93,9 @@ class InvalidMessagesTest(BitcoinTestFramework):
     5     def test_checksum(self):
     6         self.log.info("Test message with invalid checksum logs an error")
     7         conn = self.nodes[0].add_p2p_connection(P2PDataStore())
     8-        with self.nodes[0].assert_debug_log(['CHECKSUM ERROR (badmsg, 2 bytes), expected 78df0a04 was ffffffff']):
     9+        log_msg = 'CHECKSUM ERROR (badmsg, 2 bytes), expected 78df0a04 was ffffffff, peer={}'.format(
    10+            self.nodes[0].getpeerinfo()[0]['id'])
    11+        with self.nodes[0].assert_debug_log([log_msg]):
    12             msg = conn.build_message(msg_unrecognized(str_data="d"))
    13             # Checksum is after start bytes (4B), message type (12B), len (4B)
    14             cut_len = 4 + 12 + 4
    

    troygiorshev commented at 1:52 pm on July 1, 2020:
    This could be done for all of the tests, and can mostly be done separate from the changes in this PR. I’ll do this in another PR.

    troygiorshev commented at 3:33 pm on July 1, 2020:
    Additionally I’m not sure that we have consensus that assert_debug_log is really a good thing.
  116. jnewbery commented at 8:43 pm on June 30, 2020: member
    Code review ACK bc89f1f40c465aff82db5a8c6f0753a21222968d
  117. troygiorshev force-pushed on Jul 1, 2020
  118. troygiorshev commented at 1:42 pm on July 1, 2020: contributor
    git range-diff master bc89f1f 1e5a43d removed the commit that added a pchMessageStart getter, as per this comment.
  119. troygiorshev renamed this:
    p2p: Refactor, move all header verification into the network layer, without changing behavior
    p2p: Move all header verification into the network layer, extend logging
    on Jul 1, 2020
  120. jnewbery commented at 7:02 pm on July 1, 2020: member
    Code review ACK 1e5a43d371981f0d5ece98cfba0ecab32c795a4e. Only change is removing the unnecessary CMessageHeader::GetMessageStart() function.
  121. MarcoFalke commented at 11:14 pm on July 1, 2020: member

    ACK 1e5a43d371 🏒

    Signature:

     0-----BEGIN PGP SIGNED MESSAGE-----
     1Hash: SHA512
     2
     3ACK 1e5a43d371 🏒
     4-----BEGIN PGP SIGNATURE-----
     5
     6iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYEACgkQzit1aX5p
     7pUiKXAwAwBlQRnJH+82cTJNWxA553dzV7MBYXID/rH6dSlPsC0MM517e90mdcmxa
     8NS5rGqn5GWHPsYtACTouR0b/1lqKEQayH1pNbMmSRmVx4nh3p754H0B8z7IjuyTK
     9/WVZSWKu1Esao8+Aohi77LpRaJFAjr7a3YRswbNWwrlVEUZadcwwdkSFYAtiFns6
    10GqrQta2eNUwvCfrn9pCRdsfBiir82FflOzDZhpIU0FMNVqiutlCXA02/+iGxxVdk
    11BTrA8v+8JoxqQJzCySbQuzlSDUijmWz3CyRpLBt6ZHBGVOekhuzvMZuCrN8wSjtM
    12/0RwyeYyyDAJLSdK22omTXR0zE2rP4e9pAvoHp9kr9tm2cwuu2QHbdsRmOPUQjRX
    13R/jCELQUuMxkuZ6ycUrd+ik+Du/4XCkqd9tUT+9kn+odZW33eyDU2Fg+HbdnkIkP
    14tlFIOH1vIgb86uA2dLUzS3yPw90IHFWLvUV73PUBppV0ySWSDBkw35GO1yruT+CA
    15lRjilH53
    16=BsOR
    17-----END PGP SIGNATURE-----
    

    Timestamp of file with hash 4088ef0369eec339a13f0de7d14c8dde10e627b06c1833e8fd5f67b6757af295 -

  122. MarcoFalke commented at 11:15 pm on July 1, 2020: member
    cc @dongcarl You might be interested in this
  123. dongcarl commented at 9:30 pm on July 8, 2020: member

    @troygiorshev please feel free to correct me if I misunderstand anything. :relaxed:


    This PR moves 4 separate header verification pieces into the net layer, the verifications are namely:

    1. Header checksum (m_valid_checksum) -> Message ignored
    2. Header command (m_valid_header-ish) -> Message ignored
    3. Header network magic (m_valid_netmagic) -> Disconnect from originating peer

    Previously, these checks were performed in net, but any failure was effectively ignored (modulo some logging), and the message allowed to propagate as normal until the message is inspected in ProcessMessages.

    Now, these checks are performed and acted upon in net. With:

    • Checks (1) + (2) causing a nullopt to be returned from GetMessage (resulting in the same “message ignored” behaviour), and
    • Check (3) causing ReceiveMsgBytes to return false (resulting in the same “peer disconnect behaviour in SocketHandler)

    However, in acting on these validity failures early for checks (1) + (2), we seem to short-circuit some code which previously would have executed, namely:

    1. HUNK: We skip the mapRecvBytesPerMsgCmd accounting logic for failing messages where previously we would have counted them
    2. HUNK: We skip adding the invalid message to the vRecvMsg process queue, which would previously count it against our nReceiveFloodSize: https://github.com/bitcoin/bitcoin/blob/9f4c0a9694d399388d0514c13feae7d2303fac3d/src/net.cpp#L1399-L1417

    I think this is probably an accuracy improvement for (1), as the only place I see mapRecvBytesPerMsgCmd accounting logic being used is in the getpeerinfo RPC. However, I’m not sure that (2) is intended or desirable, but I’m most likely missing something.

  124. DrahtBot added the label Needs rebase on Jul 10, 2020
  125. troygiorshev commented at 4:27 pm on July 14, 2020: contributor

    @dongcarl Thank you for the review!

    (First Part)

    Yup you’ve got it, here it is more verbosely:

    Disconnect/Ignore Behaviour, in order of when acted upon

    Before

    0Unable to Deserialize Header....................-> Disconnect from originating peer
    1Payload Declared > MAX_SIZE.....................-> Disconnect from originating peer
    2Payload Declared > MAX_PROTOCOL_MESSAGE_LENGTH..-> Disconnect from originating peer
    3----- ^ SocketHandler | MessageHandler \/
    4Bad MessageStart/NetMagic.......................-> Disconnect from originating peer
    5Bad MessageStart/NetMagic.......................-> Ignore Message
    6Invalid Type/command............................-> Ignore Message
    7Payload Declared > MAX_SIZE.....................-> Ignore Message
    8Checksum Error..................................-> Ignore Message
    

    Yes there is repetition, that’s how the code is/was.

    After (All in SocketHandler)

    0Unable to Deserialize Header....................-> Disconnect from originating peer
    1Bad MessageStart/NetMagic.......................-> Disconnect from originating peer
    2Payload Declared > MAX_SIZE.....................-> Disconnect from originating peer
    3Payload Declared > MAX_PROTOCOL_MESSAGE_LENGTH..-> Disconnect from originating peer
    4Checksum Error..................................-> Ignore Message
    5Invalid Type/Command............................-> Ignore Message
    

    (Second Part)

    1. You’re right that this a change. However, I disagree that it’s an improvement. Before these changes, for a working node the sum of the values for all of the message types in mapRecvBytesPerMsgCmd (aka bytesrecv_per_msg in getpeerinfo) was equal to the value of nRecvBytes (aka bytesrecv in getpeerinfo). (I’ve checked this for one peer on a running node). Thinking for a little bit, I don’t see a way that these could have previously fell out of sync while a node stayed connected. This is a nice invariant to keep. With this PR, if a peer sends some invalid messages that are ignored, they do fall out of sync. IMO this is sufficient reason to give GetMessage an out parameter explaining the reason for failure. (I dream that one day we’ll have a Result<T,E> type…) With that it will be easy to include the command and raw_message_size.
    2. Thank you for catching this, indeed it wasn’t something I had considered. Looking at it closely though, I don’t think this is a problem. nReceiveFloodSize and nProcessQueueSize work together to prevent MessageHandler from being flooded with messages. This still works the same in this PR, we just avoid putting the message in the vRecvMsg buffer in the first place. The MessageHandler thread is still protected by fPauseRecv. SocketHandler doesn’t have any buffers that could be filled, so we don’t have to worry about possibly revealing a bug where fPauseRecv was unintentionally saving SocketHandler from something.
  126. troygiorshev force-pushed on Jul 17, 2020
  127. troygiorshev commented at 1:27 pm on July 17, 2020: contributor

    git range-diff master 1e5a43d 13ef377

    Fixed mapRecvBytesPerMsgCmd problem as discussed above. This was done by giving GetMessage an out parameter, set when message deserialization fails.

  128. troygiorshev force-pushed on Jul 17, 2020
  129. troygiorshev force-pushed on Jul 17, 2020
  130. troygiorshev commented at 1:57 pm on July 17, 2020: contributor
    git range-diff master 13ef377 6a0f059 rebase
  131. DrahtBot removed the label Needs rebase on Jul 17, 2020
  132. troygiorshev closed this on Jul 21, 2020

  133. troygiorshev reopened this on Jul 21, 2020

  134. troygiorshev commented at 1:43 pm on July 21, 2020: contributor
    close-open to restart travis
  135. in src/net.cpp:2861 in 6a0f059f25 outdated
    2807@@ -2778,7 +2808,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
    2808         LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
    2809     }
    2810 
    2811-    m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION));
    2812+    m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
    


    jnewbery commented at 4:10 pm on July 21, 2020:
    No need to use the public getter GetId() since this is a member function of CNode. You can just use id directly.

    troygiorshev commented at 0:32 am on July 22, 2020:
    I agree this is better, but I’ll leave it for now.
  136. in src/net.cpp:621 in 6a0f059f25 outdated
    614+            uint32_t out_err{0};
    615+            Optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err)};
    616+            if (!result) {
    617+                // Message deserialization failed.  Drop the message but don't disconnect the peer.
    618+                // store the size of the corrupt message
    619+                mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER)->second += out_err;
    


    jnewbery commented at 4:13 pm on July 21, 2020:
    I suggest you use a new category NET_MESSAGE_COMMAND_CORRUPT here rather than overloading _OTHER.

    troygiorshev commented at 0:34 am on July 22, 2020:

    I’m worried about how adding a new category will affect people who have scripts that parse getpeerinfo. They will either:

    1. Crash (bad)
    2. Ignore it (also bad, loses the “sum of messages in bytesrecv_per_msg” == “bytesrecv” invariant)
    3. Treat it like “_OTHER” (same as what we have here)

    As it is right now in the PR, the bytes received for corrupt messages have been moved from their respective command to _OTHER. This is definitely an accuracy improvement. I’m not sure that adding a new category is worth the scope creep.

    I’ve implemented your suggested change at this branch.


    jnewbery commented at 7:42 am on July 22, 2020:

    We do occasionally add new message types (most recently in #18876), so a client should tolerate unrecognized message types (crashing/requiring a software update for the client every time we introduce a new message type would be bad).

    I agree that this isn’t a critical issue and could be changed in a follow-up if necessary. For now, _OTHER seems fine.

  137. jnewbery commented at 4:41 pm on July 21, 2020: member

    utACK 6a0f059f254449a854ee12058a10b5d0fbfa6ae4

    A couple of small suggested changes inline. Feel free to take or leave them.

  138. troygiorshev commented at 1:08 pm on August 7, 2020: contributor
    @jonatack @dongcarl @MarcoFalke you’ve all been interested in this in the past. I gave this another look over and I think it’s ready for review.
  139. DrahtBot added the label Needs rebase on Aug 9, 2020
  140. troygiorshev force-pushed on Aug 10, 2020
  141. troygiorshev commented at 1:34 pm on August 10, 2020: contributor

    git range-diff master 6a0f059 9377c0c

    • Rebased, including new span-taking HexStr
  142. DrahtBot removed the label Needs rebase on Aug 10, 2020
  143. jnewbery commented at 1:07 pm on August 13, 2020: member
    Code review ACK 9377c0c4586a7ea70f6174626f463902e92ed76a @jonatack @dongcarl @MarcoFalke this looks ready to me. Do you mind rereviewing?
  144. dongcarl commented at 5:45 pm on August 13, 2020: member

    Looking at this again, w/re the 2nd item I mentioned last time:

    It seems like nReceiveFloodSize is configured by the -maxreceivebuffer, which has the description of:

    Maximum per-connection receive buffer, *1000 bytes (default: 5000)

    The way that I would interpret this description as a user is that this places a limit on how many bytes we receive from a peer with zero consideration for any validity checks.

    I admit that any interpretation of this description will be very subjective, and I may very well be in the minority here. And I would love to hear from others if this is the case.

    My version of the interpretation was true prior to this PR as far as I cal tell: all of the bytes received from the socket, regardless of net-validity, would all be interpreted as CNetMessages, pushed onto vRecvMsg, and all spliced into vProcessMsg, counting against nReceiveFloodSize. In effect, vRecvMsg was the same as vProcessMsg, and they were both the “receive buffer” and the “process buffer”.

    Note: The only exception here would be for messages larger than MAX_SIZE/MAX_PROTOCOL_MESSAGE_LENGTH, but in that case there would be an immediate disconnect in SocketHandler.

    However, after this PR, bytes we receive from the socket which result in net-invalid messages will not be placed in vRecvMsg, meaning that they won’t be added to vProcessMsg and not be restricted by -maxreceivebuffer/nReceiveFloodSize. In effect, RecvMsg is still the same as vProcessMsg, however, they are now both more accurately the “process buffer” and not the “receive buffer”.

    In my mind, this effectively changes the meaning of -maxreceivebuffer from “Maximum per-connection receive buffer” to “Maximum per-connection process buffer”.


    Given the complexity of our net code, I’m most likely missing something. However, if what I’m saying rings true, then I believe the resolution depends on wether we think users care more about controlling the “Maximum per-connection receive buffer” or the “Maximum per-connection process buffer”. In the latter case, we can simply rename the flag and its description and call it a day. In the former case, it might be a little more complicated.

  145. jnewbery commented at 11:13 am on August 14, 2020: member

    whether we think users care more about controlling the “Maximum per-connection receive buffer” or the “Maximum per-connection process buffer”.

    The nReceiveFloodSize limit is a memory exhaustion protection. I expect that [almost] no-one is setting it manually, except perhaps those with very low memory hardware. In all cases, it’s better to shed invalid messages as early as possible and as low in the stack as possible, rather than wasting memory by storing them in a buffer.

    In the latter case, we can simply rename the flag and its description

    I’m definitely a NACK on renaming the flag (since if anyone is using the flag, then that would break their config silently). I also don’t think changing the description is worth it. I expect anyone using this flag is dialing the value down from the default because they care about memory exhaustion. Whether the buffer is a ‘receive buffer’ or ‘process buffer’ is really more of an implementation detail.

  146. troygiorshev commented at 7:43 pm on August 14, 2020: contributor

    … places a limit on how many bytes we receive from a peer with zero consideration for any validity checks.

    How many bytes we receive before what occurs? Before we process them, or something else?

    Did some archaeology on nReceiveFloodSize. It was added in 2011 in #369 as ReceiveBufferSize(), brought to its current usage in #973 and, from what I found, I have to disagree with you here:

    In my mind, this effectively changes the meaning of -maxreceivebuffer from “Maximum per-connection receive buffer” to “Maximum per-connection process buffer”.

    nReceiveFloodSize/ReceiveBufferSize, configured by maxreceivebuffer, is and has always been a limit on the per-connection process buffer. This was true in 2011, was true before this PR, and it still true now. It has always protected the size of the data structure that sends messages from SocketHandler to MessageHandler (was called vRecv, now called vProcessMsg, I’ll use vProcessMsg unambiguously). In fact, back then we didn’t even have a data structure analogous to vRecvMsg.

    All of the following are still true:

    • When is the “protected size” added to? Before we put a message onto vProcessMsg.
    • When is the “protected size” subtracted from? After we’ve taken the message off of vProcessMsg.
    • If this limit is surpassed, what is prevented? It prevents us from receiving data from the peer.

    The fact that we are now dealing with some messages before they’re placed onto vProcessMsg is fundamental to this PR. We could, if we wanted to, add to the “protected size” the moment we receive the data from the peer and then, in the case that a check fails in net, subtract from the “protected size” there. But, as those are both happening one after another in the same thread, it’s just redundant.

    It’s unfortunate -maxreceivebuffer is named what it is. In its context, “receive” has always meant “process”.

  147. DrahtBot added the label Needs rebase on Aug 24, 2020
  148. troygiorshev force-pushed on Aug 24, 2020
  149. troygiorshev commented at 3:50 pm on August 24, 2020: contributor

    git range-diff master 9377c0c e72c5b2

    Rebased

  150. DrahtBot removed the label Needs rebase on Aug 24, 2020
  151. dongcarl commented at 6:18 pm on August 25, 2020: member

    ACK e72c5b2


    After reading @jnewbery and @troygiorshev’s responses and thinking a bit more, I think I see how it is most likely safe to not count net-invalid messages against nReceiveFloodSize. Perhaps in the future some naming can be tidied up so that the intention is clearer, but that should in no way stop this PR from getting merged.

  152. in src/net.cpp:621 in 5cecad30d9 outdated
    607-            CNetMessage msg = m_deserializer->GetMessage(Params().MessageStart(), time);
    608+            uint32_t out_err{0};
    609+            Optional<CNetMessage> result{m_deserializer->GetMessage(Params().MessageStart(), time, out_err)};
    610+            if (!result) {
    611+                // store the size of the corrupt message
    612+                mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER)->second += out_err;
    


    ryanofsky commented at 9:47 pm on August 27, 2020:

    In commit “Move checksum check from net_processing to net” (5cecad30d9ce37ba500eb1e06743d213bb911379)

    Minor:

    0mapRecvBytesPerMsgCmd.at(NET_MESSAGE_COMMAND_OTHER) += out_err;
    

    Would be shorter and avoid undefined behavior if there’s no COMMAND_OTHER entry.

  153. in src/net.cpp:757 in 5cecad30d9 outdated
    746-                 SanitizeString(msg.m_command), msg.m_message_size,
    747+                 SanitizeString(msg->m_command), msg->m_message_size,
    748                  HexStr(Span<uint8_t>(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE)),
    749                  HexStr(hdr.pchChecksum),
    750                  m_node_id);
    751+        out_err = msg->m_raw_message_size;
    


    ryanofsky commented at 9:56 pm on August 27, 2020:

    In commit “Move checksum check from net_processing to net” (5cecad30d9ce37ba500eb1e06743d213bb911379)

    jonatack commented on the out_err name and I agree it’s not descriptive, and I think additionally it’s awkward that caller can’t just refer to one variable for the raw message size, but has use look at different variables (out_err or result->m_raw_message_size) depending on whether the checksum fails. I think a better API for the function would be:

    0-Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err)
    1+CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, bool& reject_message)
    

    Switching to this would also make the commit much smaller because msg. could stay msg. everywhere and not have to change to msg-> and result->.

  154. in src/net.cpp:727 in 90fc3ffa82 outdated
    723@@ -724,10 +724,11 @@ CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageSta
    724 
    725     msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0);
    726     if (!msg.m_valid_checksum) {
    727-        LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n",
    728+        LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d\n",
    


    ryanofsky commented at 10:00 pm on August 27, 2020:

    In commit “Give V1TransportDeserializer an m_node_id member” (90fc3ffa82e398a6c95dbaf5374b6a478f17ae88)

    It would be helpful for this commit message to say “This commit doesn’t change behavior except for logging”. It’s harder to know if commit is doing the right thing if commit message doesn’t say what intended behavior change is.

  155. in src/net_processing.cpp:3891 in 5cecad30d9 outdated
    3866@@ -3867,17 +3867,8 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
    3867     // Message size
    3868     unsigned int nMessageSize = msg.m_message_size;
    3869 
    3870-    // Checksum
    3871-    CDataStream& vRecv = msg.m_recv;
    3872-    if (!msg.m_valid_checksum)
    


    ryanofsky commented at 11:45 am on August 28, 2020:

    In commit “Move checksum check from net_processing to net” (5cecad30d9ce37ba500eb1e06743d213bb911379)

    I think commit message here should say to what extent new behavior is equivalent to old behavior, because just looking at the current message, it’s not clear what behavior changes are intended, and how fPauseRecv and mapRecvBytesPerMsgCmd and nReceiveFloodSize are involved. This information is hidden away in review discussion:

    #19107 (comment) #19107 (comment) #19107 (comment) #19107 (comment) #19107 (comment)

    After reading the discussion, it’s still not clear to me why this change doesn’t make it easier for a peer to flood the connection with bad checksum messages that cause log spam but are otherwise ignored. Is the takeaway that this change makes it slightly easier to flood but not significantly easier? Or that there’s some other flood protection? Or that the new and old behavior are equivalent some other way? This is the type of information that would be good to have in the commit message.

  156. in src/net.cpp:2861 in e72c5b2e27 outdated
    2826@@ -2797,7 +2827,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
    2827         LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
    2828     }
    2829 
    2830-    m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION));
    2831+    m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
    


    jonatack commented at 1:43 pm on August 30, 2020:
    0    m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params(), id, SER_NETWORK, INIT_PROTO_VERSION));
    

    troygiorshev commented at 5:05 pm on September 16, 2020:
    Yup, I prefer this too. I don’t think it’s unclear enough to merit review effort changing it now though, I’ll incorporate it into a follow-up.
  157. in src/net.h:734 in e72c5b2e27 outdated
    684@@ -686,13 +685,15 @@ class TransportDeserializer {
    685     // read and deserialize data
    686     virtual int Read(const char *data, unsigned int bytes) = 0;
    687     // decomposes a message from the context
    688-    virtual CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, std::chrono::microseconds time) = 0;
    689+    virtual Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0;
    


    jonatack commented at 3:04 pm on August 30, 2020:
    IIUC, out_err isn’t an error number like its name suggests, but rather the size in bytes of the message. Perhaps its naming could be clearer.

    troygiorshev commented at 1:24 pm on September 16, 2020:

    I named it this way because TransportDeserializer is such a general interface. It’s possible that some other implementation of it will use out_err as some sort of magic number.

    Right now it is only set when the method fails. If we’re going to go down the route of renaming this to something like msg_size, then I would prefer to always set it.


    troygiorshev commented at 4:51 pm on September 16, 2020:
    fixed
  158. in src/net.cpp:621 in e72c5b2e27 outdated
    618+            uint32_t out_err{0};
    619+            Optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err)};
    620+            if (!result) {
    621+                // Message deserialization failed.  Drop the message but don't disconnect the peer.
    622+                // store the size of the corrupt message
    623+                mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER)->second += out_err;
    


    jonatack commented at 3:07 pm on August 30, 2020:
    This is one of several places where the out_err naming seems odd to me; out_err suggests an error number more than the message size in bytes.

    troygiorshev commented at 4:51 pm on September 16, 2020:
    fixed
  159. in src/net.cpp:674 in e72c5b2e27 outdated
    694         return -1;
    695     }
    696 
    697     // reject messages larger than MAX_SIZE or MAX_PROTOCOL_MESSAGE_LENGTH
    698     if (hdr.nMessageSize > MAX_SIZE || hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
    699+        LogPrint(BCLog::NET, "HEADER ERROR - SIZE (%s, %u bytes), peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, m_node_id);
    


    jonatack commented at 3:26 pm on August 30, 2020:

    Rather than just “size”, the logged error message could state that the message is too large and state what is expected, like for the checksum error logging

    0     // reject messages larger than MAX_SIZE or MAX_PROTOCOL_MESSAGE_LENGTH
    1-    if (hdr.nMessageSize > MAX_SIZE || hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
    2-        LogPrint(BCLog::NET, "HEADER ERROR - SIZE (%s, %u bytes), peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, m_node_id);
    3+    unsigned int max_size{std::max(MAX_SIZE, MAX_PROTOCOL_MESSAGE_LENGTH)};
    4+    if (hdr.nMessageSize > max_size) {
    5+        LogPrint(BCLog::NET, "HEADER ERROR - SIZE (%s, %u bytes, %u expected), peer=%d\n",
    6+                 hdr.GetCommand(), hdr.nMessageSize, max_size, m_node_id);
    7         return -1;
    8     }
    

    troygiorshev commented at 4:58 pm on September 16, 2020:
    I like this, I’ll leave it for a follow-up PR.
  160. in src/net.cpp:735 in e72c5b2e27 outdated
    776+                 HexStr(hdr.pchChecksum),
    777+                 m_node_id);
    778+        out_err = msg->m_raw_message_size;
    779+        msg = nullopt;
    780+    } else if (!hdr.IsCommandValid()) {
    781+        LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes), peer=%d\n",
    


    jonatack commented at 3:30 pm on August 30, 2020:
    0        LogPrint(BCLog::NET, "HEADER ERROR - INVALID COMMAND (%s, %u bytes), peer=%d\n",
    

    troygiorshev commented at 4:59 pm on September 16, 2020:
    I’ll do a run through and decide between “ERROR - X” and “ERROR - INVALID X” (and make consistent) in a follow-up PR
  161. in src/protocol.cpp:89 in 878d829b5b outdated
    87@@ -88,9 +88,9 @@ const static std::string allNetMessageTypes[] = {
    88 };
    89 const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
    90 
    91-CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn)
    92+CMessageHeader::CMessageHeader()
    93 {
    94-    memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE);
    95+    memset(pchMessageStart, 0, MESSAGE_START_SIZE);
    


    ryanofsky commented at 11:21 am on September 1, 2020:

    In commit “Change CMessageHeader Constructor” (878d829b5bff9db5140c9547400ec024a08c0105)

    I guess this is the change explained by “This messagestart should always be replaced when deserializing an actual message header so that we can run checks on it.” in the commit message.

    Would suggest making that a little more explicit like “This messagestart is overwritten by data from incoming messages during deserialization, so there is no change in behavior when processing messages.”

  162. in src/net_processing.cpp:3874 in e72c5b2e27 outdated
    3840@@ -3849,19 +3841,6 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
    3841     CNetMessage& msg(msgs.front());
    3842 
    3843     msg.SetVersion(pfrom->GetRecvVersion());
    3844-    // Check network magic
    3845-    if (!msg.m_valid_netmagic) {
    3846-        LogPrint(BCLog::NET, "PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId());
    3847-        pfrom->fDisconnect = true;
    


    ryanofsky commented at 11:45 am on September 1, 2020:

    In commit “Remove header checks out of net_processing” (e72c5b2e27b6ac3a1303b133f551bf2afcff1c0d)

    Again commit message should say what intended behavior change is because it’s hard to verify this is doing the right thing without know what it’s supposed to do. It seems like dropping fDisconnect = true would no longer cause disconnects but I see from #19107 (comment) that there will still be disconnects because ReceiveMsgBytes will disconnect after readHeader now returns -1.

    Would suggest commit message saying explicitly there is no change in behavior except for logging, that invalid magic bytes cause disconnects earlier in ReceiveMsgBytes, and that invalid header messages are ignored earlier but don’t make flooding easier for whatever reason they don’t make flooding easier

  163. ryanofsky approved
  164. ryanofsky commented at 12:04 pm on September 1, 2020: member
    Code review ACK e72c5b2e27b6ac3a1303b133f551bf2afcff1c0d. I like this change. It definitely seems nicer to just not add invalid messages to the message queue instead of giving them m_valid fields. Feel free to ignore my comments below which are mostly about improving commit messages, but I also do think it would be better not to shoehorn Optional<> in this PR and to keep using msg.m_raw_message_size everywhere instead of adding out_err.
  165. DrahtBot added the label Needs rebase on Sep 7, 2020
  166. troygiorshev force-pushed on Sep 16, 2020
  167. troygiorshev commented at 1:19 pm on September 16, 2020: contributor

    git range-diff master e72c5b2 367bb95

    Rebased

  168. DrahtBot removed the label Needs rebase on Sep 16, 2020
  169. troygiorshev force-pushed on Sep 16, 2020
  170. troygiorshev commented at 4:50 pm on September 16, 2020: contributor

    git range-diff master 367bb95 ead6393

    • s/out_err/out_err_raw_size/ in all spots except for the single prototype in TransportDeserializer
  171. troygiorshev commented at 5:05 pm on September 16, 2020: contributor

    Thanks for the review @jonatack and @ryanofsky!

    I’m going to leave the suggestions for a follow-up PR. As it stands, this PR has three ACKs from people experienced in this part of the codebase. I’d like to reduce the diff that they have to go through.

    • @jnewbery on 9377c0c. The following range-diffs may be useful
      • Rebase: git range-diff master 9377c0c e72c5b2
      • Rebase: git range-diff master e72c5b2 367bb95
      • Rename: git range-diff master 367bb95 ead6393
    • @dongcarl on e72c5b2
      • The rebase and rename immediately above this comment
    • @ryanofsky on e72c5b2
      • The rebase and rename immediately above this comment
  172. ryanofsky approved
  173. ryanofsky commented at 3:10 pm on September 18, 2020: member
    Code review ACK ead639364bd93ece1273de7180a10a669690dbc6. Only changes since last review are rebase with vRecv/msg.m_recv conflict and renaming out_err to out_err_raw_size
  174. DrahtBot added the label Needs rebase on Sep 21, 2020
  175. Give V1TransportDeserializer an m_node_id member
    This is intended to only be used for logging.
    
    This will allow log messages in the following commits to keep recording
    the peer's ID, even when logging is moved into V1TransportDeserializer.
    2716647ebf
  176. Move checksum check from net_processing to net
    This removes the m_valid_checksum member from CNetMessage.  Instead,
    GetMessage() returns an Optional.
    
    Additionally, GetMessage() has been given an out parameter to be used to
    hold error information.  For now it is specifically a uint32_t used to
    hold the raw size of the corrupt message.
    
    The checksum check is now done in GetMessage.
    890b1d7c2b
  177. Add doxygen comment for ReceiveMsgBytes 1ca20c1af8
  178. Change CMessageHeader Constructor
    This commit removes the single-parameter contructor of CMessageHeader
    and replaces it with a default constructor.
    
    The single parameter contructor isn't used anywhere except for tests.
    There is no reason to initialize a CMessageHeader with a particular
    messagestart.  This messagestart should always be replaced when
    deserializing an actual message header so that we can run checks on it.
    
    The default constructor initializes it to zero, just like the command
    and checksum.
    
    This also removes a parameter of a V1TransportDeserializer constructor,
    as it was only used for this purpose.
    5bceef6b12
  179. Give V1TransportDeserializer CChainParams& member
    This adds a CChainParams& member to V1TransportDeserializer member, and
    use it in place of many Params() calls.  In addition to reducing the
    number of calls to a global, this removes a parameter from GetMessage
    (and will later allow us to remove one from CMessageHeader::IsValid())
    52d4ae46ab
  180. Remove header checks out of net_processing
    This moves header size and netmagic checking out of net_processing and
    into net.  This check now runs in ReadHeader, so that net can exit early
    out of receiving bytes from the peer.  IsValid is now slimmed down, so
    it no longer needs a MessageStartChars& parameter.
    
    Additionally this removes the rest of the m_valid_* members from
    CNetMessage.
    deb52711a1
  181. troygiorshev force-pushed on Sep 23, 2020
  182. troygiorshev commented at 2:06 am on September 23, 2020: contributor

    git range-diff master ead6393 deb5271

    Trivial rebase

  183. DrahtBot removed the label Needs rebase on Sep 23, 2020
  184. jnewbery commented at 9:49 am on September 23, 2020: member

    Code review ACK deb52711a17236d0fca302701b5af585341ab42a.

    Should be ready for merge with a reACK from @dongcarl and @ryanofsky

  185. ryanofsky approved
  186. ryanofsky commented at 8:39 pm on September 25, 2020: member
    Code review ACK deb52711a17236d0fca302701b5af585341ab42a just rebase due to conflict on adjacent line
  187. fanquake merged this on Sep 29, 2020
  188. fanquake closed this on Sep 29, 2020

  189. sidhujag referenced this in commit 7f1c584210 on Sep 29, 2020
  190. jnewbery referenced this in commit 43ba90620e on Aug 18, 2021
  191. jnewbery referenced this in commit 8c96008ab1 on Aug 19, 2021
  192. MarcoFalke referenced this in commit 9e3f7dcaa2 on Nov 2, 2021
  193. DrahtBot locked this on Feb 15, 2022

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-01 10:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me