-
Notifications
You must be signed in to change notification settings - Fork 37.8k
net: Use mockable time for ping/pong, add tests #18638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,123 @@ | ||||||||||||||||
#!/usr/bin/env python3 | ||||||||||||||||
# Copyright (c) 2020 The Bitcoin Core developers | ||||||||||||||||
# Distributed under the MIT software license, see the accompanying | ||||||||||||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||||||||||||||
"""Test ping message | ||||||||||||||||
""" | ||||||||||||||||
|
||||||||||||||||
import time | ||||||||||||||||
|
||||||||||||||||
from test_framework.messages import ( | ||||||||||||||||
msg_pong, | ||||||||||||||||
) | ||||||||||||||||
Comment on lines
+10
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: single line for a single import? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. Can be fixed in a follow-up |
||||||||||||||||
from test_framework.mininode import ( | ||||||||||||||||
P2PInterface, | ||||||||||||||||
wait_until, | ||||||||||||||||
) | ||||||||||||||||
from test_framework.test_framework import BitcoinTestFramework | ||||||||||||||||
from test_framework.util import assert_equal | ||||||||||||||||
|
||||||||||||||||
PING_INTERVAL = 2 * 60 | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: if you retouch, could add a comment expressing the time units or along the lines of: PING_INTERVAL = 2 * 60 # 2 minutes (corresponds to net_processing::PING_INTERVAL) |
||||||||||||||||
|
||||||||||||||||
|
||||||||||||||||
class msg_pong_corrupt(msg_pong): | ||||||||||||||||
def serialize(self): | ||||||||||||||||
return b"" | ||||||||||||||||
|
||||||||||||||||
|
||||||||||||||||
class NodePongAdd1(P2PInterface): | ||||||||||||||||
def on_ping(self, message): | ||||||||||||||||
self.send_message(msg_pong(message.nonce + 1)) | ||||||||||||||||
|
||||||||||||||||
|
||||||||||||||||
class NodeNoPong(P2PInterface): | ||||||||||||||||
def on_ping(self, message): | ||||||||||||||||
pass | ||||||||||||||||
|
||||||||||||||||
|
||||||||||||||||
class PingPongTest(BitcoinTestFramework): | ||||||||||||||||
def set_test_params(self): | ||||||||||||||||
self.setup_clean_chain = True | ||||||||||||||||
maflcko marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||
self.num_nodes = 1 | ||||||||||||||||
self.extra_args = [['-peertimeout=3']] | ||||||||||||||||
|
||||||||||||||||
def check_peer_info(self, *, pingtime, minping, pingwait): | ||||||||||||||||
stats = self.nodes[0].getpeerinfo()[0] | ||||||||||||||||
assert_equal(stats.pop('pingtime', None), pingtime) | ||||||||||||||||
assert_equal(stats.pop('minping', None), minping) | ||||||||||||||||
assert_equal(stats.pop('pingwait', None), pingwait) | ||||||||||||||||
|
||||||||||||||||
def mock_forward(self, delta): | ||||||||||||||||
self.mock_time += delta | ||||||||||||||||
self.nodes[0].setmocktime(self.mock_time) | ||||||||||||||||
|
||||||||||||||||
def run_test(self): | ||||||||||||||||
self.mock_time = int(time.time()) | ||||||||||||||||
self.mock_forward(0) | ||||||||||||||||
|
||||||||||||||||
self.log.info('Check that ping is sent after connection is established') | ||||||||||||||||
no_pong_node = self.nodes[0].add_p2p_connection(NodeNoPong()) | ||||||||||||||||
self.mock_forward(3) | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||
assert no_pong_node.last_message.pop('ping').nonce != 0 | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why pop here, as opposed to just accessing the value? Seems like having Maybe decrementing
Suggested change
Or maybe
Suggested change
or
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't aware that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the reason I use pop is to ensure the value is discarded after a read and has no way to poison follow-up test cases |
||||||||||||||||
self.check_peer_info(pingtime=None, minping=None, pingwait=3) | ||||||||||||||||
|
||||||||||||||||
self.log.info('Reply without nonce cancels ping') | ||||||||||||||||
with self.nodes[0].assert_debug_log(['pong peer=0: Short payload']): | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I'm surprised it isn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Be aware that a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, that's it 👍 |
||||||||||||||||
no_pong_node.send_and_ping(msg_pong_corrupt()) | ||||||||||||||||
self.check_peer_info(pingtime=None, minping=None, pingwait=None) | ||||||||||||||||
|
||||||||||||||||
self.log.info('Reply without ping') | ||||||||||||||||
with self.nodes[0].assert_debug_log([ | ||||||||||||||||
'pong peer=0: Unsolicited pong without ping, 0 expected, 0 received, 8 bytes', | ||||||||||||||||
]): | ||||||||||||||||
no_pong_node.send_and_ping(msg_pong()) | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like to use
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, sending a ping and receiving a ping should never interfere, unless I am missing something obvious |
||||||||||||||||
self.check_peer_info(pingtime=None, minping=None, pingwait=None) | ||||||||||||||||
|
||||||||||||||||
self.log.info('Reply with wrong nonce does not cancel ping') | ||||||||||||||||
assert 'ping' not in no_pong_node.last_message | ||||||||||||||||
with self.nodes[0].assert_debug_log(['pong peer=0: Nonce mismatch']): | ||||||||||||||||
# mock time PING_INTERVAL ahead to trigger node into sending a ping | ||||||||||||||||
self.mock_forward(PING_INTERVAL + 1) | ||||||||||||||||
wait_until(lambda: 'ping' in no_pong_node.last_message) | ||||||||||||||||
self.mock_forward(9) | ||||||||||||||||
# Send the wrong pong | ||||||||||||||||
no_pong_node.send_and_ping(msg_pong(no_pong_node.last_message.pop('ping').nonce - 1)) | ||||||||||||||||
self.check_peer_info(pingtime=None, minping=None, pingwait=9) | ||||||||||||||||
|
||||||||||||||||
self.log.info('Reply with zero nonce does cancel ping') | ||||||||||||||||
with self.nodes[0].assert_debug_log(['pong peer=0: Nonce zero']): | ||||||||||||||||
no_pong_node.send_and_ping(msg_pong(0)) | ||||||||||||||||
self.check_peer_info(pingtime=None, minping=None, pingwait=None) | ||||||||||||||||
|
||||||||||||||||
self.log.info('Check that ping is properly reported on RPC') | ||||||||||||||||
assert 'ping' not in no_pong_node.last_message | ||||||||||||||||
# mock time PING_INTERVAL ahead to trigger node into sending a ping | ||||||||||||||||
self.mock_forward(PING_INTERVAL + 1) | ||||||||||||||||
wait_until(lambda: 'ping' in no_pong_node.last_message) | ||||||||||||||||
ping_delay = 29 | ||||||||||||||||
self.mock_forward(ping_delay) | ||||||||||||||||
wait_until(lambda: 'ping' in no_pong_node.last_message) | ||||||||||||||||
no_pong_node.send_and_ping(msg_pong(no_pong_node.last_message.pop('ping').nonce)) | ||||||||||||||||
self.check_peer_info(pingtime=ping_delay, minping=ping_delay, pingwait=None) | ||||||||||||||||
|
||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From what I understand, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also I'm just wondering, why is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! Will extend the test as suggested by you. |
||||||||||||||||
self.log.info('Check that minping is decreased after a fast roundtrip') | ||||||||||||||||
# mock time PING_INTERVAL ahead to trigger node into sending a ping | ||||||||||||||||
self.mock_forward(PING_INTERVAL + 1) | ||||||||||||||||
wait_until(lambda: 'ping' in no_pong_node.last_message) | ||||||||||||||||
ping_delay = 9 | ||||||||||||||||
self.mock_forward(ping_delay) | ||||||||||||||||
wait_until(lambda: 'ping' in no_pong_node.last_message) | ||||||||||||||||
no_pong_node.send_and_ping(msg_pong(no_pong_node.last_message.pop('ping').nonce)) | ||||||||||||||||
self.check_peer_info(pingtime=ping_delay, minping=ping_delay, pingwait=None) | ||||||||||||||||
|
||||||||||||||||
self.log.info('Check that peer is disconnected after ping timeout') | ||||||||||||||||
assert 'ping' not in no_pong_node.last_message | ||||||||||||||||
self.nodes[0].ping() | ||||||||||||||||
wait_until(lambda: 'ping' in no_pong_node.last_message) | ||||||||||||||||
with self.nodes[0].assert_debug_log(['ping timeout: 1201.000000s']): | ||||||||||||||||
self.mock_forward(20 * 60 + 1) | ||||||||||||||||
time.sleep(4) # peertimeout + 1 | ||||||||||||||||
|
||||||||||||||||
|
||||||||||||||||
if __name__ == '__main__': | ||||||||||||||||
PingPongTest().main() |
Uh oh!
There was an error while loading. Please reload this page.