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