feature_bind_extra.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2014-2022 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 # Due to OS-specific network stats queries, we only run on Linux. 36 self.skip_if_platform_not_linux() 37 38 def setup_network(self): 39 loopback_ipv4 = addr_to_hex("127.0.0.1") 40 41 # Start custom ports by reusing unused p2p ports 42 def extra_port(): 43 port = p2p_port(extra_port.index) 44 extra_port.index += 1 45 return port 46 extra_port.index = self.num_nodes 47 48 # Array of tuples [command line arguments, expected bind addresses]. 49 self.expected = [] 50 51 # Node0, no normal -bind=... with -bind=...=onion, thus only the tor target. 52 port = extra_port() 53 self.expected.append( 54 [ 55 [f"-bind=127.0.0.1:{port}=onion"], 56 [(loopback_ipv4, port)], 57 ], 58 ) 59 60 # Node1, both -bind=... and -bind=...=onion. 61 port = [extra_port(), extra_port()] 62 self.expected.append( 63 [ 64 [f"-bind=127.0.0.1:{port[0]}", f"-bind=127.0.0.1:{port[1]}=onion"], 65 [(loopback_ipv4, port[0]), (loopback_ipv4, port[1])], 66 ], 67 ) 68 69 # Node2, no -bind=...=onion, thus no extra port for Tor target. 70 port = extra_port() 71 self.expected.append( 72 [ 73 [f"-bind=127.0.0.1:{port}"], 74 [(loopback_ipv4, port)], 75 ], 76 ) 77 78 self.extra_args = list(map(lambda e: e[0], self.expected)) 79 self.setup_nodes() 80 81 def run_test(self): 82 for i, (args, expected_services) in enumerate(self.expected): 83 self.log.info(f"Checking listening ports of node {i} with {args}") 84 pid = self.nodes[i].process.pid 85 binds = set(get_bind_addrs(pid)) 86 # Remove IPv6 addresses because on some CI environments "::1" is not configured 87 # on the system (so our test_ipv6_local() would return False), but it is 88 # possible to bind on "::". This makes it unpredictable whether to expect 89 # that bitcoind has bound on "::1" (for RPC) and "::" (for P2P). 90 ipv6_addr_len_bytes = 32 91 binds = set(filter(lambda e: len(e[0]) != ipv6_addr_len_bytes, binds)) 92 # Remove RPC ports. They are not relevant for this test. 93 binds = set(filter(lambda e: e[1] != rpc_port(i), binds)) 94 assert_equal(binds, set(expected_services)) 95 96 self.stop_node(0) 97 98 addr = "127.0.0.1:11012" 99 for opt1, opt2 in combinations_with_replacement([f"-bind={addr}", f"-bind={addr}=onion", f"-whitebind=noban@{addr}"], 2): 100 self.nodes[0].assert_start_raises_init_error( 101 [opt1, opt2], 102 "Error: Duplicate binding configuration", 103 match=ErrorMatch.PARTIAL_REGEX) 104 105 if __name__ == '__main__': 106 BindExtraTest(__file__).main()