net, test: invalid p2p messages and test framework improvements #19272

pull jonatack wants to merge 5 commits into bitcoin:master from jonatack:improve-p2p_invalid_messages changing 5 files +56 −37
  1. jonatack commented at 11:48 am on June 14, 2020: member

    …seen while reviewing #19264, #19252, #19304 and #19107:

    in net_processing.cpp

    • make the debug logging for oversized message size misbehavior the same for addr, getdata, headers and inv messages

    in p2p_invalid_messages

    • add missing logging
    • improve assertions/message sends, move cleanup disconnections outside the assertion scopes
    • split a slowish 3-part test into 3 order-independent tests
    • add a few p2p constants to the test framework
  2. in test/functional/p2p_invalid_messages.py:106 in bc42270f21 outdated
    112-        with self.nodes[0].assert_debug_log(['Misbehaving', 'peer=4 (40 -> 60): headers message size = 2001']):
    113-            msg = msg_headers([CBlockHeader()] * 2001)
    114-            conn.send_and_ping(msg)
    115+    def test_oversized(self, msg_type, msg, size):
    116+        self.log.info("Test {} message of size {} is logged as misbehaving".format(msg_type, size))
    117+        with self.nodes[0].assert_debug_log(['Misbehaving', msg_type, str(size)]):
    


    MarcoFalke commented at 11:59 am on June 14, 2020:
    splitting up msg_type and size could match them on different lines, while we want to match both on the same line

    jonatack commented at 7:47 pm on June 14, 2020:
    Good point! Fixed. This made it apparent that in net_processing.cpp the debug logging for misbehaving from headers message size was a bit different from the equivalent misbehavior logging for inv, getdata, and addr message sizes. Fixed. If we don’t want to touch it, we can also have each test pass in their own debug log message to test for.
  3. DrahtBot added the label Tests on Jun 14, 2020
  4. DrahtBot commented at 6:01 pm on June 14, 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:

    • #19145 (Add hash_type options for gettxoutsetinfo by fjahr)
    • #19107 (p2p: Refactor, move all header verification into the network layer, without changing behavior by troygiorshev)
    • #18642 (Use std::chrono for the time to rotate destination of addr messages + tests by naumenkogs)

    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.

  5. jonatack force-pushed on Jun 14, 2020
  6. in test/functional/p2p_invalid_messages.py:135 in 6b18bfefed outdated
    145         for _ in range(80):
    146             conn.send_message(msg_at_size)
    147 
    148         # Check that, even though the node is being hammered by nonsense from one
    149         # connection, it can still service other peers in a timely way.
    150+        self.log.info("(b) Check node still services peer in a timely way")
    


    troygiorshev commented at 1:18 am on June 16, 2020:

    Maybe this should specify “other peers”? As it stands someone may miss the difference between conn and conn2.

    0        self.log.info("(b) Check node still services other peers in a timely way")
    

    jonatack commented at 8:08 am on June 16, 2020:
    Done.
  7. troygiorshev commented at 2:51 am on June 16, 2020: contributor
    ACK 6b18bfefed402a904db45a19b9cd8c1cb5d4c337
  8. troygiorshev approved
  9. troygiorshev commented at 2:52 am on June 16, 2020: contributor
    Reviewed and ran all tests. Just one suggestion if you plan on making any changes.
  10. jonatack force-pushed on Jun 16, 2020
  11. jonatack commented at 8:10 am on June 16, 2020: member
    Thanks for reviewing @troygiorshev. Took your suggestion and also removed an unnecessary disconnect_p2ps() call in test_oversized_msg().
  12. glozow commented at 10:19 pm on June 16, 2020: member
    Concept ACK Mentioned this elsewhere but my edit is already outdated. Small suggestion for p2p_invalid_messages.py, feel free to ignore: If you replace the 3 (specifically, test_checksum, test_size, and test_msgtype) self.nodes[0].p2p with conn, all of the subtest disconnect_p2ps can also be removed. The only reason they need it now is to clear p2ps.
  13. jonatack force-pushed on Jun 17, 2020
  14. jonatack commented at 10:14 am on June 17, 2020: member
    Thank you for reviewing, @gzhao408. Took your suggestion and credited you as co-author in the commit.
  15. jnewbery commented at 3:57 pm on June 17, 2020: member
    I don’t think we should be removing the disconnect_p2ps calls at the end of each subtest. Ideally the subtest finishes by leaving the node and fixture in as close a state to they were in originally. See #19304 (review)
  16. glozow commented at 4:53 pm on June 17, 2020: member

    @jnewbery good point…

    Ideally the subtest finishes by leaving the node and fixture in as close a state to they were in originally

    In terms of state, I’m thinking state = (1) the test_node.p2ps array and (2) the connections between the node and its peers.

    (1) I think it’s ok if the array has an extra entry if we’re not using the .p2p property? Would you agree? If not, then yeah, we need to put disconnect_p2ps() back 😄 .

    (2) For connections: Definitely agree that subtests should disconnect peers to clean up after themselves. Either node.disconnect_p2ps or peer.peer_disconnect Some of the subtests call wait_for_disconnect which makes sure that they are disconnected from the peer’s POV (no peer._transport). With #19252,disconnect_p2ps waits for node’s POV (not in node.getpeerinfo()).

    I’m not sure if disconnected from peer’s POV == disconnected from the node’s POV. If they’re effectively the same, I’d say it’s unnecessary to call disconnect_p2ps after wait_for_disconnect.

    I might be overthinking this haha, I dug into this when I thought superfluous disconnect_p2ps would cause performance problems 😅 .

  17. jonatack commented at 4:53 am on June 18, 2020: member

    AFAICT the tests are order-independent with this PR, and I checked that this passes when rebased to current master:

     0+from test_framework.util import assert_equal
     1  
     2+    def assert_no_connected_peers(self):
     3+        assert_equal(self.nodes[0].num_connected_mininodes(), 0)
     4+
     5     def run_test(self):
     6         self.test_magic_bytes()
     7+        self.assert_no_connected_peers()
     8         self.test_checksum()
     9+        self.assert_no_connected_peers()
    10         self.test_size()
    11+        self.assert_no_connected_peers()
    12         self.test_msgtype()
    13+        self.assert_no_connected_peers()
    14         self.test_oversized_inv_msg()
    15+        self.assert_no_connected_peers()
    16         self.test_oversized_getdata_msg()
    17+        self.assert_no_connected_peers()
    18         self.test_oversized_headers_msg()
    19+        self.assert_no_connected_peers()
    20         self.test_resource_exhaustion()
    21+        self.assert_no_connected_peers()
    
  18. jonatack force-pushed on Jun 18, 2020
  19. jonatack commented at 5:14 am on June 18, 2020: member
    Anyway, removed the removal of disconnect_p2ps, and rebased to master to be sure all is well with the latest version of disconnect_p2ps. Edit: Improved the logging and the assertions with new ideas after reviewing #19107.
  20. jonatack force-pushed on Jun 18, 2020
  21. MarcoFalke referenced this in commit 343c0bfbf1 on Jun 18, 2020
  22. jonatack commented at 11:47 am on June 18, 2020: member
    @MarcoFalke several PRs on this test have been merged but no approval from you on this one. I think they are good improvements but feel free to close it if you disagree; it will save me from spinning my wheels.
  23. MarcoFalke commented at 11:56 am on June 18, 2020: member

    I generally use the conflicts list of DrahtBot to see which pr has the most reviews and is the most ready to merge and then get them in one by one if they have received their ACKs.

    Will take another look here. One sec

  24. in test/functional/p2p_invalid_messages.py:46 in efb9a95272 outdated
    42@@ -39,19 +43,22 @@ def __repr__(self):
    43 class InvalidMessagesTest(BitcoinTestFramework):
    44     def set_test_params(self):
    45         self.num_nodes = 1
    46-        self.setup_clean_chain = True
    47+        self.setup_clean_chain = False
    


    MarcoFalke commented at 12:02 pm on June 18, 2020:
    why is this changed?

    jonatack commented at 12:08 pm on June 18, 2020:
    i thought it was faster but can revert that change while rebasing

    MarcoFalke commented at 12:09 pm on June 18, 2020:
    why would it be faster to copy the blocksdir as opposed to copy nothing?

    jonatack commented at 12:55 pm on June 18, 2020:
    I (mis)understood(?) the discussions to say that using the cached chain was faster than spinning up a new one. Reverted.

    MarcoFalke commented at 12:58 pm on June 18, 2020:

    There is no new chain generated in this test:

    0$ git grep generate 'test/functional/p2p_invalid_messages.py'
    1$
    
  25. in test/functional/p2p_invalid_messages.py:27 in efb9a95272 outdated
    21@@ -19,7 +22,8 @@
    22 from test_framework.test_framework import BitcoinTestFramework
    23 
    24 MSG_LIMIT = 4 * 1000 * 1000  # 4MB, per MAX_PROTOCOL_MESSAGE_LENGTH
    


    MarcoFalke commented at 12:02 pm on June 18, 2020:
    0MSG_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH
    

    jonatack commented at 12:09 pm on June 18, 2020:
    Good eye; that’s an error introduced this morning in my last rebase, will fix

    jonatack commented at 12:52 pm on June 18, 2020:
    Removed the line.
  26. MarcoFalke commented at 12:04 pm on June 18, 2020: member
    Fine with me
  27. jonatack force-pushed on Jun 18, 2020
  28. jonatack commented at 12:57 pm on June 18, 2020: member

    Rebased for merged #19304, reverted the chain setting, and removed a line left over from the last change (thanks Marco).

    0-MSG_LIMIT = 4 * 1000 * 1000  # 4MB, per MAX_PROTOCOL_MESSAGE_LENGTH
    1VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5  # Account for the 5-byte length prefix
    2
    3-        self.setup_clean_chain = False
    4+        self.setup_clean_chain = True
    
  29. in src/net_processing.cpp:3248 in 255fbcaee5 outdated
    3244@@ -3245,7 +3245,7 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
    3245         unsigned int nCount = ReadCompactSize(vRecv);
    3246         if (nCount > MAX_HEADERS_RESULTS) {
    3247             LOCK(cs_main);
    3248-            Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
    3249+            Misbehaving(pfrom.GetId(), 20, strprintf("message headers size() = %u", nCount));
    


    jnewbery commented at 3:21 pm on June 18, 2020:
    I don’t understand this change. This is a “headers” message, so the original log seems correct - that it’s a headers message of size x.

    jonatack commented at 3:39 pm on June 18, 2020:
    I don’t disagree but opted for the smallest change. Do you think the inv, getdata, and addr oversized messages misbehavior logging should be changed instead? Or are they all good as-is (in which case the headers one remains different, and each specific one can be passed into the test_oversized_msg() assertion function from the caller.)

    jnewbery commented at 3:49 pm on June 18, 2020:
    ah ok, it’s to be consistent with the other logs. That’s fine.

    troygiorshev commented at 3:50 pm on June 18, 2020:

    IMO they should all be changed to <type> message size = %u. <type> message because that sounds better to my ear, and size as opposed to size() because we’re talking about the value of the function, not the function itself.

    That said, I’ll ACK anything that’s consistent among these 4 logs.


    jonatack commented at 4:55 pm on June 18, 2020:
    @troygiorshev I share your preference but opted for the minimum possible change. Given both of your reviews will try the changes you suggest.

    jonatack commented at 5:26 pm on June 18, 2020:
    Done.
  30. in test/functional/p2p_invalid_messages.py:156 in 255fbcaee5 outdated
    173+        assert len(msg_at_size.serialize()) == MAX_PROTOCOL_MESSAGE_LENGTH
    174 
    175-        self.log.info("Sending a bunch of large, junk messages to test memory exhaustion. May take a bit...")
    176-
    177-        # Run a bunch of times to test for memory exhaustion.
    178+        self.log.info("(a) Send 80 messages of max data size 4MB to test for memory exhaustion")
    


    jnewbery commented at 3:23 pm on June 18, 2020:
    This comment “test for memory exhaustion” is no longer relevant. When the test was written, it tested the node’s memory usage before and after sending the messages and asserted that it hadn’t gone up. That was an unreliable test and so the memory checking was removed.

    jonatack commented at 3:31 pm on June 18, 2020:
    Thanks @jnewbery, what change would you suggest instead?

    jonatack commented at 3:42 pm on June 18, 2020:
    We could just remove “to test for memory exhaustion” but saying why could be good here.

    jnewbery commented at 3:49 pm on June 18, 2020:
    Yes, I suggest removing “to test for memory exhaustion” because the test isn’t doing that.

    jonatack commented at 4:47 pm on June 18, 2020:
    Thanks – done.
  31. in test/functional/p2p_invalid_messages.py:31 in 255fbcaee5 outdated
    26@@ -24,8 +27,8 @@
    27     wait_until,
    28 )
    29 
    30-MSG_LIMIT = 4 * 1000 * 1000  # 4MB, per MAX_PROTOCOL_MESSAGE_LENGTH
    31-VALID_DATA_LIMIT = MSG_LIMIT - 5  # Account for the 5-byte length prefix
    32+VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5  # Account for the 5-byte length prefix
    33+DISCONNECTING = 'disconnecting peer='
    


    jnewbery commented at 3:25 pm on June 18, 2020:
    My preference is not to test the debug log for actions that we can test directly. Here, we can directly test that the peer has been disconnected, so I don’t think we need to test for this message in the debug log.

    jonatack commented at 3:35 pm on June 18, 2020:
    I added this after noticing both here and in the #19107 that some of the tests aren’t testing for the disconnection, and it’s a free additional check. If a change made one of these disconnections no longer happen or be logged without warning, and no test needing to be changed, ISTM that could be a regression.

    jnewbery commented at 3:42 pm on June 18, 2020:
    Right - I think the fix is to test for disconnection, not to test for the log message that accompanies disconnection.

    jonatack commented at 4:11 pm on June 18, 2020:
    Ok, in 4 cases it was only logging the disconnection because disconnect_p2ps() was previously being called in the scope of the assert_debug_log. Updating along with my review feedback in #19107.

    jonatack commented at 4:51 pm on June 18, 2020:
    Done – good call, thanks.
  32. jnewbery commented at 3:25 pm on June 18, 2020: member
    Nice changes, Jon. I’ve left a few minor comments inline.
  33. jonatack force-pushed on Jun 18, 2020
  34. jonatack force-pushed on Jun 18, 2020
  35. jonatack renamed this:
    test: p2p_invalid_messages and test framework improvements
    net, test: p2p_invalid_messages and test framework improvements
    on Jun 18, 2020
  36. jonatack renamed this:
    net, test: p2p_invalid_messages and test framework improvements
    net, test: invalid p2p messages and test framework improvements
    on Jun 18, 2020
  37. troygiorshev commented at 5:44 pm on June 18, 2020: contributor

    reACK ca658361c7e6a137d1ee8be8a005b35ad0426fa3

    Reviewed and stepped through each test. Verified that none of these changes caused linting errors.

  38. in test/functional/p2p_invalid_messages.py:127 in cb71adbf93 outdated
    134-            msg = msg_getdata([CInv(MSG_TX, 1)] * 50001)
    135-            conn.send_and_ping(msg)
    136-        with self.nodes[0].assert_debug_log(['Misbehaving', '(40 -> 60): headers message size = 2001']):
    137-            msg = msg_headers([CBlockHeader()] * 2001)
    138-            conn.send_and_ping(msg)
    139+    def test_oversized_msg(self, msg_type, msg, size):
    


    MarcoFalke commented at 5:54 pm on June 18, 2020:

    in commit cb71adbf9338c8a3f8348c936d4c1caa1086a33f:

    no need to pass the message type, since the message already knows its own type:

     0diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
     1index f1ef5c6414..ad918bf3bb 100755
     2--- a/test/functional/p2p_invalid_messages.py
     3+++ b/test/functional/p2p_invalid_messages.py
     4@@ -124,7 +124,8 @@ class InvalidMessagesTest(BitcoinTestFramework):
     5             conn.sync_with_ping(timeout=1)
     6             self.nodes[0].disconnect_p2ps()
     7 
     8-    def test_oversized_msg(self, msg_type, msg, size):
     9+    def test_oversized_msg(self, _, msg, size):
    10+        msg_type = msg.msgtype.decode('ascii')
    11         self.log.info("Test {} message of size {} is logged as misbehaving".format(msg_type, size))
    12         with self.nodes[0].assert_debug_log(['Misbehaving', '{} message size = {}'.format(msg_type, size)]):
    13             self.nodes[0].add_p2p_connection(P2PInterface()).send_and_ping(msg)
    

    jonatack commented at 8:23 pm on June 18, 2020:
    Nice; done.
  39. in test/functional/p2p_invalid_messages.py:83 in 058dbcec56 outdated
    79@@ -80,49 +80,49 @@ def test_buffer(self):
    80     def test_magic_bytes(self):
    81         self.log.info("Test message with invalid magic bytes disconnects peer")
    82         conn = self.nodes[0].add_p2p_connection(P2PDataStore())
    83-        with self.nodes[0].assert_debug_log(['PROCESSMESSAGE: INVALID MESSAGESTART badmsg']):
    84+        with self.nodes[0].assert_debug_log('PROCESSMESSAGE: INVALID MESSAGESTART badmsg'):
    


    MarcoFalke commented at 5:56 pm on June 18, 2020:

    in commit 058dbcec567eb5b6fa780f0adbbd70b421621c38

    This must stay an array. Otherwise it will assert nothing.


    jonatack commented at 8:22 pm on June 18, 2020:

    Thanks, done. I should have verified the method. Am in the long-standing habit of accepting either a string or an array in framework APIs I make, e.g.

    0     def assert_debug_log(self, expected_msgs, unexpected_msgs=None, timeout=2):
    1+        if type(expected_msgs) == str:
    2+            expected_msgs = [expected_msgs]
    3+        if type(unexpected_msgs) == str:
    4+            unexpected_msgs = [unexpected_msgs]
    5         if unexpected_msgs is None:
    6             unexpected_msgs = []
    

    troygiorshev commented at 9:11 pm on June 18, 2020:

    MarcoFalke commented at 10:52 am on June 19, 2020:
    I did try to add : List[str], but for some reason it didn’t complain when I passed a string. Maybe mypy was broken on my machine?
  40. MarcoFalke changes_requested
  41. MarcoFalke commented at 6:13 pm on June 18, 2020: member
    c6e7519647ef8736aaa5d8e7f804d0340a36029a
  42. jonatack force-pushed on Jun 18, 2020
  43. jonatack force-pushed on Jun 18, 2020
  44. troygiorshev commented at 9:11 pm on June 18, 2020: contributor
    reACK 98af0f02055409af7f3d1a5df06d315f36b0b856
  45. DrahtBot added the label Needs rebase on Jun 19, 2020
  46. net: update misbehavior logging for oversized messages
    so that oversized ADDR, GETDATA, HEADERS and INV messages print
    the same consistent debug logs.
    9fa494dc09
  47. test: add p2p_invalid_messages logging e2b21d8a59
  48. test: refactor test_large_inv() into 3 tests with common method 57960192a5
  49. test: improve msg sends and p2p disconnections in p2p_invalid_messages
    - call disconnect_p2ps() outside of the assert_debug_log scopes
    - send messages directly from the p2p conn rather than via nodes[0].p2p
    - add an assertion
    75447f0893
  50. test: hoist p2p values to test framework constants 56010f9256
  51. jonatack force-pushed on Jun 19, 2020
  52. DrahtBot removed the label Needs rebase on Jun 19, 2020
  53. jonatack commented at 2:10 pm on June 22, 2020: member
    Rebased, no changes. Thank you for ACKing @troygiorshev! Should be trivial to re-ack with git range-diff 8ef15e8 98af0f0 56010f9
  54. jonatack requested review from MarcoFalke on Jun 23, 2020
  55. troygiorshev commented at 1:41 am on June 24, 2020: contributor
    reACK 56010f92564a94b0ca6c008c0e6f74a19fad4a2a
  56. in test/functional/p2p_invalid_messages.py:30 in 56010f9256
    26@@ -24,8 +27,7 @@
    27     wait_until,
    28 )
    29 
    30-MSG_LIMIT = 4 * 1000 * 1000  # 4MB, per MAX_PROTOCOL_MESSAGE_LENGTH
    31-VALID_DATA_LIMIT = MSG_LIMIT - 5  # Account for the 5-byte length prefix
    32+VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5  # Account for the 5-byte length prefix
    


    rajarshimaitra commented at 2:24 pm on June 24, 2020:
    Probably I am missing something, but this 5 bytes message length seems to be coming from ser_string() in messages.py. But is it something that the node also recognizes? It doesn’t seem to be part of the p2p protocol.

    jonatack commented at 11:33 am on June 25, 2020:

    We set VALID_DATA_LIMIT so that the length of the serialised msg_unrecognized message built with it will equal MAX_PROTOCOL_MESSAGE_LENGTH. This is verified at line 153; you can also add the following assertion at line 111

    0             msg = msg_unrecognized(str_data="d" * (VALID_DATA_LIMIT + 1))
    1+            assert_equal(len(msg.serialize()), MAX_PROTOCOL_MESSAGE_LENGTH + 1)
    

    or try changing the offset from 5 to 4 or 6.

    I’ve added this assertion and some other ones while testing @troygiorshev’s net message headers refactoring and may propose the changes if they look worthwhile to me after a few days’ time.

  57. in test/functional/p2p_invalid_messages.py:155 in 56010f9256
    174-
    175-        self.log.info("Sending a bunch of large, junk messages to test memory exhaustion. May take a bit...")
    176+        assert len(msg_at_size.serialize()) == MAX_PROTOCOL_MESSAGE_LENGTH
    177 
    178-        # Run a bunch of times to test for memory exhaustion.
    179+        self.log.info("(a) Send 80 messages, each of maximum valid data size (4MB)")
    


    rajarshimaitra commented at 2:32 pm on June 24, 2020:
    Nit: “Sending 80 junk messages…”. makes the log a bit more clear.

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

    “junk messages” is stated in the log immediately beforehand:

    02020-06-25 ... TestFramework (INFO): Test node stays up despite many large junk messages
    12020-06-25 ... TestFramework (INFO): (a) Send 80 messages, each of maximum valid data size (4MB)
    22020-06-25 ... TestFramework (INFO): (b) Check node still services peers in a timely way
    32020-06-25 ... TestFramework (INFO): (c) Wait for node to drop junk messages, while remaining connected
    
  58. rajarshimaitra commented at 3:46 pm on June 24, 2020: contributor
    Concept ACK. Nice changes, easy to review. Compiled without issue, verified all unit and functions tests passing. Below are a nonblocking nit and a question for explanation.
  59. MarcoFalke commented at 7:54 pm on June 24, 2020: member

    ACK 56010f9256 🎛

    Signature:

     0-----BEGIN PGP SIGNED MESSAGE-----
     1Hash: SHA512
     2
     3ACK 56010f9256 🎛
     4-----BEGIN PGP SIGNATURE-----
     5
     6iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
     7pUicWwv+KIsi9o22/6uZbUTRM0UsyIPesQTV0ZcUzpXX8Sz27WY/psDv60xlD3+W
     8eUGgldLLYL1EBH8wWRbyMWsQ4WFQJILWXl6+8Cr2b8Lv9N1NhJ/z+u+FLfHHMlXt
     9dRxOMXXsytLgH8g3eW8f7zWbH0RaHtre+gcBiYqWAEbzq4i8D8h4lEO8ggA5c9TC
    10iGgj4ACk04YvOXDTOOgfF4DJQXIn3CuHrh7y55zo5X7+hZp43YEDRaVtKnggLTiM
    11dXZ3r3p+6i4Wqn2uFiZuw06rJN6PtSPEcBa8T3dmGVHixzPlXV5W3aILjU9TUGSV
    12tMoysA6J0N/RPZVEL0pucUOjrJPB3l6ULSEnl4anjC9rvURvaWJogqwJ5Fa/73Tq
    13OAFMkcO5iZod95zCDr6Alt34VI7nsJ//rej6/qpkeOSNc6vhQFbuSPaj5QNo/2Sc
    14BF2ySsHCMluT0JrkGY8EYvMUJka5cO7D894tRvaudfhn6wWT2mNc/E+95LTsGflG
    15EWs3YKPd
    16=Yt/T
    17-----END PGP SIGNATURE-----
    

    Timestamp of file with hash 937d2e5bea5f5ff38ae343e4664be89319c7ac06458b75aca1a579bbd3abbcd1 -

  60. MarcoFalke merged this on Jun 24, 2020
  61. MarcoFalke closed this on Jun 24, 2020

  62. jonatack deleted the branch on Jun 25, 2020
  63. deadalnix referenced this in commit 21970f454c on May 17, 2021
  64. 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 13:12 UTC

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