7
7
from test_framework .messages import (
8
8
CBlockHeader ,
9
9
CInv ,
10
+ MAX_HEADERS_RESULTS ,
11
+ MAX_INV_SIZE ,
12
+ MAX_PROTOCOL_MESSAGE_LENGTH ,
10
13
msg_getdata ,
11
14
msg_headers ,
12
15
msg_inv ,
24
27
wait_until ,
25
28
)
26
29
27
- MSG_LIMIT = 4 * 1000 * 1000 # 4MB, per MAX_PROTOCOL_MESSAGE_LENGTH
28
- VALID_DATA_LIMIT = MSG_LIMIT - 5 # Account for the 5-byte length prefix
30
+ VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5 # Account for the 5-byte length prefix
29
31
30
32
class msg_unrecognized :
31
33
"""Nonsensical message. Modeled after similar types in test_framework.messages."""
@@ -53,11 +55,13 @@ def run_test(self):
53
55
self .test_checksum ()
54
56
self .test_size ()
55
57
self .test_msgtype ()
56
- self .test_large_inv ()
58
+ self .test_oversized_inv_msg ()
59
+ self .test_oversized_getdata_msg ()
60
+ self .test_oversized_headers_msg ()
57
61
self .test_resource_exhaustion ()
58
62
59
63
def test_buffer (self ):
60
- self .log .info ("Test message with header split across two buffers, should be received" )
64
+ self .log .info ("Test message with header split across two buffers is received" )
61
65
conn = self .nodes [0 ].add_p2p_connection (P2PDataStore ())
62
66
# Create valid message
63
67
msg = conn .build_message (msg_ping (nonce = 12345 ))
@@ -76,83 +80,94 @@ def test_buffer(self):
76
80
self .nodes [0 ].disconnect_p2ps ()
77
81
78
82
def test_magic_bytes (self ):
83
+ self .log .info ("Test message with invalid magic bytes disconnects peer" )
79
84
conn = self .nodes [0 ].add_p2p_connection (P2PDataStore ())
80
85
with self .nodes [0 ].assert_debug_log (['PROCESSMESSAGE: INVALID MESSAGESTART badmsg' ]):
81
86
msg = conn .build_message (msg_unrecognized (str_data = "d" ))
82
87
# modify magic bytes
83
88
msg = b'\xff ' * 4 + msg [4 :]
84
89
conn .send_raw_message (msg )
85
90
conn .wait_for_disconnect (timeout = 1 )
86
- self .nodes [0 ].disconnect_p2ps ()
91
+ self .nodes [0 ].disconnect_p2ps ()
87
92
88
93
def test_checksum (self ):
94
+ self .log .info ("Test message with invalid checksum logs an error" )
89
95
conn = self .nodes [0 ].add_p2p_connection (P2PDataStore ())
90
96
with self .nodes [0 ].assert_debug_log (['CHECKSUM ERROR (badmsg, 2 bytes), expected 78df0a04 was ffffffff' ]):
91
97
msg = conn .build_message (msg_unrecognized (str_data = "d" ))
92
98
# Checksum is after start bytes (4B), message type (12B), len (4B)
93
99
cut_len = 4 + 12 + 4
94
100
# modify checksum
95
101
msg = msg [:cut_len ] + b'\xff ' * 4 + msg [cut_len + 4 :]
96
- self . nodes [ 0 ]. p2p .send_raw_message (msg )
102
+ conn .send_raw_message (msg )
97
103
conn .sync_with_ping (timeout = 1 )
98
- self .nodes [0 ].disconnect_p2ps ()
104
+ self .nodes [0 ].disconnect_p2ps ()
99
105
100
106
def test_size (self ):
107
+ self .log .info ("Test message with oversized payload disconnects peer" )
101
108
conn = self .nodes [0 ].add_p2p_connection (P2PDataStore ())
102
109
with self .nodes [0 ].assert_debug_log (['' ]):
103
- # Create a message with oversized payload
104
110
msg = msg_unrecognized (str_data = "d" * (VALID_DATA_LIMIT + 1 ))
105
111
msg = conn .build_message (msg )
106
- self . nodes [ 0 ]. p2p .send_raw_message (msg )
112
+ conn .send_raw_message (msg )
107
113
conn .wait_for_disconnect (timeout = 1 )
108
- self .nodes [0 ].disconnect_p2ps ()
114
+ self .nodes [0 ].disconnect_p2ps ()
109
115
110
116
def test_msgtype (self ):
117
+ self .log .info ("Test message with invalid message type logs an error" )
111
118
conn = self .nodes [0 ].add_p2p_connection (P2PDataStore ())
112
119
with self .nodes [0 ].assert_debug_log (['PROCESSMESSAGE: ERRORS IN HEADER' ]):
113
120
msg = msg_unrecognized (str_data = "d" )
114
121
msg .msgtype = b'\xff ' * 12
115
122
msg = conn .build_message (msg )
116
123
# Modify msgtype
117
124
msg = msg [:7 ] + b'\x00 ' + msg [7 + 1 :]
118
- self . nodes [ 0 ]. p2p .send_raw_message (msg )
125
+ conn .send_raw_message (msg )
119
126
conn .sync_with_ping (timeout = 1 )
120
- self .nodes [0 ].disconnect_p2ps ()
121
-
122
- def test_large_inv (self ):
123
- conn = self .nodes [0 ].add_p2p_connection (P2PInterface ())
124
- with self .nodes [0 ].assert_debug_log (['Misbehaving' , '(0 -> 20): message inv size() = 50001' ]):
125
- msg = msg_inv ([CInv (MSG_TX , 1 )] * 50001 )
126
- conn .send_and_ping (msg )
127
- with self .nodes [0 ].assert_debug_log (['Misbehaving' , '(20 -> 40): message getdata size() = 50001' ]):
128
- msg = msg_getdata ([CInv (MSG_TX , 1 )] * 50001 )
129
- conn .send_and_ping (msg )
130
- with self .nodes [0 ].assert_debug_log (['Misbehaving' , '(40 -> 60): headers message size = 2001' ]):
131
- msg = msg_headers ([CBlockHeader ()] * 2001 )
132
- conn .send_and_ping (msg )
133
127
self .nodes [0 ].disconnect_p2ps ()
134
128
129
+ def test_oversized_msg (self , msg , size ):
130
+ msg_type = msg .msgtype .decode ('ascii' )
131
+ self .log .info ("Test {} message of size {} is logged as misbehaving" .format (msg_type , size ))
132
+ with self .nodes [0 ].assert_debug_log (['Misbehaving' , '{} message size = {}' .format (msg_type , size )]):
133
+ self .nodes [0 ].add_p2p_connection (P2PInterface ()).send_and_ping (msg )
134
+ self .nodes [0 ].disconnect_p2ps ()
135
+
136
+ def test_oversized_inv_msg (self ):
137
+ size = MAX_INV_SIZE + 1
138
+ self .test_oversized_msg (msg_inv ([CInv (MSG_TX , 1 )] * size ), size )
139
+
140
+ def test_oversized_getdata_msg (self ):
141
+ size = MAX_INV_SIZE + 1
142
+ self .test_oversized_msg (msg_getdata ([CInv (MSG_TX , 1 )] * size ), size )
143
+
144
+ def test_oversized_headers_msg (self ):
145
+ size = MAX_HEADERS_RESULTS + 1
146
+ self .test_oversized_msg (msg_headers ([CBlockHeader ()] * size ), size )
147
+
135
148
def test_resource_exhaustion (self ):
149
+ self .log .info ("Test node stays up despite many large junk messages" )
136
150
conn = self .nodes [0 ].add_p2p_connection (P2PDataStore ())
137
151
conn2 = self .nodes [0 ].add_p2p_connection (P2PDataStore ())
138
152
msg_at_size = msg_unrecognized (str_data = "b" * VALID_DATA_LIMIT )
139
- assert len (msg_at_size .serialize ()) == MSG_LIMIT
140
-
141
- self .log .info ("Sending a bunch of large, junk messages to test memory exhaustion. May take a bit..." )
153
+ assert len (msg_at_size .serialize ()) == MAX_PROTOCOL_MESSAGE_LENGTH
142
154
143
- # Run a bunch of times to test for memory exhaustion.
155
+ self . log . info ( "(a) Send 80 messages, each of maximum valid data size (4MB)" )
144
156
for _ in range (80 ):
145
157
conn .send_message (msg_at_size )
146
158
147
159
# Check that, even though the node is being hammered by nonsense from one
148
160
# connection, it can still service other peers in a timely way.
161
+ self .log .info ("(b) Check node still services peers in a timely way" )
149
162
for _ in range (20 ):
150
163
conn2 .sync_with_ping (timeout = 2 )
151
164
152
- # Peer 1, despite being served up a bunch of nonsense, should still be connected.
153
- self .log .info ("Waiting for node to drop junk messages." )
165
+ self .log .info ("(c) Wait for node to drop junk messages, while remaining connected" )
154
166
conn .sync_with_ping (timeout = 400 )
167
+
168
+ # Despite being served up a bunch of nonsense, the peers should still be connected.
155
169
assert conn .is_connected
170
+ assert conn2 .is_connected
156
171
self .nodes [0 ].disconnect_p2ps ()
157
172
158
173
0 commit comments