1 #!/usr/bin/env python3
2 # Copyright (c) 2021-present The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test compact blocks HB selection logic."""
6 7 from test_framework.test_framework import BitcoinTestFramework
8 from test_framework.util import assert_equal
9 10 11 class CompactBlocksConnectionTest(BitcoinTestFramework):
12 """Test class for verifying selection of HB peer connections."""
13 14 def set_test_params(self):
15 self.setup_clean_chain = True
16 self.num_nodes = 6
17 18 def peer_info(self, from_node, to_node):
19 """Query from_node for its getpeerinfo about to_node."""
20 for peerinfo in self.nodes[from_node].getpeerinfo():
21 if "(testnode%i)" % to_node in peerinfo['subver']:
22 return peerinfo
23 return None
24 25 def setup_network(self):
26 self.setup_nodes()
27 # Start network with everyone disconnected
28 self.sync_all()
29 30 def relay_block_through(self, peer):
31 """Relay a new block through peer peer, and return HB status between 1 and [2,3,4,5]."""
32 self.connect_nodes(peer, 0)
33 self.generate(self.nodes[0], 1)
34 self.disconnect_nodes(peer, 0)
35 36 def status_to():
37 return [self.peer_info(1, i)['bip152_hb_to'] for i in range(2, 6)]
38 39 def status_from():
40 return [self.peer_info(i, 1)['bip152_hb_from'] for i in range(2, 6)]
41 42 self.wait_until(lambda: status_to() == status_from())
43 return status_to()
44 45 def run_test(self):
46 self.log.info("Testing reserved high-bandwidth mode slot for outbound peer...")
47 48 # Connect everyone to node 0, and mine some blocks to get all nodes out of IBD.
49 for i in range(1, 6):
50 self.connect_nodes(i, 0)
51 self.generate(self.nodes[0], 2)
52 for i in range(1, 6):
53 self.disconnect_nodes(i, 0)
54 55 # Construct network topology:
56 # - Node 0 is the block producer
57 # - Node 1 is the "target" node being tested
58 # - Nodes 2-5 are intermediaries.
59 # - Node 1 has an outbound connection to node 2
60 # - Node 1 has inbound connections from nodes 3-5
61 self.connect_nodes(3, 1)
62 self.connect_nodes(4, 1)
63 self.connect_nodes(5, 1)
64 self.connect_nodes(1, 2)
65 66 # Mine blocks subsequently relaying through nodes 3,4,5 (inbound to node 1)
67 for nodeid in range(3, 6):
68 status = self.relay_block_through(nodeid)
69 assert_equal(status, [False, nodeid >= 3, nodeid >= 4, nodeid >= 5])
70 71 # And again through each. This should not change HB status.
72 for nodeid in range(3, 6):
73 status = self.relay_block_through(nodeid)
74 assert_equal(status, [False, True, True, True])
75 76 # Now relay one block through peer 2 (outbound from node 1), so it should take HB status
77 # from one of the inbounds.
78 status = self.relay_block_through(2)
79 assert_equal(status[0], True)
80 assert_equal(sum(status), 3)
81 82 # Now relay again through nodes 3,4,5. Since 2 is outbound, it should remain HB.
83 for nodeid in range(3, 6):
84 status = self.relay_block_through(nodeid)
85 assert status[0]
86 assert status[nodeid - 2]
87 assert_equal(sum(status), 3)
88 89 # Reconnect peer 2, and retry. Now the three inbounds should be HB again.
90 self.disconnect_nodes(1, 2)
91 self.connect_nodes(1, 2)
92 for nodeid in range(3, 6):
93 status = self.relay_block_through(nodeid)
94 assert not status[0]
95 assert status[nodeid - 2]
96 assert_equal(status, [False, True, True, True])
97 98 99 if __name__ == '__main__':
100 CompactBlocksConnectionTest(__file__).main()
101