feature_bind_extra.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2014-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 starting bitcoind with -bind and/or -bind=...=onion, confirm that
7 it binds to the expected ports, and verify that duplicate or conflicting
8 -bind/-whitebind configurations are rejected with a descriptive error.
9 """
10
11 from itertools import combinations_with_replacement
12 from test_framework.netutil import (
13 addr_to_hex,
14 get_bind_addrs,
15 )
16 from test_framework.test_framework import (
17 BitcoinTestFramework,
18 )
19 from test_framework.test_node import ErrorMatch
20 from test_framework.util import (
21 assert_equal,
22 p2p_port,
23 rpc_port,
24 )
25
26 class BindExtraTest(BitcoinTestFramework):
27 def set_test_params(self):
28 self.setup_clean_chain = True
29 # Avoid any -bind= on the command line. Force the framework to avoid
30 # adding -bind=127.0.0.1.
31 self.bind_to_localhost_only = False
32 self.num_nodes = 3
33
34 def skip_test_if_missing_module(self):
35 self.skip_if_platform_not_posix()
36
37 def setup_network(self):
38 loopback_ipv4 = addr_to_hex("127.0.0.1")
39
40 # Start custom ports by reusing unused p2p ports
41 def extra_port():
42 port = p2p_port(extra_port.index)
43 extra_port.index += 1
44 return port
45 extra_port.index = self.num_nodes
46
47 # Array of tuples [command line arguments, expected bind addresses].
48 self.expected = []
49
50 # Node0, no normal -bind=... with -bind=...=onion, thus only the tor target.
51 port = extra_port()
52 self.expected.append(
53 [
54 [f"-bind=127.0.0.1:{port}=onion"],
55 [(loopback_ipv4, port)],
56 ],
57 )
58
59 # Node1, both -bind=... and -bind=...=onion.
60 port = [extra_port(), extra_port()]
61 self.expected.append(
62 [
63 [f"-bind=127.0.0.1:{port[0]}", f"-bind=127.0.0.1:{port[1]}=onion"],
64 [(loopback_ipv4, port[0]), (loopback_ipv4, port[1])],
65 ],
66 )
67
68 # Node2, no -bind=...=onion, thus no extra port for Tor target.
69 port = extra_port()
70 self.expected.append(
71 [
72 [f"-bind=127.0.0.1:{port}"],
73 [(loopback_ipv4, port)],
74 ],
75 )
76
77 self.extra_args = list(map(lambda e: e[0], self.expected))
78 self.setup_nodes()
79
80 def run_test(self):
81 for i, (args, expected_services) in enumerate(self.expected):
82 self.log.info(f"Checking listening ports of node {i} with {args}")
83 pid = self.nodes[i].process.pid
84 binds = set(get_bind_addrs(pid))
85 # Remove IPv6 addresses because on some CI environments "::1" is not configured
86 # on the system (so our test_ipv6_local() would return False), but it is
87 # possible to bind on "::". This makes it unpredictable whether to expect
88 # that bitcoind has bound on "::1" (for RPC) and "::" (for P2P).
89 ipv6_addr_len_bytes = 32
90 binds = set(filter(lambda e: len(e[0]) != ipv6_addr_len_bytes, binds))
91 # Remove RPC ports. They are not relevant for this test.
92 binds = set(filter(lambda e: e[1] != rpc_port(i), binds))
93 assert_equal(binds, set(expected_services))
94
95 self.stop_node(0)
96
97 addr = "127.0.0.1:11012"
98 for opt1, opt2 in combinations_with_replacement([f"-bind={addr}", f"-bind={addr}=onion", f"-whitebind=noban@{addr}"], 2):
99 self.nodes[0].assert_start_raises_init_error(
100 [opt1, opt2],
101 "Error: Duplicate binding configuration",
102 match=ErrorMatch.PARTIAL_REGEX)
103
104 if __name__ == '__main__':
105 BindExtraTest(__file__).main()
106