feature_bind_extra.py
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()