@@ -312,4 +312,81 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
312
312
loaded_snapshot_blockhash);
313
313
}
314
314
315
+ // ! Test LoadBlockIndex behavior when multiple chainstates are in use.
316
+ // !
317
+ // ! - First, verfiy that setBlockIndexCandidates is as expected when using a single,
318
+ // ! fully-validating chainstate.
319
+ // !
320
+ // ! - Then mark a region of the chain BLOCK_ASSUMED_VALID and introduce a second chainstate
321
+ // ! that will tolerate assumed-valid blocks. Run LoadBlockIndex() and ensure that the first
322
+ // ! chainstate only contains fully validated blocks and the other chainstate contains all blocks,
323
+ // ! even those assumed-valid.
324
+ // !
325
+ BOOST_FIXTURE_TEST_CASE (chainstatemanager_loadblockindex, TestChain100Setup)
326
+ {
327
+ ChainstateManager& chainman = *Assert (m_node.chainman );
328
+ CTxMemPool& mempool = *m_node.mempool ;
329
+ CChainState& cs1 = chainman.ActiveChainstate ();
330
+
331
+ int num_indexes{0 };
332
+ int num_assumed_valid{0 };
333
+ const int expected_assumed_valid{20 };
334
+ const int last_assumed_valid_idx{40 };
335
+ const int assumed_valid_start_idx = last_assumed_valid_idx - expected_assumed_valid;
336
+
337
+ CBlockIndex* validated_tip{nullptr };
338
+ CBlockIndex* assumed_tip{chainman.ActiveChain ().Tip ()};
339
+
340
+ auto reload_all_block_indexes = [&]() {
341
+ for (CChainState* cs : chainman.GetAll ()) {
342
+ LOCK (::cs_main);
343
+ cs->UnloadBlockIndex ();
344
+ BOOST_CHECK (cs->setBlockIndexCandidates .empty ());
345
+ }
346
+
347
+ WITH_LOCK (::cs_main, chainman.LoadBlockIndex ());
348
+ };
349
+
350
+ // Ensure that without any assumed-valid BlockIndex entries, all entries are considered
351
+ // tip candidates.
352
+ reload_all_block_indexes ();
353
+ BOOST_CHECK_EQUAL (cs1.setBlockIndexCandidates .size (), cs1.m_chain .Height () + 1 );
354
+
355
+ // Mark some region of the chain assumed-valid.
356
+ for (int i = 0 ; i <= cs1.m_chain .Height (); ++i) {
357
+ auto index = cs1.m_chain [i];
358
+
359
+ if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
360
+ index->nStatus = BlockStatus::BLOCK_VALID_TREE | BlockStatus::BLOCK_ASSUMED_VALID;
361
+ }
362
+
363
+ ++num_indexes;
364
+ if (index->IsAssumedValid ()) ++num_assumed_valid;
365
+
366
+ // Note the last fully-validated block as the expected validated tip.
367
+ if (i == (assumed_valid_start_idx - 1 )) {
368
+ validated_tip = index;
369
+ BOOST_CHECK (!index->IsAssumedValid ());
370
+ }
371
+ }
372
+
373
+ BOOST_CHECK_EQUAL (expected_assumed_valid, num_assumed_valid);
374
+
375
+ CChainState& cs2 = WITH_LOCK (::cs_main,
376
+ return chainman.InitializeChainstate (&mempool, GetRandHash ()));
377
+
378
+ reload_all_block_indexes ();
379
+
380
+ // The fully validated chain only has candidates up to the start of the assumed-valid
381
+ // blocks.
382
+ BOOST_CHECK_EQUAL (cs1.setBlockIndexCandidates .count (validated_tip), 1 );
383
+ BOOST_CHECK_EQUAL (cs1.setBlockIndexCandidates .count (assumed_tip), 0 );
384
+ BOOST_CHECK_EQUAL (cs1.setBlockIndexCandidates .size (), assumed_valid_start_idx);
385
+
386
+ // The assumed-valid tolerant chain has all blocks as candidates.
387
+ BOOST_CHECK_EQUAL (cs2.setBlockIndexCandidates .count (validated_tip), 1 );
388
+ BOOST_CHECK_EQUAL (cs2.setBlockIndexCandidates .count (assumed_tip), 1 );
389
+ BOOST_CHECK_EQUAL (cs2.setBlockIndexCandidates .size (), num_indexes);
390
+ }
391
+
315
392
BOOST_AUTO_TEST_SUITE_END ()
0 commit comments