/ test / functional / p2p_compactblocks_blocksonly.py
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()