test: index with an unclean restart after a reorg
What changed, and why it matters
This commit adds a new automated test for Bitcoin Core's coin statistics index. It checks that after a blockchain reorganization and an unclean shutdown, the index correctly rewinds to the last safely saved point. The commit itself is only a test file change and does not modify production code.
No action required for this test-only commit. Review the preceding commit to identify and assess the actual production fix that this test validates.
Security signals we found
Regression test for index state consistency after unclean shutdown and reorg
Commit message indicates the test exercises a fix in the immediately preceding commit
Evidence from the diff
The diff appends a new functional test case to test/functional/feature_coinstatsindex.py. The test restarts an index node, generates two blocks, invalidates the tip to simulate a reorg, mines a replacement block, kills the node process uncleanly, restarts it, and asserts that the coinstats index’s best_block_height rewinds to the height recorded before the reorg. The commit message states the test fails without the preceding commit, implying this is regression coverage for a recently fixed index-persistence bug.
Changed components
test/functional/feature_coinstatsindex.pyInspect captured patch +15 / −0
diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py
index 73f3e393..13f321bc 100755
--- a/test/functional/feature_coinstatsindex.py
+++ b/test/functional/feature_coinstatsindex.py
@@ -321,6 +321,21 @@ class CoinStatsIndexTest(BitcoinTestFramework):
res1 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=True)
assert_equal(res["muhash"], res1["muhash"])
+ self.log.info("Test index with an unclean restart after a reorg")
+ self.restart_node(1, extra_args=self.extra_args[1])
+ committed_height = index_node.getblockcount()
+ self.generate(index_node, 2, sync_fun=self.no_op)
+ self.sync_index_node()
+ block2 = index_node.getbestblockhash()
+ index_node.invalidateblock(block2)
+ self.generatetoaddress(index_node, 1, getnewdestination()[2], sync_fun=self.no_op)
+ self.sync_index_node()
+ index_node.kill_process()
+ self.start_node(1, extra_args=self.extra_args[1])
+ self.sync_index_node()
+ # Because of the unclean shutdown above, indexes reset to the point we last committed them to disk.
+ assert_equal(index_node.getindexinfo()['coinstatsindex']['best_block_height'], committed_height)
+
if __name__ == '__main__':
CoinStatsIndexTest(__file__).main()
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.