Skip to content

Commit 60bec3c

Browse files
committed
index: Use first block from locator instead of looking for fork point
The index sync code has logic to go back the chain to the forking point, while also updating index-specific state, which is necessary to prevent possible corruption of the coinstatsindex. Also add a test for this (a reorg happens while the index is deactivated) that would not pass before this change.
1 parent 594f05d commit 60bec3c

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

src/index/base.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,13 @@ bool BaseIndex::Init()
9292
if (locator.IsNull()) {
9393
SetBestBlockIndex(nullptr);
9494
} else {
95-
SetBestBlockIndex(m_chainstate->FindForkInGlobalIndex(locator));
95+
// Setting the best block to the locator's top block. If it is not part of the
96+
// best chain, we will rewind to the fork point during index sync
97+
const CBlockIndex* locator_index{m_chainstate->m_blockman.LookupBlockIndex(locator.vHave.at(0))};
98+
if (!locator_index) {
99+
return InitError(strprintf(Untranslated("%s: best block of the index not found. Please rebuild the index."), GetName()));
100+
}
101+
SetBestBlockIndex(locator_index);
96102
}
97103

98104
// Note: this will latch to true immediately if the user starts up with an empty

test/functional/feature_coinstatsindex.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def run_test(self):
5252
self._test_use_index_option()
5353
self._test_reorg_index()
5454
self._test_index_rejects_hash_serialized()
55+
self._test_init_index_after_reorg()
5556

5657
def block_sanity_check(self, block_info):
5758
block_subsidy = 50
@@ -60,6 +61,9 @@ def block_sanity_check(self, block_info):
6061
block_info['new_outputs_ex_coinbase'] + block_info['coinbase'] + block_info['unspendable']
6162
)
6263

64+
def sync_index_node(self):
65+
self.wait_until(lambda: self.nodes[1].getindexinfo()['coinstatsindex']['synced'] is True)
66+
6367
def _test_coin_stats_index(self):
6468
node = self.nodes[0]
6569
index_node = self.nodes[1]
@@ -296,6 +300,26 @@ def _test_index_rejects_hash_serialized(self):
296300
for use_index in {True, False, None}:
297301
assert_raises_rpc_error(-8, msg, self.nodes[1].gettxoutsetinfo, hash_type='hash_serialized_2', hash_or_height=111, use_index=use_index)
298302

303+
def _test_init_index_after_reorg(self):
304+
self.log.info("Test a reorg while the index is deactivated")
305+
index_node = self.nodes[1]
306+
block = self.nodes[0].getbestblockhash()
307+
self.generate(index_node, 2, sync_fun=self.no_op)
308+
self.sync_index_node()
309+
310+
# Restart without index
311+
self.restart_node(1, extra_args=[])
312+
self.connect_nodes(0, 1)
313+
index_node.invalidateblock(block)
314+
self.generatetoaddress(index_node, 5, getnewdestination()[2])
315+
res = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=False)
316+
317+
# Restart with index that still has its best block on the old chain
318+
self.restart_node(1, extra_args=self.extra_args[1])
319+
self.sync_index_node()
320+
res1 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=True)
321+
assert_equal(res["muhash"], res1["muhash"])
322+
299323

300324
if __name__ == '__main__':
301325
CoinStatsIndexTest().main()

0 commit comments

Comments
 (0)