@@ -51,6 +51,7 @@ def skip_test_if_missing_module(self):
5151 def run_test (self ):
5252 self ._test_coin_stats_index ()
5353 self ._test_use_index_option ()
54+ self ._test_reorg_index ()
5455
5556 def block_sanity_check (self , block_info ):
5657 block_subsidy = 50
@@ -247,6 +248,56 @@ def _test_use_index_option(self):
247248 del res ['disk_size' ], option_res ['disk_size' ]
248249 assert_equal (res , option_res )
249250
251+ def _test_reorg_index (self ):
252+ self .log .info ("Test that index can handle reorgs" )
253+
254+ # Generate two block, let the index catch up, then invalidate the blocks
255+ index_node = self .nodes [1 ]
256+ reorg_blocks = index_node .generatetoaddress (2 , index_node .getnewaddress ())
257+ reorg_block = reorg_blocks [1 ]
258+ self .wait_until (lambda : not try_rpc (- 32603 , "Unable to read UTXO set" , index_node .gettxoutsetinfo , 'muhash' ))
259+ res_invalid = index_node .gettxoutsetinfo ('muhash' )
260+ index_node .invalidateblock (reorg_blocks [0 ])
261+ assert_equal (index_node .gettxoutsetinfo ('muhash' )['height' ], 110 )
262+
263+ # Add two new blocks
264+ block = index_node .generate (2 )[1 ]
265+ self .wait_until (lambda : not try_rpc (- 32603 , "Unable to read UTXO set" , index_node .gettxoutsetinfo , 'muhash' ))
266+ res = index_node .gettxoutsetinfo (hash_type = 'muhash' , hash_or_height = None , use_index = False )
267+
268+ # Test that the result of the reorged block is not returned for its old block height
269+ res2 = index_node .gettxoutsetinfo (hash_type = 'muhash' , hash_or_height = 112 )
270+ assert_equal (res ["bestblock" ], block )
271+ assert_equal (res ["muhash" ], res2 ["muhash" ])
272+ assert (res ["muhash" ] != res_invalid ["muhash" ])
273+
274+ # Test that requesting reorged out block by hash is still returning correct results
275+ res_invalid2 = index_node .gettxoutsetinfo (hash_type = 'muhash' , hash_or_height = reorg_block )
276+ assert_equal (res_invalid2 ["muhash" ], res_invalid ["muhash" ])
277+ assert (res ["muhash" ] != res_invalid2 ["muhash" ])
278+
279+ # Add another block, so we don't depend on reconsiderblock remembering which
280+ # blocks were touched by invalidateblock
281+ index_node .generate (1 )
282+
283+ # Ensure that removing and re-adding blocks yields consistent results
284+ block = index_node .getblockhash (99 )
285+ index_node .invalidateblock (block )
286+ self .wait_until (lambda : not try_rpc (- 32603 , "Unable to read UTXO set" , index_node .gettxoutsetinfo , 'muhash' ))
287+ index_node .reconsiderblock (block )
288+ self .wait_until (lambda : not try_rpc (- 32603 , "Unable to read UTXO set" , index_node .gettxoutsetinfo , 'muhash' ))
289+ res3 = index_node .gettxoutsetinfo (hash_type = 'muhash' , hash_or_height = 112 )
290+ assert_equal (res2 , res3 )
291+
292+ self .log .info ("Test that a node aware of stale blocks syncs them as well" )
293+ node = self .nodes [0 ]
294+ # Ensure the node is aware of a stale block prior to restart
295+ node .getblock (reorg_block )
296+
297+ self .restart_node (0 , ["-coinstatsindex" ])
298+ self .wait_until (lambda : not try_rpc (- 32603 , "Unable to read UTXO set" , node .gettxoutsetinfo , 'muhash' ))
299+ assert_raises_rpc_error (- 32603 , "Unable to read UTXO set" , node .gettxoutsetinfo , 'muhash' , reorg_block )
300+
250301
251302if __name__ == '__main__' :
252303 CoinStatsIndexTest ().main ()
0 commit comments