1 #!/usr/bin/env python3
2 # Copyright (c) 2017-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 message sending before handshake completion.
6 7 Before receiving a VERACK, a node should not send anything but VERSION/VERACK
8 and feature negotiation messages (WTXIDRELAY, SENDADDRV2, FEATURE).
9 10 This test connects to a node and sends it a few messages, trying to entice it
11 into sending us something it shouldn't."""
12 13 import time
14 15 from test_framework.messages import (
16 msg_getaddr,
17 msg_ping,
18 msg_version,
19 )
20 from test_framework.p2p import (
21 P2PInterface,
22 P2P_SUBVERSION,
23 P2P_SERVICES,
24 P2P_VERSION_RELAY,
25 )
26 from test_framework.test_framework import BitcoinTestFramework
27 from test_framework.util import (
28 assert_equal,
29 assert_greater_than_or_equal,
30 )
31 32 PEER_TIMEOUT = 3
33 34 35 class LazyPeer(P2PInterface):
36 def __init__(self):
37 super().__init__()
38 self.unexpected_msg = False
39 self.ever_connected = False
40 self.got_wtxidrelay = False
41 self.got_sendaddrv2 = False
42 self.got_feature = False
43 44 def bad_message(self, message):
45 self.unexpected_msg = True
46 print("should not have received message: %s" % message.msgtype)
47 48 def on_open(self):
49 self.ever_connected = True
50 51 # Does not respond to "version" with "verack"
52 def on_version(self, message): self.bad_message(message)
53 def on_verack(self, message): self.bad_message(message)
54 def on_inv(self, message): self.bad_message(message)
55 def on_addr(self, message): self.bad_message(message)
56 def on_getdata(self, message): self.bad_message(message)
57 def on_getblocks(self, message): self.bad_message(message)
58 def on_tx(self, message): self.bad_message(message)
59 def on_block(self, message): self.bad_message(message)
60 def on_getaddr(self, message): self.bad_message(message)
61 def on_headers(self, message): self.bad_message(message)
62 def on_getheaders(self, message): self.bad_message(message)
63 def on_ping(self, message): self.bad_message(message)
64 def on_mempool(self, message): self.bad_message(message)
65 def on_pong(self, message): self.bad_message(message)
66 def on_feefilter(self, message): self.bad_message(message)
67 def on_sendheaders(self, message): self.bad_message(message)
68 def on_sendcmpct(self, message): self.bad_message(message)
69 def on_cmpctblock(self, message): self.bad_message(message)
70 def on_getblocktxn(self, message): self.bad_message(message)
71 def on_blocktxn(self, message): self.bad_message(message)
72 def on_wtxidrelay(self, message): self.got_wtxidrelay = True
73 def on_sendaddrv2(self, message): self.got_sendaddrv2 = True
74 def on_feature(self, message): self.got_feature = True
75 76 77 # Peer that sends a version but not a verack.
78 class NoVerackIdlePeer(LazyPeer):
79 def __init__(self):
80 self.version_received = False
81 super().__init__()
82 83 def on_verack(self, message): pass
84 # When version is received, don't reply with a verack. Instead, see if the
85 # node will give us a message that it shouldn't. This is not an exhaustive
86 # list!
87 def on_version(self, message):
88 self.version_received = True
89 self.send_without_ping(msg_ping())
90 self.send_without_ping(msg_getaddr())
91 92 93 class P2PVersionStore(P2PInterface):
94 version_received = None
95 96 def on_version(self, msg):
97 # Responds with an appropriate verack
98 super().on_version(msg)
99 self.version_received = msg
100 101 102 class P2PLeakTest(BitcoinTestFramework):
103 def set_test_params(self):
104 self.num_nodes = 1
105 self.extra_args = [[f"-peertimeout={PEER_TIMEOUT}"]]
106 107 def create_old_version(self, nversion):
108 old_version_msg = msg_version()
109 old_version_msg.nVersion = nversion
110 old_version_msg.strSubVer = P2P_SUBVERSION
111 old_version_msg.nServices = P2P_SERVICES
112 old_version_msg.relay = P2P_VERSION_RELAY
113 return old_version_msg
114 115 def run_test(self):
116 self.log.info('Check that the node doesn\'t send unexpected messages before handshake completion')
117 # Peer that never sends a version, nor any other messages. It shouldn't receive anything from the node.
118 no_version_idle_peer = self.nodes[0].add_p2p_connection(LazyPeer(), send_version=False, wait_for_verack=False)
119 120 # Peer that sends a version but not a verack.
121 no_verack_idle_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), wait_for_verack=False)
122 123 # Pre-wtxidRelay peer that sends a version but not a verack and does not support feature negotiation
124 # messages which start at nVersion >= 70016
125 pre_wtxidrelay_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), send_version=False, wait_for_verack=False)
126 pre_wtxidrelay_peer.send_without_ping(self.create_old_version(70015))
127 128 # Wait until the peer gets the verack in response to the version. Though, don't wait for the node to receive the
129 # verack, since the peer never sent one
130 no_verack_idle_peer.wait_for_verack()
131 pre_wtxidrelay_peer.wait_for_verack()
132 133 no_version_idle_peer.wait_until(lambda: no_version_idle_peer.ever_connected)
134 no_verack_idle_peer.wait_until(lambda: no_verack_idle_peer.version_received)
135 pre_wtxidrelay_peer.wait_until(lambda: pre_wtxidrelay_peer.version_received)
136 137 # Mine a block and make sure that it's not sent to the connected peers
138 self.generate(self.nodes[0], nblocks=1)
139 140 # Give the node enough time to possibly leak out a message
141 time.sleep(PEER_TIMEOUT + 2)
142 143 self.log.info("Connect peer to ensure the net thread runs the disconnect logic at least once")
144 self.nodes[0].add_p2p_connection(P2PInterface())
145 146 # Make sure only expected messages came in
147 assert not no_version_idle_peer.unexpected_msg
148 assert not no_version_idle_peer.got_wtxidrelay
149 assert not no_version_idle_peer.got_sendaddrv2
150 assert not no_version_idle_peer.got_feature
151 152 assert not no_verack_idle_peer.unexpected_msg
153 assert no_verack_idle_peer.got_wtxidrelay
154 assert no_verack_idle_peer.got_sendaddrv2
155 #assert no_verack_idle_peer.got_feature ## uncomment once a feature message is supported
156 157 assert not pre_wtxidrelay_peer.unexpected_msg
158 assert not pre_wtxidrelay_peer.got_wtxidrelay
159 assert not pre_wtxidrelay_peer.got_sendaddrv2
160 assert not pre_wtxidrelay_peer.got_feature
161 162 # Expect peers to be disconnected due to timeout
163 assert not no_version_idle_peer.is_connected
164 assert not no_verack_idle_peer.is_connected
165 assert not pre_wtxidrelay_peer.is_connected
166 167 self.log.info('Check that the version message does not leak the local address of the node')
168 p2p_version_store = self.nodes[0].add_p2p_connection(P2PVersionStore())
169 ver = p2p_version_store.version_received
170 # Check that received time is within one hour of now
171 assert_greater_than_or_equal(ver.nTime, time.time() - 3600)
172 assert_greater_than_or_equal(time.time() + 3600, ver.nTime)
173 assert_equal(ver.addrFrom.port, 0)
174 assert_equal(ver.addrFrom.ip, '0.0.0.0')
175 assert_equal(ver.nStartingHeight, 201)
176 assert_equal(ver.relay, 1)
177 178 self.log.info('Check that old peers are disconnected')
179 p2p_old_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)
180 with self.nodes[0].assert_debug_log(["using obsolete version 31799, disconnecting peer=5"]):
181 p2p_old_peer.send_without_ping(self.create_old_version(31799))
182 p2p_old_peer.wait_for_disconnect()
183 184 185 if __name__ == '__main__':
186 P2PLeakTest(__file__).main()
187