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