10
10
#include < sync.h>
11
11
#include < test/util/chainstate.h>
12
12
#include < test/util/setup_common.h>
13
+ #include < timedata.h>
13
14
#include < uint256.h>
14
15
#include < validation.h>
15
16
#include < validationinterface.h>
@@ -155,12 +156,32 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
155
156
}
156
157
157
158
struct SnapshotTestSetup : TestChain100Setup {
159
+ // Run with coinsdb on the filesystem to support, e.g., moving invalidated
160
+ // chainstate dirs to "*_invalid".
161
+ //
162
+ // Note that this means the tests run considerably slower than in-memory DB
163
+ // tests, but we can't otherwise test this functionality since it relies on
164
+ // destructive filesystem operations.
165
+ SnapshotTestSetup () : TestChain100Setup{
166
+ {},
167
+ {},
168
+ /* coins_db_in_memory=*/ false ,
169
+ /* block_tree_db_in_memory=*/ false ,
170
+ }
171
+ {
172
+ }
173
+
158
174
std::tuple<Chainstate*, Chainstate*> SetupSnapshot ()
159
175
{
160
176
ChainstateManager& chainman = *Assert (m_node.chainman );
161
177
162
178
BOOST_CHECK (!chainman.IsSnapshotActive ());
163
- WITH_LOCK (::cs_main, BOOST_CHECK (!chainman.IsSnapshotValidated ()));
179
+
180
+ {
181
+ LOCK (::cs_main);
182
+ BOOST_CHECK (!chainman.IsSnapshotValidated ());
183
+ BOOST_CHECK (!node::FindSnapshotChainstateDir ());
184
+ }
164
185
165
186
size_t initial_size;
166
187
size_t initial_total_coins{100 };
@@ -208,6 +229,9 @@ struct SnapshotTestSetup : TestChain100Setup {
208
229
auto_infile >> outpoint;
209
230
auto_infile >> coin;
210
231
}));
232
+
233
+ BOOST_CHECK (!node::FindSnapshotChainstateDir ());
234
+
211
235
BOOST_REQUIRE (!CreateAndActivateUTXOSnapshot (
212
236
this , [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
213
237
// Coins count is larger than coins in file
@@ -230,6 +254,7 @@ struct SnapshotTestSetup : TestChain100Setup {
230
254
}));
231
255
232
256
BOOST_REQUIRE (CreateAndActivateUTXOSnapshot (this ));
257
+ BOOST_CHECK (fs::exists (*node::FindSnapshotChainstateDir ()));
233
258
234
259
// Ensure our active chain is the snapshot chainstate.
235
260
BOOST_CHECK (!chainman.ActiveChainstate ().m_from_snapshot_blockhash ->IsNull ());
@@ -242,9 +267,11 @@ struct SnapshotTestSetup : TestChain100Setup {
242
267
{
243
268
LOCK (::cs_main);
244
269
270
+ fs::path found = *node::FindSnapshotChainstateDir ();
271
+
245
272
// Note: WriteSnapshotBaseBlockhash() is implicitly tested above.
246
273
BOOST_CHECK_EQUAL (
247
- *node::ReadSnapshotBaseBlockhash (m_args. GetDataDirNet () / " chainstate_snapshot " ),
274
+ *node::ReadSnapshotBaseBlockhash (found ),
248
275
*chainman.SnapshotBlockhash ());
249
276
250
277
// Ensure that the genesis block was not marked assumed-valid.
@@ -328,6 +355,34 @@ struct SnapshotTestSetup : TestChain100Setup {
328
355
loaded_snapshot_blockhash);
329
356
return std::make_tuple (&validation_chainstate, &snapshot_chainstate);
330
357
}
358
+
359
+ // Simulate a restart of the node by flushing all state to disk, clearing the
360
+ // existing ChainstateManager, and unloading the block index.
361
+ //
362
+ // @returns a reference to the "restarted" ChainstateManager
363
+ ChainstateManager& SimulateNodeRestart ()
364
+ {
365
+ ChainstateManager& chainman = *Assert (m_node.chainman );
366
+
367
+ BOOST_TEST_MESSAGE (" Simulating node restart" );
368
+ {
369
+ LOCK (::cs_main);
370
+ for (Chainstate* cs : chainman.GetAll ()) {
371
+ cs->ForceFlushStateToDisk ();
372
+ }
373
+ chainman.ResetChainstates ();
374
+ BOOST_CHECK_EQUAL (chainman.GetAll ().size (), 0 );
375
+ const ChainstateManager::Options chainman_opts{
376
+ .chainparams = ::Params (),
377
+ .adjusted_time_callback = GetAdjustedTime,
378
+ };
379
+ // For robustness, ensure the old manager is destroyed before creating a
380
+ // new one.
381
+ m_node.chainman .reset ();
382
+ m_node.chainman .reset (new ChainstateManager (chainman_opts));
383
+ }
384
+ return *Assert (m_node.chainman );
385
+ }
331
386
};
332
387
333
388
// ! Test basic snapshot activation.
@@ -414,4 +469,59 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
414
469
BOOST_CHECK_EQUAL (cs2.setBlockIndexCandidates .size (), num_indexes);
415
470
}
416
471
472
+ // ! Ensure that snapshot chainstates initialize properly when found on disk.
473
+ BOOST_FIXTURE_TEST_CASE (chainstatemanager_snapshot_init, SnapshotTestSetup)
474
+ {
475
+ this ->SetupSnapshot ();
476
+
477
+ ChainstateManager& chainman = *Assert (m_node.chainman );
478
+
479
+ fs::path snapshot_chainstate_dir = *node::FindSnapshotChainstateDir ();
480
+ BOOST_CHECK (fs::exists (snapshot_chainstate_dir));
481
+ BOOST_CHECK_EQUAL (snapshot_chainstate_dir, gArgs .GetDataDirNet () / " chainstate_snapshot" );
482
+
483
+ BOOST_CHECK (chainman.IsSnapshotActive ());
484
+ const uint256 snapshot_tip_hash = WITH_LOCK (chainman.GetMutex (),
485
+ return chainman.ActiveTip ()->GetBlockHash ());
486
+
487
+ auto all_chainstates = chainman.GetAll ();
488
+ BOOST_CHECK_EQUAL (all_chainstates.size (), 2 );
489
+
490
+ // Test that simulating a shutdown (resetting ChainstateManager) and then performing
491
+ // chainstate reinitializing successfully cleans up the background-validation
492
+ // chainstate data, and we end up with a single chainstate that is at tip.
493
+ ChainstateManager& chainman_restarted = this ->SimulateNodeRestart ();
494
+
495
+ BOOST_TEST_MESSAGE (" Performing Load/Verify/Activate of chainstate" );
496
+
497
+ // This call reinitializes the chainstates.
498
+ this ->LoadVerifyActivateChainstate ();
499
+
500
+ {
501
+ LOCK (chainman_restarted.GetMutex ());
502
+ BOOST_CHECK_EQUAL (chainman_restarted.GetAll ().size (), 2 );
503
+ BOOST_CHECK (chainman_restarted.IsSnapshotActive ());
504
+ BOOST_CHECK (!chainman_restarted.IsSnapshotValidated ());
505
+
506
+ BOOST_CHECK_EQUAL (chainman_restarted.ActiveTip ()->GetBlockHash (), snapshot_tip_hash);
507
+ BOOST_CHECK_EQUAL (chainman_restarted.ActiveHeight (), 210 );
508
+ }
509
+
510
+ BOOST_TEST_MESSAGE (
511
+ " Ensure we can mine blocks on top of the initialized snapshot chainstate" );
512
+ mineBlocks (10 );
513
+ {
514
+ LOCK (chainman_restarted.GetMutex ());
515
+ BOOST_CHECK_EQUAL (chainman_restarted.ActiveHeight (), 220 );
516
+
517
+ // Background chainstate should be unaware of new blocks on the snapshot
518
+ // chainstate.
519
+ for (Chainstate* cs : chainman_restarted.GetAll ()) {
520
+ if (cs != &chainman_restarted.ActiveChainstate ()) {
521
+ BOOST_CHECK_EQUAL (cs->m_chain .Height (), 110 );
522
+ }
523
+ }
524
+ }
525
+ }
526
+
417
527
BOOST_AUTO_TEST_SUITE_END ()
0 commit comments