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