p2p_compactblocks_blocksonly.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2021-2021 The Limenka 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 that a node in blocksonly mode does not request compact blocks."""
6
7 from test_framework.messages import (
8 MSG_BLOCK,
9 MSG_CMPCT_BLOCK,
10 MSG_WITNESS_FLAG,
11 CBlock,
12 CBlockHeader,
13 CInv,
14 from_hex,
15 msg_block,
16 msg_getdata,
17 msg_headers,
18 msg_sendcmpct,
19 )
20 from test_framework.p2p import P2PInterface
21 from test_framework.test_framework import LimenkaTestFramework
22 from test_framework.util import assert_equal
23
24
25 class P2PCompactBlocksBlocksOnly(LimenkaTestFramework):
26 def set_test_params(self):
27 self.extra_args = [["-blocksonly"], [], [], []]
28 self.num_nodes = 4
29
30 def setup_network(self):
31 self.setup_nodes()
32 # Start network with everyone disconnected
33 self.sync_all()
34
35 def build_block_on_tip(self):
36 blockhash = self.generate(self.nodes[2], 1, sync_fun=self.no_op)[0]
37 block_hex = self.nodes[2].getblock(blockhash=blockhash, verbosity=0)
38 block = from_hex(CBlock(), block_hex)
39 block.rehash()
40 return block
41
42 def run_test(self):
43 # Nodes will only request hb compact blocks mode when they're out of IBD
44 for node in self.nodes:
45 assert not node.getblockchaininfo()['initialblockdownload']
46
47 p2p_conn_blocksonly = self.nodes[0].add_p2p_connection(P2PInterface())
48 p2p_conn_high_bw = self.nodes[1].add_p2p_connection(P2PInterface())
49 p2p_conn_low_bw = self.nodes[3].add_p2p_connection(P2PInterface())
50 for conn in [p2p_conn_blocksonly, p2p_conn_high_bw, p2p_conn_low_bw]:
51 assert_equal(conn.message_count['sendcmpct'], 1)
52 conn.send_and_ping(msg_sendcmpct(announce=False, version=2))
53
54 # Nodes:
55 # 0 -> blocksonly
56 # 1 -> high bandwidth
57 # 2 -> miner
58 # 3 -> low bandwidth
59 #
60 # Topology:
61 # p2p_conn_blocksonly ---> node0
62 # p2p_conn_high_bw ---> node1
63 # p2p_conn_low_bw ---> node3
64 # node2 (no connections)
65 #
66 # node2 produces blocks that are passed to the rest of the nodes
67 # through the respective p2p connections.
68
69 self.log.info("Test that -blocksonly nodes do not select peers for BIP152 high bandwidth mode")
70
71 block0 = self.build_block_on_tip()
72
73 # A -blocksonly node should not request BIP152 high bandwidth mode upon
74 # receiving a new valid block at the tip.
75 p2p_conn_blocksonly.send_and_ping(msg_block(block0))
76 assert_equal(int(self.nodes[0].getbestblockhash(), 16), block0.sha256)
77 assert_equal(p2p_conn_blocksonly.message_count['sendcmpct'], 1)
78 assert_equal(p2p_conn_blocksonly.last_message['sendcmpct'].announce, False)
79
80 # A normal node participating in transaction relay should request BIP152
81 # high bandwidth mode upon receiving a new valid block at the tip.
82 p2p_conn_high_bw.send_and_ping(msg_block(block0))
83 assert_equal(int(self.nodes[1].getbestblockhash(), 16), block0.sha256)
84 p2p_conn_high_bw.wait_until(lambda: p2p_conn_high_bw.message_count['sendcmpct'] == 2)
85 assert_equal(p2p_conn_high_bw.last_message['sendcmpct'].announce, True)
86
87 # Don't send a block from the p2p_conn_low_bw so the low bandwidth node
88 # doesn't select it for BIP152 high bandwidth relay.
89 self.nodes[3].submitblock(block0.serialize().hex())
90
91 self.log.info("Test that -blocksonly nodes send getdata(BLOCK) instead"
92 " of getdata(CMPCT) in BIP152 low bandwidth mode")
93
94 block1 = self.build_block_on_tip()
95
96 p2p_conn_blocksonly.send_message(msg_headers(headers=[CBlockHeader(block1)]))
97 p2p_conn_blocksonly.sync_with_ping()
98 assert_equal(p2p_conn_blocksonly.last_message['getdata'].inv, [CInv(MSG_BLOCK | MSG_WITNESS_FLAG, block1.sha256)])
99
100 p2p_conn_high_bw.send_message(msg_headers(headers=[CBlockHeader(block1)]))
101 p2p_conn_high_bw.sync_with_ping()
102 assert_equal(p2p_conn_high_bw.last_message['getdata'].inv, [CInv(MSG_CMPCT_BLOCK, block1.sha256)])
103
104 self.log.info("Test that getdata(CMPCT) is still sent on BIP152 low bandwidth connections"
105 " when no -blocksonly nodes are involved")
106
107 p2p_conn_low_bw.send_and_ping(msg_headers(headers=[CBlockHeader(block1)]))
108 p2p_conn_low_bw.sync_with_ping()
109 assert_equal(p2p_conn_low_bw.last_message['getdata'].inv, [CInv(MSG_CMPCT_BLOCK, block1.sha256)])
110
111 self.log.info("Test that -blocksonly nodes still serve compact blocks")
112
113 def test_for_cmpctblock(block):
114 if 'cmpctblock' not in p2p_conn_blocksonly.last_message:
115 return False
116 return p2p_conn_blocksonly.last_message['cmpctblock'].header_and_shortids.header.rehash() == block.sha256
117
118 p2p_conn_blocksonly.send_message(msg_getdata([CInv(MSG_CMPCT_BLOCK, block0.sha256)]))
119 p2p_conn_blocksonly.wait_until(lambda: test_for_cmpctblock(block0))
120
121 # Request BIP152 high bandwidth mode from the -blocksonly node.
122 p2p_conn_blocksonly.send_and_ping(msg_sendcmpct(announce=True, version=2))
123
124 block2 = self.build_block_on_tip()
125 self.nodes[0].submitblock(block1.serialize().hex())
126 self.nodes[0].submitblock(block2.serialize().hex())
127 p2p_conn_blocksonly.wait_until(lambda: test_for_cmpctblock(block2))
128
129 if __name__ == '__main__':
130 P2PCompactBlocksBlocksOnly(__file__).main()
131