p2p_compactblocks_blocksonly.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2021-2021 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 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().main()