@@ -116,6 +116,7 @@ def __init__(self):
116
116
117
117
self .block_announced = False
118
118
self .last_blockhash_announced = None
119
+ self .recent_headers_announced = []
119
120
120
121
def send_get_data (self , block_hashes ):
121
122
"""Request data for a list of block hashes."""
@@ -163,40 +164,45 @@ def on_inv(self, message):
163
164
def on_headers (self , message ):
164
165
if len (message .headers ):
165
166
self .block_announced = True
166
- message .headers [- 1 ].calc_sha256 ()
167
+ for x in message .headers :
168
+ x .calc_sha256 ()
169
+ # append because headers may be announced over multiple messages.
170
+ self .recent_headers_announced .append (x .sha256 )
167
171
self .last_blockhash_announced = message .headers [- 1 ].sha256
168
172
169
- def clear_last_announcement (self ):
173
+ def clear_block_announcements (self ):
170
174
with mininode_lock :
171
175
self .block_announced = False
172
176
self .last_message .pop ("inv" , None )
173
177
self .last_message .pop ("headers" , None )
178
+ self .recent_headers_announced = []
174
179
175
- def check_last_announcement (self , headers = None , inv = None ):
176
- """Test whether the last announcement received had the right header or the right inv.
177
180
178
- inv and headers should be lists of block hashes."""
181
+ def check_last_headers_announcement (self , headers ):
182
+ """Test whether the last headers announcements received are right.
183
+ Headers may be announced across more than one message."""
184
+ test_function = lambda : (len (self .recent_headers_announced ) >= len (headers ))
185
+ wait_until (test_function , timeout = 60 , lock = mininode_lock )
186
+ with mininode_lock :
187
+ assert_equal (self .recent_headers_announced , headers )
188
+ self .block_announced = False
189
+ self .last_message .pop ("headers" , None )
190
+ self .recent_headers_announced = []
191
+
192
+ def check_last_inv_announcement (self , inv ):
193
+ """Test whether the last announcement received had the right inv.
194
+ inv should be a list of block hashes."""
179
195
180
196
test_function = lambda : self .block_announced
181
197
wait_until (test_function , timeout = 60 , lock = mininode_lock )
182
198
183
199
with mininode_lock :
184
- self .block_announced = False
185
-
186
200
compare_inv = []
187
201
if "inv" in self .last_message :
188
202
compare_inv = [x .hash for x in self .last_message ["inv" ].inv ]
189
- if inv is not None :
190
- assert_equal (compare_inv , inv )
191
-
192
- compare_headers = []
193
- if "headers" in self .last_message :
194
- compare_headers = [x .sha256 for x in self .last_message ["headers" ].headers ]
195
- if headers is not None :
196
- assert_equal (compare_headers , headers )
197
-
203
+ assert_equal (compare_inv , inv )
204
+ self .block_announced = False
198
205
self .last_message .pop ("inv" , None )
199
- self .last_message .pop ("headers" , None )
200
206
201
207
class SendHeadersTest (BitcoinTestFramework ):
202
208
def set_test_params (self ):
@@ -206,8 +212,8 @@ def set_test_params(self):
206
212
def mine_blocks (self , count ):
207
213
"""Mine count blocks and return the new tip."""
208
214
209
- # Clear out last block announcement from each p2p listener
210
- [x .clear_last_announcement () for x in self .nodes [0 ].p2ps ]
215
+ # Clear out block announcements from each p2p listener
216
+ [x .clear_block_announcements () for x in self .nodes [0 ].p2ps ]
211
217
self .nodes [0 ].generate (count )
212
218
return int (self .nodes [0 ].getbestblockhash (), 16 )
213
219
@@ -222,7 +228,7 @@ def mine_reorg(self, length):
222
228
sync_blocks (self .nodes , wait = 0.1 )
223
229
for x in self .nodes [0 ].p2ps :
224
230
x .wait_for_block_announcement (int (self .nodes [0 ].getbestblockhash (), 16 ))
225
- x .clear_last_announcement ()
231
+ x .clear_block_announcements ()
226
232
227
233
tip_height = self .nodes [1 ].getblockcount ()
228
234
hash_to_invalidate = self .nodes [1 ].getblockhash (tip_height - (length - 1 ))
@@ -255,25 +261,25 @@ def test_null_locators(self, test_node, inv_node):
255
261
tip = self .nodes [0 ].getblockheader (self .nodes [0 ].generate (1 )[0 ])
256
262
tip_hash = int (tip ["hash" ], 16 )
257
263
258
- inv_node .check_last_announcement (inv = [tip_hash ], headers = [ ])
259
- test_node .check_last_announcement (inv = [tip_hash ], headers = [ ])
264
+ inv_node .check_last_inv_announcement (inv = [tip_hash ])
265
+ test_node .check_last_inv_announcement (inv = [tip_hash ])
260
266
261
267
self .log .info ("Verify getheaders with null locator and valid hashstop returns headers." )
262
- test_node .clear_last_announcement ()
268
+ test_node .clear_block_announcements ()
263
269
test_node .send_get_headers (locator = [], hashstop = tip_hash )
264
- test_node .check_last_announcement (headers = [tip_hash ])
270
+ test_node .check_last_headers_announcement (headers = [tip_hash ])
265
271
266
272
self .log .info ("Verify getheaders with null locator and invalid hashstop does not return headers." )
267
273
block = create_block (int (tip ["hash" ], 16 ), create_coinbase (tip ["height" ] + 1 ), tip ["mediantime" ] + 1 )
268
274
block .solve ()
269
275
test_node .send_header_for_blocks ([block ])
270
- test_node .clear_last_announcement ()
276
+ test_node .clear_block_announcements ()
271
277
test_node .send_get_headers (locator = [], hashstop = int (block .hash , 16 ))
272
278
test_node .sync_with_ping ()
273
279
assert_equal (test_node .block_announced , False )
274
- inv_node .clear_last_announcement ()
280
+ inv_node .clear_block_announcements ()
275
281
test_node .send_message (msg_block (block ))
276
- inv_node .check_last_announcement (inv = [int (block .hash , 16 )], headers = [ ])
282
+ inv_node .check_last_inv_announcement (inv = [int (block .hash , 16 )])
277
283
278
284
def test_nonnull_locators (self , test_node , inv_node ):
279
285
tip = int (self .nodes [0 ].getbestblockhash (), 16 )
@@ -284,8 +290,8 @@ def test_nonnull_locators(self, test_node, inv_node):
284
290
for i in range (4 ):
285
291
old_tip = tip
286
292
tip = self .mine_blocks (1 )
287
- inv_node .check_last_announcement (inv = [tip ], headers = [ ])
288
- test_node .check_last_announcement (inv = [tip ], headers = [ ])
293
+ inv_node .check_last_inv_announcement (inv = [tip ])
294
+ test_node .check_last_inv_announcement (inv = [tip ])
289
295
# Try a few different responses; none should affect next announcement
290
296
if i == 0 :
291
297
# first request the block
@@ -296,7 +302,7 @@ def test_nonnull_locators(self, test_node, inv_node):
296
302
test_node .send_get_headers (locator = [old_tip ], hashstop = tip )
297
303
test_node .send_get_data ([tip ])
298
304
test_node .wait_for_block (tip )
299
- test_node .clear_last_announcement () # since we requested headers...
305
+ test_node .clear_block_announcements () # since we requested headers...
300
306
elif i == 2 :
301
307
# this time announce own block via headers
302
308
height = self .nodes [0 ].getblockcount ()
@@ -308,8 +314,8 @@ def test_nonnull_locators(self, test_node, inv_node):
308
314
test_node .wait_for_getdata ([new_block .sha256 ])
309
315
test_node .send_message (msg_block (new_block ))
310
316
test_node .sync_with_ping () # make sure this block is processed
311
- inv_node .clear_last_announcement ()
312
- test_node .clear_last_announcement ()
317
+ inv_node .clear_block_announcements ()
318
+ test_node .clear_block_announcements ()
313
319
314
320
self .log .info ("Part 1: success!" )
315
321
self .log .info ("Part 2: announce blocks with headers after sendheaders message..." )
@@ -323,8 +329,8 @@ def test_nonnull_locators(self, test_node, inv_node):
323
329
324
330
# Now that we've synced headers, headers announcements should work
325
331
tip = self .mine_blocks (1 )
326
- inv_node .check_last_announcement (inv = [tip ], headers = [ ])
327
- test_node .check_last_announcement (headers = [tip ])
332
+ inv_node .check_last_inv_announcement (inv = [tip ])
333
+ test_node .check_last_headers_announcement (headers = [tip ])
328
334
329
335
height = self .nodes [0 ].getblockcount () + 1
330
336
block_time += 10 # Advance far enough ahead
@@ -368,8 +374,8 @@ def test_nonnull_locators(self, test_node, inv_node):
368
374
assert "inv" not in inv_node .last_message
369
375
assert "headers" not in inv_node .last_message
370
376
tip = self .mine_blocks (1 )
371
- inv_node .check_last_announcement (inv = [tip ], headers = [ ])
372
- test_node .check_last_announcement (headers = [tip ])
377
+ inv_node .check_last_inv_announcement (inv = [tip ])
378
+ test_node .check_last_headers_announcement (headers = [tip ])
373
379
height += 1
374
380
block_time += 1
375
381
@@ -383,16 +389,16 @@ def test_nonnull_locators(self, test_node, inv_node):
383
389
# First try mining a reorg that can propagate with header announcement
384
390
new_block_hashes = self .mine_reorg (length = 7 )
385
391
tip = new_block_hashes [- 1 ]
386
- inv_node .check_last_announcement (inv = [tip ], headers = [ ])
387
- test_node .check_last_announcement (headers = new_block_hashes )
392
+ inv_node .check_last_inv_announcement (inv = [tip ])
393
+ test_node .check_last_headers_announcement (headers = new_block_hashes )
388
394
389
395
block_time += 8
390
396
391
397
# Mine a too-large reorg, which should be announced with a single inv
392
398
new_block_hashes = self .mine_reorg (length = 8 )
393
399
tip = new_block_hashes [- 1 ]
394
- inv_node .check_last_announcement (inv = [tip ], headers = [ ])
395
- test_node .check_last_announcement (inv = [tip ], headers = [ ])
400
+ inv_node .check_last_inv_announcement (inv = [tip ])
401
+ test_node .check_last_inv_announcement (inv = [tip ])
396
402
397
403
block_time += 9
398
404
@@ -401,15 +407,15 @@ def test_nonnull_locators(self, test_node, inv_node):
401
407
402
408
# Use getblocks/getdata
403
409
test_node .send_getblocks (locator = [fork_point ])
404
- test_node .check_last_announcement (inv = new_block_hashes , headers = [] )
410
+ test_node .check_last_inv_announcement (inv = new_block_hashes )
405
411
test_node .send_get_data (new_block_hashes )
406
412
test_node .wait_for_block (new_block_hashes [- 1 ])
407
413
408
414
for i in range (3 ):
409
415
# Mine another block, still should get only an inv
410
416
tip = self .mine_blocks (1 )
411
- inv_node .check_last_announcement (inv = [tip ], headers = [ ])
412
- test_node .check_last_announcement (inv = [tip ], headers = [ ])
417
+ inv_node .check_last_inv_announcement (inv = [tip ])
418
+ test_node .check_last_inv_announcement (inv = [tip ])
413
419
if i == 0 :
414
420
self .log .debug ("Just get the data -- shouldn't cause headers announcements to resume" )
415
421
test_node .send_get_data ([tip ])
@@ -431,8 +437,8 @@ def test_nonnull_locators(self, test_node, inv_node):
431
437
test_node .sync_with_ping ()
432
438
# New blocks should now be announced with header
433
439
tip = self .mine_blocks (1 )
434
- inv_node .check_last_announcement (inv = [tip ], headers = [ ])
435
- test_node .check_last_announcement (headers = [tip ])
440
+ inv_node .check_last_inv_announcement (inv = [tip ])
441
+ test_node .check_last_headers_announcement (headers = [tip ])
436
442
437
443
self .log .info ("Part 3: success!" )
438
444
0 commit comments