p2p_compactblocks_blocksonly.py
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 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 BitcoinTestFramework 22 from test_framework.util import assert_equal 23 24 25 class P2PCompactBlocksBlocksOnly(BitcoinTestFramework): 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 return block 40 41 def run_test(self): 42 # Nodes will only request hb compact blocks mode when they're out of IBD 43 for node in self.nodes: 44 assert not node.getblockchaininfo()['initialblockdownload'] 45 46 p2p_conn_blocksonly = self.nodes[0].add_p2p_connection(P2PInterface()) 47 p2p_conn_high_bw = self.nodes[1].add_p2p_connection(P2PInterface()) 48 p2p_conn_low_bw = self.nodes[3].add_p2p_connection(P2PInterface()) 49 for conn in [p2p_conn_blocksonly, p2p_conn_high_bw, p2p_conn_low_bw]: 50 assert_equal(conn.message_count['sendcmpct'], 1) 51 conn.send_and_ping(msg_sendcmpct(announce=False, version=2)) 52 53 # Nodes: 54 # 0 -> blocksonly 55 # 1 -> high bandwidth 56 # 2 -> miner 57 # 3 -> low bandwidth 58 # 59 # Topology: 60 # p2p_conn_blocksonly ---> node0 61 # p2p_conn_high_bw ---> node1 62 # p2p_conn_low_bw ---> node3 63 # node2 (no connections) 64 # 65 # node2 produces blocks that are passed to the rest of the nodes 66 # through the respective p2p connections. 67 68 self.log.info("Test that -blocksonly nodes do not select peers for BIP152 high bandwidth mode") 69 70 block0 = self.build_block_on_tip() 71 72 # A -blocksonly node should not request BIP152 high bandwidth mode upon 73 # receiving a new valid block at the tip. 74 p2p_conn_blocksonly.send_and_ping(msg_block(block0)) 75 assert_equal(self.nodes[0].getbestblockhash(), block0.hash_hex) 76 assert_equal(p2p_conn_blocksonly.message_count['sendcmpct'], 1) 77 assert_equal(p2p_conn_blocksonly.last_message['sendcmpct'].announce, False) 78 79 # A normal node participating in transaction relay should request BIP152 80 # high bandwidth mode upon receiving a new valid block at the tip. 81 p2p_conn_high_bw.send_and_ping(msg_block(block0)) 82 assert_equal(self.nodes[1].getbestblockhash(), block0.hash_hex) 83 p2p_conn_high_bw.wait_until(lambda: p2p_conn_high_bw.message_count['sendcmpct'] == 2) 84 assert_equal(p2p_conn_high_bw.last_message['sendcmpct'].announce, True) 85 86 # Don't send a block from the p2p_conn_low_bw so the low bandwidth node 87 # doesn't select it for BIP152 high bandwidth relay. 88 self.nodes[3].submitblock(block0.serialize().hex()) 89 90 self.log.info("Test that -blocksonly nodes send getdata(BLOCK) instead" 91 " of getdata(CMPCT) in BIP152 low bandwidth mode") 92 93 block1 = self.build_block_on_tip() 94 95 p2p_conn_blocksonly.send_and_ping(msg_headers(headers=[CBlockHeader(block1)])) 96 assert_equal(p2p_conn_blocksonly.last_message['getdata'].inv, [CInv(MSG_BLOCK | MSG_WITNESS_FLAG, block1.hash_int)]) 97 98 p2p_conn_high_bw.send_and_ping(msg_headers(headers=[CBlockHeader(block1)])) 99 assert_equal(p2p_conn_high_bw.last_message['getdata'].inv, [CInv(MSG_CMPCT_BLOCK, block1.hash_int)]) 100 101 self.log.info("Test that getdata(CMPCT) is still sent on BIP152 low bandwidth connections" 102 " when no -blocksonly nodes are involved") 103 104 p2p_conn_low_bw.send_and_ping(msg_headers(headers=[CBlockHeader(block1)])) 105 assert_equal(p2p_conn_low_bw.last_message['getdata'].inv, [CInv(MSG_CMPCT_BLOCK, block1.hash_int)]) 106 107 self.log.info("Test that -blocksonly nodes still serve compact blocks") 108 109 def test_for_cmpctblock(block): 110 if 'cmpctblock' not in p2p_conn_blocksonly.last_message: 111 return False 112 return p2p_conn_blocksonly.last_message['cmpctblock'].header_and_shortids.header.hash_int == block.hash_int 113 114 p2p_conn_blocksonly.send_without_ping(msg_getdata([CInv(MSG_CMPCT_BLOCK, block0.hash_int)])) 115 p2p_conn_blocksonly.wait_until(lambda: test_for_cmpctblock(block0)) 116 117 # Request BIP152 high bandwidth mode from the -blocksonly node. 118 p2p_conn_blocksonly.send_and_ping(msg_sendcmpct(announce=True, version=2)) 119 120 block2 = self.build_block_on_tip() 121 self.nodes[0].submitblock(block1.serialize().hex()) 122 self.nodes[0].submitblock(block2.serialize().hex()) 123 p2p_conn_blocksonly.wait_until(lambda: test_for_cmpctblock(block2)) 124 125 if __name__ == '__main__': 126 P2PCompactBlocksBlocksOnly(__file__).main()