Skip to content

Commit e91cf4b

Browse files
committed
Add test for handling of unconnecting headers
1 parent 96fa953 commit e91cf4b

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

qa/rpc-tests/sendheaders.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@
6363
Expect: getdata request for 14 more blocks.
6464
f. Announce 1 more header that builds on that fork.
6565
Expect: no response.
66+
67+
Part 5: Test handling of headers that don't connect.
68+
a. Repeat 10 times:
69+
1. Announce a header that doesn't connect.
70+
Expect: getheaders message
71+
2. Send headers chain.
72+
Expect: getdata for the missing blocks, tip update.
73+
b. Then send 9 more headers that don't connect.
74+
Expect: getheaders message each time.
75+
c. Announce a header that does connect.
76+
Expect: no response.
77+
d. Announce 49 headers that don't connect.
78+
Expect: getheaders message each time.
79+
e. Announce one more that doesn't connect.
80+
Expect: disconnect.
6681
'''
6782

6883
class BaseNode(NodeConnCB):
@@ -77,6 +92,8 @@ def __init__(self):
7792
self.last_getdata = None
7893
self.sleep_time = 0.05
7994
self.block_announced = False
95+
self.last_getheaders = None
96+
self.disconnected = False
8097

8198
def clear_last_announcement(self):
8299
with mininode_lock:
@@ -127,6 +144,12 @@ def on_getdata(self, conn, message):
127144
def on_pong(self, conn, message):
128145
self.last_pong = message
129146

147+
def on_getheaders(self, conn, message):
148+
self.last_getheaders = message
149+
150+
def on_close(self, conn):
151+
self.disconnected = True
152+
130153
# Test whether the last announcement we received had the
131154
# right header or the right inv
132155
# inv and headers should be lists of block hashes
@@ -178,6 +201,11 @@ def wait_for_block(self, blockhash, timeout=60):
178201
self.sync(test_function, timeout)
179202
return
180203

204+
def wait_for_getheaders(self, timeout=60):
205+
test_function = lambda: self.last_getheaders != None
206+
self.sync(test_function, timeout)
207+
return
208+
181209
def wait_for_getdata(self, hash_list, timeout=60):
182210
if hash_list == []:
183211
return
@@ -186,6 +214,11 @@ def wait_for_getdata(self, hash_list, timeout=60):
186214
self.sync(test_function, timeout)
187215
return
188216

217+
def wait_for_disconnect(self, timeout=60):
218+
test_function = lambda: self.disconnected
219+
self.sync(test_function, timeout)
220+
return
221+
189222
def send_header_for_blocks(self, new_blocks):
190223
headers_message = msg_headers()
191224
headers_message.headers = [ CBlockHeader(b) for b in new_blocks ]
@@ -510,6 +543,78 @@ def run_test(self):
510543

511544
print("Part 4: success!")
512545

546+
# Now deliver all those blocks we announced.
547+
[ test_node.send_message(msg_block(x)) for x in blocks ]
548+
549+
print("Part 5: Testing handling of unconnecting headers")
550+
# First we test that receipt of an unconnecting header doesn't prevent
551+
# chain sync.
552+
for i in range(10):
553+
test_node.last_getdata = None
554+
blocks = []
555+
# Create two more blocks.
556+
for j in range(2):
557+
blocks.append(create_block(tip, create_coinbase(height), block_time))
558+
blocks[-1].solve()
559+
tip = blocks[-1].sha256
560+
block_time += 1
561+
height += 1
562+
# Send the header of the second block -> this won't connect.
563+
with mininode_lock:
564+
test_node.last_getheaders = None
565+
test_node.send_header_for_blocks([blocks[1]])
566+
test_node.wait_for_getheaders(timeout=1)
567+
test_node.send_header_for_blocks(blocks)
568+
test_node.wait_for_getdata([x.sha256 for x in blocks])
569+
[ test_node.send_message(msg_block(x)) for x in blocks ]
570+
test_node.sync_with_ping()
571+
assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256)
572+
573+
blocks = []
574+
# Now we test that if we repeatedly don't send connecting headers, we
575+
# don't go into an infinite loop trying to get them to connect.
576+
MAX_UNCONNECTING_HEADERS = 10
577+
for j in range(MAX_UNCONNECTING_HEADERS+1):
578+
blocks.append(create_block(tip, create_coinbase(height), block_time))
579+
blocks[-1].solve()
580+
tip = blocks[-1].sha256
581+
block_time += 1
582+
height += 1
583+
584+
for i in range(1, MAX_UNCONNECTING_HEADERS):
585+
# Send a header that doesn't connect, check that we get a getheaders.
586+
with mininode_lock:
587+
test_node.last_getheaders = None
588+
test_node.send_header_for_blocks([blocks[i]])
589+
test_node.wait_for_getheaders(timeout=1)
590+
591+
# Next header will connect, should re-set our count:
592+
test_node.send_header_for_blocks([blocks[0]])
593+
594+
# Remove the first two entries (blocks[1] would connect):
595+
blocks = blocks[2:]
596+
597+
# Now try to see how many unconnecting headers we can send
598+
# before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS
599+
for i in range(5*MAX_UNCONNECTING_HEADERS - 1):
600+
# Send a header that doesn't connect, check that we get a getheaders.
601+
with mininode_lock:
602+
test_node.last_getheaders = None
603+
test_node.send_header_for_blocks([blocks[i%len(blocks)]])
604+
test_node.wait_for_getheaders(timeout=1)
605+
606+
# Eventually this stops working.
607+
with mininode_lock:
608+
self.last_getheaders = None
609+
test_node.send_header_for_blocks([blocks[-1]])
610+
611+
# Should get disconnected
612+
test_node.wait_for_disconnect()
613+
with mininode_lock:
614+
self.last_getheaders = True
615+
616+
print("Part 5: success!")
617+
513618
# Finally, check that the inv node never received a getdata request,
514619
# throughout the test
515620
assert_equal(inv_node.last_getdata, None)

0 commit comments

Comments
 (0)