40
40
it's missing an intermediate block.
41
41
Node1 should reorg to this longer chain.
42
42
43
+ 4b.Send 288 more blocks on the longer chain.
44
+ Node0 should process all but the last block (too far ahead in height).
45
+ Send all headers to Node1, and then send the last block in that chain.
46
+ Node1 should accept the block because it's coming from a whitelisted peer.
47
+
43
48
5. Send a duplicate of the block in #3 to Node0.
44
49
Node0 should not process the block because it is unrequested, and stay on
45
50
the shorter chain.
@@ -59,6 +64,8 @@ def __init__(self):
59
64
NodeConnCB .__init__ (self )
60
65
self .create_callback_map ()
61
66
self .connection = None
67
+ self .ping_counter = 1
68
+ self .last_pong = msg_pong ()
62
69
63
70
def add_connection (self , conn ):
64
71
self .connection = conn
@@ -82,6 +89,24 @@ def wait_for_verack(self):
82
89
def send_message (self , message ):
83
90
self .connection .send_message (message )
84
91
92
+ def on_pong (self , conn , message ):
93
+ self .last_pong = message
94
+
95
+ # Sync up with the node after delivery of a block
96
+ def sync_with_ping (self , timeout = 30 ):
97
+ self .connection .send_message (msg_ping (nonce = self .ping_counter ))
98
+ received_pong = False
99
+ sleep_time = 0.05
100
+ while not received_pong and timeout > 0 :
101
+ time .sleep (sleep_time )
102
+ timeout -= sleep_time
103
+ with mininode_lock :
104
+ if self .last_pong .nonce == self .ping_counter :
105
+ received_pong = True
106
+ self .ping_counter += 1
107
+ return received_pong
108
+
109
+
85
110
class AcceptBlockTest (BitcoinTestFramework ):
86
111
def add_options (self , parser ):
87
112
parser .add_option ("--testbinary" , dest = "testbinary" ,
@@ -126,13 +151,15 @@ def run_test(self):
126
151
# 2. Send one block that builds on each tip.
127
152
# This should be accepted.
128
153
blocks_h2 = [] # the height 2 blocks on each node's chain
154
+ block_time = time .time () + 1
129
155
for i in xrange (2 ):
130
- blocks_h2 .append (create_block (tips [i ], create_coinbase (), time . time () + 1 ))
156
+ blocks_h2 .append (create_block (tips [i ], create_coinbase (), block_time ))
131
157
blocks_h2 [i ].solve ()
158
+ block_time += 1
132
159
test_node .send_message (msg_block (blocks_h2 [0 ]))
133
160
white_node .send_message (msg_block (blocks_h2 [1 ]))
134
161
135
- time . sleep ( 1 )
162
+ [ x . sync_with_ping () for x in [ test_node , white_node ] ]
136
163
assert_equal (self .nodes [0 ].getblockcount (), 2 )
137
164
assert_equal (self .nodes [1 ].getblockcount (), 2 )
138
165
print "First height 2 block accepted by both nodes"
@@ -145,7 +172,7 @@ def run_test(self):
145
172
test_node .send_message (msg_block (blocks_h2f [0 ]))
146
173
white_node .send_message (msg_block (blocks_h2f [1 ]))
147
174
148
- time . sleep ( 1 ) # Give time to process the block
175
+ [ x . sync_with_ping () for x in [ test_node , white_node ] ]
149
176
for x in self .nodes [0 ].getchaintips ():
150
177
if x ['hash' ] == blocks_h2f [0 ].hash :
151
178
assert_equal (x ['status' ], "headers-only" )
@@ -164,7 +191,7 @@ def run_test(self):
164
191
test_node .send_message (msg_block (blocks_h3 [0 ]))
165
192
white_node .send_message (msg_block (blocks_h3 [1 ]))
166
193
167
- time . sleep ( 1 )
194
+ [ x . sync_with_ping () for x in [ test_node , white_node ] ]
168
195
# Since the earlier block was not processed by node0, the new block
169
196
# can't be fully validated.
170
197
for x in self .nodes [0 ].getchaintips ():
@@ -182,6 +209,45 @@ def run_test(self):
182
209
assert_equal (self .nodes [1 ].getblockcount (), 3 )
183
210
print "Successfully reorged to length 3 chain from whitelisted peer"
184
211
212
+ # 4b. Now mine 288 more blocks and deliver; all should be processed but
213
+ # the last (height-too-high) on node0. Node1 should process the tip if
214
+ # we give it the headers chain leading to the tip.
215
+ tips = blocks_h3
216
+ headers_message = msg_headers ()
217
+ all_blocks = [] # node0's blocks
218
+ for j in xrange (2 ):
219
+ for i in xrange (288 ):
220
+ next_block = create_block (tips [j ].sha256 , create_coinbase (), tips [j ].nTime + 1 )
221
+ next_block .solve ()
222
+ if j == 0 :
223
+ test_node .send_message (msg_block (next_block ))
224
+ all_blocks .append (next_block )
225
+ else :
226
+ headers_message .headers .append (CBlockHeader (next_block ))
227
+ tips [j ] = next_block
228
+
229
+ time .sleep (2 )
230
+ for x in all_blocks :
231
+ try :
232
+ self .nodes [0 ].getblock (x .hash )
233
+ if x == all_blocks [287 ]:
234
+ raise AssertionError ("Unrequested block too far-ahead should have been ignored" )
235
+ except :
236
+ if x == all_blocks [287 ]:
237
+ print "Unrequested block too far-ahead not processed"
238
+ else :
239
+ raise AssertionError ("Unrequested block with more work should have been accepted" )
240
+
241
+ headers_message .headers .pop () # Ensure the last block is unrequested
242
+ white_node .send_message (headers_message ) # Send headers leading to tip
243
+ white_node .send_message (msg_block (tips [1 ])) # Now deliver the tip
244
+ try :
245
+ white_node .sync_with_ping ()
246
+ self .nodes [1 ].getblock (tips [1 ].hash )
247
+ print "Unrequested block far ahead of tip accepted from whitelisted peer"
248
+ except :
249
+ raise AssertionError ("Unrequested block from whitelisted peer not accepted" )
250
+
185
251
# 5. Test handling of unrequested block on the node that didn't process
186
252
# Should still not be processed (even though it has a child that has more
187
253
# work).
@@ -192,7 +258,7 @@ def run_test(self):
192
258
# the node processes it and incorrectly advances the tip).
193
259
# But this would be caught later on, when we verify that an inv triggers
194
260
# a getdata request for this block.
195
- time . sleep ( 1 )
261
+ test_node . sync_with_ping ( )
196
262
assert_equal (self .nodes [0 ].getblockcount (), 2 )
197
263
print "Unrequested block that would complete more-work chain was ignored"
198
264
@@ -204,21 +270,20 @@ def run_test(self):
204
270
test_node .last_getdata = None
205
271
test_node .send_message (msg_inv ([CInv (2 , blocks_h3 [0 ].sha256 )]))
206
272
207
- time . sleep ( 1 )
273
+ test_node . sync_with_ping ( )
208
274
with mininode_lock :
209
275
getdata = test_node .last_getdata
210
276
211
- # Check that the getdata is for the right block
212
- assert_equal (len (getdata .inv ), 1 )
277
+ # Check that the getdata includes the right block
213
278
assert_equal (getdata .inv [0 ].hash , blocks_h2f [0 ].sha256 )
214
279
print "Inv at tip triggered getdata for unprocessed block"
215
280
216
281
# 7. Send the missing block for the third time (now it is requested)
217
282
test_node .send_message (msg_block (blocks_h2f [0 ]))
218
283
219
- time . sleep ( 1 )
220
- assert_equal (self .nodes [0 ].getblockcount (), 3 )
221
- print "Successfully reorged to length 3 chain from non-whitelisted peer"
284
+ test_node . sync_with_ping ( )
285
+ assert_equal (self .nodes [0 ].getblockcount (), 290 )
286
+ print "Successfully reorged to longer chain from non-whitelisted peer"
222
287
223
288
[ c .disconnect_node () for c in connections ]
224
289
0 commit comments