Skip to content

Commit 3a45493

Browse files
author
MarcoFalke
committed
Merge #13512: [qa] mininode: Expose connection state through is_connected
fa1eac9 [qa] mininode: Expose connection state through is_connected (MarcoFalke) Pull request description: This gets rid of some non-type safe string comparisons and access to members that are implementation details of `class P2PConnection(asyncore.dispatcher)`. Such refactoring is required to replace the deprecated asyncore with something more sane. Changes: * Get rid of non-enum member `state` and replace is with bool `connected` * Get rid of confusing argument `pushbuf` and literally just push to the buffer at the call site Tree-SHA512: 09074c7e5ed251a2e0509ef205ab82f89887c1e1fa1cc6efc1db60d196eb2403788a4987df8809fd06d80ef652e614c5d3c3fdef70096fc5815102243388288d
2 parents 000abbb + fa1eac9 commit 3a45493

File tree

5 files changed

+51
-47
lines changed

5 files changed

+51
-47
lines changed

test/functional/feature_assumevalid.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ def setup_network(self):
6868
def send_blocks_until_disconnected(self, p2p_conn):
6969
"""Keep sending blocks to the node until we're disconnected."""
7070
for i in range(len(self.blocks)):
71-
if p2p_conn.state != "connected":
71+
if not p2p_conn.is_connected:
7272
break
7373
try:
7474
p2p_conn.send_message(msg_block(self.blocks[i]))
7575
except IOError as e:
76-
assert str(e) == 'Not connected, no pushbuf'
76+
assert not p2p_conn.is_connected
7777
break
7878

7979
def assert_blockchain_height(self, node, height):

