Skip to content

Commit e4d7995

Browse files
committed
test: add testcases for snapshot initialization
1 parent cced4e7 commit e4d7995

File tree

1 file changed

+112
-2
lines changed

1 file changed

+112
-2
lines changed

src/test/validation_chainstatemanager_tests.cpp

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <sync.h>
1111
#include <test/util/chainstate.h>
1212
#include <test/util/setup_common.h>
13+
#include <timedata.h>
1314
#include <uint256.h>
1415
#include <validation.h>
1516
#include <validationinterface.h>
@@ -155,12 +156,32 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
155156
}
156157

157158
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+
158174
std::tuple<Chainstate*, Chainstate*> SetupSnapshot()
159175
{
160176
ChainstateManager& chainman = *Assert(m_node.chainman);
161177

162178
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+
}
164185

165186
size_t initial_size;
166187
size_t initial_total_coins{100};
@@ -208,6 +229,9 @@ struct SnapshotTestSetup : TestChain100Setup {
208229
auto_infile >> outpoint;
209230
auto_infile >> coin;
210231
}));
232+
233+
BOOST_CHECK(!node::FindSnapshotChainstateDir());
234+
211235
BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
212236
this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
213237
// Coins count is larger than coins in file
@@ -230,6 +254,7 @@ struct SnapshotTestSetup : TestChain100Setup {
230254
}));
231255

232256
BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(this));
257+
BOOST_CHECK(fs::exists(*node::FindSnapshotChainstateDir()));
233258

234259
// Ensure our active chain is the snapshot chainstate.
235260
BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
@@ -242,9 +267,11 @@ struct SnapshotTestSetup : TestChain100Setup {
242267
{
243268
LOCK(::cs_main);
244269

270+
fs::path found = *node::FindSnapshotChainstateDir();
271+
245272
// Note: WriteSnapshotBaseBlockhash() is implicitly tested above.
246273
BOOST_CHECK_EQUAL(
247-
*node::ReadSnapshotBaseBlockhash(m_args.GetDataDirNet() / "chainstate_snapshot"),
274+
*node::ReadSnapshotBaseBlockhash(found),
248275
*chainman.SnapshotBlockhash());
249276

250277
// Ensure that the genesis block was not marked assumed-valid.
@@ -328,6 +355,34 @@ struct SnapshotTestSetup : TestChain100Setup {
328355
loaded_snapshot_blockhash);
329356
return std::make_tuple(&validation_chainstate, &snapshot_chainstate);
330357
}
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+
}
331386
};
332387

333388
//! Test basic snapshot activation.
@@ -414,4 +469,59 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
414469
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.size(), num_indexes);
415470
}
416471

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+
417527
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)