feature_bind_port_discover.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2020-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  """
   6  Test that -discover does not add all interfaces' addresses if we listen on only some of them
   7  """
   8  
   9  from test_framework.test_framework import BitcoinTestFramework, SkipTest
  10  from test_framework.test_node import (
  11      FailedToStartError,
  12  )
  13  from test_framework.util import (
  14      assert_equal,
  15      assert_not_equal,
  16      p2p_port,
  17      tor_port,
  18  )
  19  
  20  # We need to bind to routable addresses for this test. Both addresses must be on an
  21  # interface that is UP and not a loopback interface (IFF_LOOPBACK). To set these
  22  # routable addresses on the machine, use:
  23  # Linux:
  24  # First find your interfaces: ip addr show
  25  # Then use your actual interface names (replace INTERFACE_NAME with yours):
  26  # ip addr add 1.1.1.5/32 dev INTERFACE_NAME && ip addr add 1111:1111::5/128 dev INTERFACE_NAME  # to set up
  27  # ip addr del 1.1.1.5/32 dev INTERFACE_NAME && ip addr del 1111:1111::5/128 dev INTERFACE_NAME  # to remove it
  28  #
  29  # FreeBSD and MacOS:
  30  # ifconfig INTERFACE_NAME 1.1.1.5/32 alias && ifconfig INTERFACE_NAME inet6 1111:1111::5/128 alias  # to set up
  31  # ifconfig INTERFACE_NAME 1.1.1.5 -alias && ifconfig INTERFACE_NAME inet6 1111:1111::5 -alias       # to remove it, after the test
  32  ADDR1 = '1.1.1.5' # This and the address below are set in the CI environment, don't change it just here (keep them in sync).
  33  ADDR2 = '1111:1111::5'
  34  
  35  class BindPortDiscoverTest(BitcoinTestFramework):
  36      def set_test_params(self):
  37          # Avoid any -bind= on the command line. Force the framework to avoid adding -bind=127.0.0.1.
  38          self.bind_to_localhost_only = False
  39          # Get dynamic ports for each node from the test framework
  40          self.bind_ports = [
  41              p2p_port(0),
  42              p2p_port(2), # node0 will use their port + 1 for onion listen, which is the same as p2p_port(1), so avoid collision
  43              p2p_port(3),
  44              p2p_port(4),
  45          ]
  46          self.extra_args = [
  47              ['-discover', f'-port={self.bind_ports[0]}', '-listen=1'], # Without any -bind
  48              ['-discover', f'-bind=0.0.0.0:{self.bind_ports[1]}'], # Explicit -bind=0.0.0.0
  49              # Explicit -whitebind=0.0.0.0, add onion bind to avoid port conflict
  50              ['-discover', f'-whitebind=0.0.0.0:{self.bind_ports[2]}', f'-bind=127.0.0.1:{tor_port(3)}=onion'],
  51              ['-discover', f'-bind={ADDR1}:{self.bind_ports[3]}'], # Explicit -bind=routable_addr
  52          ]
  53          self.num_nodes = len(self.extra_args)
  54  
  55      def setup_network(self):
  56          """
  57          Override to avoid connecting nodes together. This test intentionally does not connect nodes
  58          because each node is bound to a different address or interface, and connections are not needed.
  59          """
  60          self.setup_nodes()
  61  
  62      def setup_nodes(self):
  63          """
  64          Override to set has_explicit_bind=True for nodes with explicit bind arguments.
  65          """
  66          self.add_nodes(self.num_nodes, self.extra_args)
  67          # TestNode.start() will add -bind= to extra_args if has_explicit_bind is
  68          # False. We do not want any -bind= thus set has_explicit_bind to True.
  69          for node in self.nodes:
  70              node.has_explicit_bind = True
  71  
  72          try:
  73              self.start_nodes()
  74          except FailedToStartError as e:
  75              self.cleanup_partially_started_nodes()
  76              if 'Unable to bind to ' in str(e):
  77                  raise SkipTest(
  78                      f'To run this test make sure that {ADDR1} and {ADDR2} '
  79                      '(routable addresses) are assigned to non-loopback '
  80                      'interfaces on this machine')
  81              raise
  82  
  83      def run_test(self):
  84          self.log.info(
  85                  "Test that if -bind= is not passed or -bind=0.0.0.0 is used then all addresses are "
  86                  "added to localaddresses")
  87          for i in [0, 1, 2]:
  88              found_addr1 = False
  89              found_addr2 = False
  90              localaddresses = self.nodes[i].getnetworkinfo()['localaddresses']
  91              for local in localaddresses:
  92                  if local['address'] == ADDR1:
  93                      found_addr1 = True
  94                      assert_equal(local['port'], self.bind_ports[i])
  95                  if local['address'] == ADDR2:
  96                      found_addr2 = True
  97                      assert_equal(local['port'], self.bind_ports[i])
  98              if not found_addr1:
  99                  self.log.error(f"Address {ADDR1} not found in node{i}'s local addresses: {localaddresses}")
 100                  assert False
 101              if not found_addr2:
 102                  self.log.error(f"Address {ADDR2} not found in node{i}'s local addresses: {localaddresses}")
 103                  assert False
 104  
 105          self.log.info(
 106                  "Test that if -bind=routable_addr is passed then only that address is "
 107                  "added to localaddresses")
 108          found_addr1 = False
 109          i = 3
 110          for local in self.nodes[i].getnetworkinfo()['localaddresses']:
 111              if local['address'] == ADDR1:
 112                  found_addr1 = True
 113                  assert_equal(local['port'], self.bind_ports[i])
 114              assert_not_equal(local['address'], ADDR2)
 115          assert found_addr1
 116  
 117  if __name__ == '__main__':
 118      BindPortDiscoverTest(__file__).main()
 119