test/functional/p2p_compactblocks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def send_await_disconnect(self, message, timeout=30):
8787
This is used when we want to send a message into the node that we expect
8888
will get us disconnected, eg an invalid block."""
8989
self.send_message(message)
90-
wait_until(lambda: self.state != "connected", timeout=timeout, lock=mininode_lock)
90+
wait_until(lambda: not self.is_connected, timeout=timeout, lock=mininode_lock)
9191

9292
class CompactBlocksTest(BitcoinTestFramework):
9393
def set_test_params(self):

test/functional/p2p_leak.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ def run_test(self):
118118
time.sleep(5)
119119

120120
#This node should have been banned
121-
assert no_version_bannode.state != "connected"
121+
assert not no_version_bannode.is_connected
122122

123123
# These nodes should have been disconnected
124-
assert unsupported_service_bit5_node.state != "connected"
125-
assert unsupported_service_bit7_node.state != "connected"
124+
assert not unsupported_service_bit5_node.is_connected
125+
assert not unsupported_service_bit7_node.is_connected
126126

127127
self.nodes[0].disconnect_p2ps()
128128

test/functional/p2p_timeouts.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ def run_test(self):
4747

4848
sleep(1)
4949

50-
assert no_verack_node.connected
51-
assert no_version_node.connected
52-
assert no_send_node.connected
50+
assert no_verack_node.is_connected
51+
assert no_version_node.is_connected
52+
assert no_send_node.is_connected
5353

5454
no_verack_node.send_message(msg_ping())
5555
no_version_node.send_message(msg_ping())
@@ -58,18 +58,18 @@ def run_test(self):
5858

5959
assert "version" in no_verack_node.last_message
6060

61-
assert no_verack_node.connected
62-
assert no_version_node.connected
63-
assert no_send_node.connected
61+
assert no_verack_node.is_connected
62+
assert no_version_node.is_connected
63+
assert no_send_node.is_connected
6464

6565
no_verack_node.send_message(msg_ping())
6666
no_version_node.send_message(msg_ping())
6767

6868
sleep(31)
6969

70-
assert not no_verack_node.connected
71-
assert not no_version_node.connected
72-
assert not no_send_node.connected
70+
assert not no_verack_node.is_connected
71+
assert not no_version_node.is_connected
72+
assert not no_send_node.is_connected
7373

7474
if __name__ == '__main__':
7575
TimeoutsTest().main()

test/functional/test_framework/mininode.py

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,20 @@ def __init__(self):
7777

7878
super().__init__(map=mininode_socket_map)
7979

80+
self._conn_open = False
81+
82+
@property
83+
def is_connected(self):
84+
return self._conn_open
85+
8086
def peer_connect(self, dstaddr, dstport, net="regtest"):
8187
self.dstaddr = dstaddr
8288
self.dstport = dstport
8389
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
8490
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
8591
self.sendbuf = b""
8692
self.recvbuf = b""
87-
self.state = "connecting"
93+
self._asyncore_pre_connection = True
8894
self.network = net
8995
self.disconnect = False
9096

@@ -97,22 +103,23 @@ def peer_connect(self, dstaddr, dstport, net="regtest"):
97103

98104
def peer_disconnect(self):
99105
# Connection could have already been closed by other end.
100-
if self.state == "connected":
101-
self.disconnect_node()
106+
if self.is_connected:
107+
self.disconnect = True # Signal asyncore to disconnect
102108

103109
# Connection and disconnection methods
104110

105111
def handle_connect(self):
106112
"""asyncore callback when a connection is opened."""
107-
if self.state != "connected":
113+
if not self.is_connected:
108114
logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport))
109-
self.state = "connected"
115+
self._conn_open = True
116+
self._asyncore_pre_connection = False
110117
self.on_open()
111118

112119
def handle_close(self):
113120
"""asyncore callback when a connection is closed."""
114121
logger.debug("Closing connection to: %s:%d" % (self.dstaddr, self.dstport))
115-
self.state = "closed"
122+
self._conn_open = False
116123
self.recvbuf = b""
117124
self.sendbuf = b""
118125
try:
@@ -121,13 +128,6 @@ def handle_close(self):
121128
pass
122129
self.on_close()
123130

124-
def disconnect_node(self):
125-
"""Disconnect the p2p connection.
126-
127-
Called by the test logic thread. Causes the p2p connection
128-
to be disconnected on the next iteration of the asyncore loop."""
129-
self.disconnect = True
130-
131131
# Socket read methods
132132

133133
def handle_read(self):
@@ -182,17 +182,16 @@ def on_message(self, message):
182182
def writable(self):
183183
"""asyncore method to determine whether the handle_write() callback should be called on the next loop."""
184184
with mininode_lock:
185-
pre_connection = self.state == "connecting"
186185
length = len(self.sendbuf)
187-
return (length > 0 or pre_connection)
186+
return length > 0 or self._asyncore_pre_connection
188187

189188
def handle_write(self):
190189
"""asyncore callback when data should be written to the socket."""
191190
with mininode_lock:
192191
# asyncore does not expose socket connection, only the first read/write
193192
# event, thus we must check connection manually here to know when we
194193
# actually connect
195-
if self.state == "connecting":
194+
if self._asyncore_pre_connection:
196195
self.handle_connect()
197196
if not self.writable():
198197
return
@@ -204,26 +203,17 @@ def handle_write(self):
204203
return
205204
self.sendbuf = self.sendbuf[sent:]
206205

207-
def send_message(self, message, pushbuf=False):
206+
def send_message(self, message):
208207
"""Send a P2P message over the socket.
209208
210209
This method takes a P2P payload, builds the P2P header and adds
211210
the message to the send buffer to be sent over the socket."""
212-
if self.state != "connected" and not pushbuf:
213-
raise IOError('Not connected, no pushbuf')
211+
if not self.is_connected:
212+
raise IOError('Not connected')
214213
self._log_message("send", message)
215-
command = message.command
216-
data = message.serialize()
217-
tmsg = MAGIC_BYTES[self.network]
218-
tmsg += command
219-
tmsg += b"\x00" * (12 - len(command))
220-
tmsg += struct.pack("<I", len(data))
221-
th = sha256(data)
222-
h = sha256(th)
223-
tmsg += h[:4]
224-
tmsg += data
214+
tmsg = self._build_message(message)
225215
with mininode_lock:
226-
if (len(self.sendbuf) == 0 and not pushbuf):
216+
if len(self.sendbuf) == 0:
227217
try:
228218
sent = self.send(tmsg)
229219
self.sendbuf = tmsg[sent:]
@@ -234,6 +224,20 @@ def send_message(self, message, pushbuf=False):
234224

235225
# Class utility methods
236226

227+
def _build_message(self, message):
228+
"""Build a serialized P2P message"""
229+
command = message.command
230+
data = message.serialize()
231+
tmsg = MAGIC_BYTES[self.network]
232+
tmsg += command
233+
tmsg += b"\x00" * (12 - len(command))
234+
tmsg += struct.pack("<I", len(data))
235+
th = sha256(data)
236+
h = sha256(th)
237+
tmsg += h[:4]
238+
tmsg += data
239+
return tmsg
240+
237241
def _log_message(self, direction, msg):
238242
"""Logs a message being sent or received over the connection."""
239243
if direction == "send":
@@ -280,7 +284,7 @@ def peer_connect(self, *args, services=NODE_NETWORK|NODE_WITNESS, send_version=T
280284
vt.addrTo.port = self.dstport
281285
vt.addrFrom.ip = "0.0.0.0"
282286
vt.addrFrom.port = 0
283-
self.send_message(vt, True)
287+
self.sendbuf = self._build_message(vt) # Will be sent right after handle_connect
284288

285289
# Message receiving methods
286290

@@ -348,7 +352,7 @@ def on_version(self, message):
348352
# Connection helper methods
349353

350354
def wait_for_disconnect(self, timeout=60):
351-
test_function = lambda: self.state != "connected"
355+
test_function = lambda: not self.is_connected
352356
wait_until(test_function, timeout=timeout, lock=mininode_lock)
353357

354358
# Message receiving helper methods

0 commit comments

Comments
 (0)