p2p_v2_encrypted.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2022 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  """
   6  Test encrypted v2 p2p proposed in BIP 324
   7  """
   8  from test_framework.blocktools import (
   9      create_block,
  10      create_coinbase,
  11  )
  12  from test_framework.p2p import (
  13      P2PDataStore,
  14      P2PInterface,
  15  )
  16  from test_framework.test_framework import LimenkaTestFramework
  17  from test_framework.util import (
  18      assert_equal,
  19      assert_greater_than,
  20      check_node_connections,
  21      p2p_port,
  22  )
  23  from test_framework.crypto.chacha20 import REKEY_INTERVAL
  24  from test_framework.socks5 import Socks5Configuration, Socks5Server
  25  
  26  
  27  class P2PEncrypted(LimenkaTestFramework):
  28      def set_test_params(self):
  29          self.num_nodes = 2
  30          self.extra_args = [["-v2transport=1"], ["-v2transport=1"]]
  31  
  32      def setup_network(self):
  33          self.setup_nodes()
  34  
  35      def generate_blocks(self, node, number):
  36          test_blocks = []
  37          last_block = node.getbestblockhash()
  38          tip = int(last_block, 16)
  39          tipheight = node.getblockcount()
  40          last_block_time = node.getblock(last_block)['time']
  41          for _ in range(number):
  42              # Create some blocks
  43              block = create_block(tip, create_coinbase(tipheight + 1), last_block_time + 1)
  44              block.solve()
  45              test_blocks.append(block)
  46              tip = block.sha256
  47              tipheight += 1
  48              last_block_time += 1
  49          return test_blocks
  50  
  51      def create_test_block(self, txs):
  52          block = create_block(self.tip, create_coinbase(self.tipheight + 1), self.last_block_time + 600, txlist=txs)
  53          block.solve()
  54          return block
  55  
  56      def run_test(self):
  57          node0, node1 = self.nodes[0], self.nodes[1]
  58          self.log.info("Check inbound connection to v2 TestNode from v2 P2PConnection is v2")
  59          peer1 = node0.add_p2p_connection(P2PInterface(), wait_for_verack=True, supports_v2_p2p=True)
  60          assert peer1.supports_v2_p2p
  61          assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v2")
  62  
  63          self.log.info("Check inbound connection to v2 TestNode from v1 P2PConnection is v1")
  64          peer2 = node0.add_p2p_connection(P2PInterface(), wait_for_verack=True, supports_v2_p2p=False)
  65          assert not peer2.supports_v2_p2p
  66          assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v1")
  67  
  68          self.log.info("Check outbound connection from v2 TestNode to v1 P2PConnection advertised as v1 is v1")
  69          peer3 = node0.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, supports_v2_p2p=False, advertise_v2_p2p=False)
  70          assert not peer3.supports_v2_p2p
  71          assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v1")
  72  
  73          # v2 TestNode performs downgrading here
  74          self.log.info("Check outbound connection from v2 TestNode to v1 P2PConnection advertised as v2 is v1")
  75          peer4 = node0.add_outbound_p2p_connection(P2PInterface(), p2p_idx=1, supports_v2_p2p=False, advertise_v2_p2p=True)
  76          assert not peer4.supports_v2_p2p
  77          assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v1")
  78  
  79          self.log.info("Check outbound connection from v2 TestNode to v2 P2PConnection advertised as v2 is v2")
  80          peer5 = node0.add_outbound_p2p_connection(P2PInterface(), p2p_idx=2, supports_v2_p2p=True, advertise_v2_p2p=True)
  81          assert peer5.supports_v2_p2p
  82          assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v2")
  83  
  84          self.log.info("Check if version is sent and verack is received in inbound/outbound connections")
  85          assert_equal(len(node0.getpeerinfo()), 5)  # check if above 5 connections are present in node0's getpeerinfo()
  86          for peer in node0.getpeerinfo():
  87              assert_greater_than(peer['bytessent_per_msg']['version'], 0)
  88              assert_greater_than(peer['bytesrecv_per_msg']['verack'], 0)
  89  
  90          self.log.info("Testing whether blocks propagate - check if tips sync when number of blocks >= REKEY_INTERVAL")
  91          # tests whether rekeying (which happens every REKEY_INTERVAL packets) works correctly
  92          test_blocks = self.generate_blocks(node0, REKEY_INTERVAL+1)
  93  
  94          for i in range(2):
  95              peer6 = node0.add_p2p_connection(P2PDataStore(), supports_v2_p2p=True)
  96              assert peer6.supports_v2_p2p
  97              assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v2")
  98  
  99              # Consider: node0 <-- peer6. node0 and node1 aren't connected here.
 100              # Construct the following topology: node1 <--> node0 <-- peer6
 101              # and test that blocks produced by peer6 will be received by node1 if sent normally
 102              # and won't be received by node1 if sent as decoy messages
 103  
 104              # First, check whether blocks produced be peer6 are received by node0 if sent normally
 105              # and not received by node0 if sent as decoy messages.
 106              if i:
 107                  # check that node0 receives blocks produced by peer6
 108                  self.log.info("Check if blocks produced by node0's p2p connection is received by node0")
 109                  peer6.send_blocks_and_test(test_blocks, node0, success=True)  # node0's tip advances
 110              else:
 111                  # check that node0 doesn't receive blocks produced by peer6 since they are sent as decoy messages
 112                  self.log.info("Check if blocks produced by node0's p2p connection sent as decoys aren't received by node0")
 113                  peer6.send_blocks_and_test(test_blocks, node0, success=False, is_decoy=True)  # node0's tip doesn't advance
 114  
 115              # Then, connect node0 and node1 using v2 and check whether the blocks are received by node1
 116              self.connect_nodes(0, 1, peer_advertises_v2=True)
 117              self.log.info("Wait for node1 to receive all the blocks from node0")
 118              self.sync_all()
 119              self.log.info("Make sure node0 and node1 have same block tips")
 120              assert_equal(node0.getbestblockhash(), node1.getbestblockhash())
 121  
 122              self.disconnect_nodes(0, 1)
 123  
 124          self.log.info("Check the connections opened as expected")
 125          check_node_connections(node=node0, num_in=4, num_out=3)
 126  
 127          self.log.info("Check inbound connection to v1 TestNode from v2 P2PConnection is v1")
 128          self.restart_node(0, ["-v2transport=0"])
 129          peer1 = node0.add_p2p_connection(P2PInterface(), wait_for_verack=True, supports_v2_p2p=True)
 130          assert not peer1.supports_v2_p2p
 131          assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v1")
 132          check_node_connections(node=node0, num_in=1, num_out=0)
 133  
 134          conf = Socks5Configuration()
 135          conf.auth = True
 136          conf.unauth = True
 137          conf.addr = ('127.0.0.1', p2p_port(self.num_nodes))
 138          conf.keep_alive = True
 139          proxy = Socks5Server(conf)
 140          proxy.start()
 141          args = ['-listen', f'-proxy={conf.addr[0]}:{conf.addr[1]}', '-proxyrandomize=0', '-v2onlyclearnet=1', '-v2transport=1']
 142          self.restart_node(0, extra_args=args)
 143          self.log.info("Test -v2onlyclearnet=1 behaviour")
 144          self.log.info("Check that outbound v2 connection to an ipv4 peer is successful")
 145          node0.addnode("15.61.23.23:1234", "onetry", True)
 146          assert_equal(node0.getpeerinfo()[-1]["addr"], "15.61.23.23:1234")
 147          self.log.info("Check that outbound v1 connection to an ipv4 peer is unsuccessful")
 148          node0.addnode("8.8.8.8:1234", "onetry", False)
 149          assert all(peer["addr"] != "8.8.8.8:1234" for peer in node0.getpeerinfo())
 150          self.log.info("Check that outbound v1 connection to an onion peer is successful")
 151          addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
 152          node0.addnode(addr, "onetry", False)
 153          assert_equal(node0.getpeerinfo()[-1]["addr"], addr)
 154  
 155  
 156  if __name__ == '__main__':
 157      P2PEncrypted(__file__).main()
 158