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 """Test add_outbound_p2p_connection test framework functionality"""
6 7 from test_framework.p2p import P2PInterface
8 from test_framework.test_framework import BitcoinTestFramework
9 from test_framework.util import (
10 assert_equal,
11 check_node_connections,
12 )
13 14 15 class VersionSender(P2PInterface):
16 def on_open(self):
17 assert self.on_connection_send_msg is not None
18 self.send_version()
19 assert self.on_connection_send_msg is None
20 21 22 class P2PFeelerReceiver(P2PInterface):
23 def on_version(self, message):
24 # The bitcoind node closes feeler connections as soon as a version
25 # message is received from the test framework. Don't send any responses
26 # to the node's version message since the connection will already be
27 # closed.
28 self.send_version()
29 30 class P2PAddConnections(BitcoinTestFramework):
31 def set_test_params(self):
32 self.num_nodes = 2
33 34 def setup_network(self):
35 self.setup_nodes()
36 # Don't connect the nodes
37 38 def run_test(self):
39 self.log.info("Add 8 outbounds to node 0")
40 for i in range(8):
41 self.log.info(f"outbound: {i}")
42 self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i, connection_type="outbound-full-relay")
43 44 self.log.info("Add 2 block-relay-only connections to node 0")
45 for i in range(2):
46 self.log.info(f"block-relay-only: {i}")
47 # set p2p_idx based on the outbound connections already open to the
48 # node, so add 8 to account for the previous full-relay connections
49 self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i + 8, connection_type="block-relay-only")
50 51 self.log.info("Add 2 block-relay-only connections to node 1")
52 for i in range(2):
53 self.log.info(f"block-relay-only: {i}")
54 self.nodes[1].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i, connection_type="block-relay-only")
55 56 self.log.info("Add 5 inbound connections to node 1")
57 for i in range(5):
58 self.log.info(f"inbound: {i}")
59 self.nodes[1].add_p2p_connection(P2PInterface())
60 61 self.log.info("Add 8 outbounds to node 1")
62 for i in range(8):
63 self.log.info(f"outbound: {i}")
64 # bump p2p_idx to account for the 2 existing outbounds on node 1
65 self.nodes[1].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i + 2)
66 67 self.log.info("Check the connections opened as expected")
68 check_node_connections(node=self.nodes[0], num_in=0, num_out=10)
69 check_node_connections(node=self.nodes[1], num_in=5, num_out=10)
70 71 self.log.info("Disconnect p2p connections & try to re-open")
72 self.nodes[0].disconnect_p2ps()
73 check_node_connections(node=self.nodes[0], num_in=0, num_out=0)
74 75 self.log.info("Add 8 outbounds to node 0")
76 for i in range(8):
77 self.log.info(f"outbound: {i}")
78 self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i)
79 check_node_connections(node=self.nodes[0], num_in=0, num_out=8)
80 81 self.log.info("Add 2 block-relay-only connections to node 0")
82 for i in range(2):
83 self.log.info(f"block-relay-only: {i}")
84 # bump p2p_idx to account for the 8 existing outbounds on node 0
85 self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i + 8, connection_type="block-relay-only")
86 check_node_connections(node=self.nodes[0], num_in=0, num_out=10)
87 88 self.log.info("Restart node 0 and try to reconnect to p2ps")
89 self.restart_node(0)
90 91 self.log.info("Add 4 outbounds to node 0")
92 for i in range(4):
93 self.log.info(f"outbound: {i}")
94 self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i)
95 check_node_connections(node=self.nodes[0], num_in=0, num_out=4)
96 97 self.log.info("Add 2 block-relay-only connections to node 0")
98 for i in range(2):
99 self.log.info(f"block-relay-only: {i}")
100 # bump p2p_idx to account for the 4 existing outbounds on node 0
101 self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i + 4, connection_type="block-relay-only")
102 check_node_connections(node=self.nodes[0], num_in=0, num_out=6)
103 104 check_node_connections(node=self.nodes[1], num_in=5, num_out=10)
105 106 self.log.info("Add 1 feeler connection to node 0")
107 feeler_conn = self.nodes[0].add_outbound_p2p_connection(P2PFeelerReceiver(), p2p_idx=6, connection_type="feeler")
108 109 # Feeler connection is closed
110 assert not feeler_conn.is_connected
111 112 # Verify version message received
113 assert_equal(feeler_conn.message_count["version"], 1)
114 # Feeler connections do not request tx relay
115 assert_equal(feeler_conn.last_message["version"].relay, 0)
116 117 self.log.info("Send version message early to node")
118 # Normally the test framework would be shy and send the version message
119 # only after it received one. See the on_version method. Check that
120 # bitcoind behaves properly when a version is sent unexpectedly (but
121 # tolerably) early.
122 #
123 # This checks that bitcoind sends its own version prior to processing
124 # the remote version (and replying with a verack). Otherwise it would
125 # be violating its own rules, such as "non-version message before
126 # version handshake".
127 ver_conn = self.nodes[0].add_outbound_p2p_connection(VersionSender(), p2p_idx=6, connection_type="outbound-full-relay", supports_v2_p2p=False, advertise_v2_p2p=False)
128 ver_conn.sync_with_ping()
129 130 131 if __name__ == '__main__':
132 P2PAddConnections(__file__).main()
133