Skip to content

Commit 62ac519

Browse files
jamesobryanofsky
andcommitted
validation: do not activate snapshot if behind active chain
Most easily reviewed with git show --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space Co-authored-by: Ryan Ofsky <[email protected]>
1 parent 9511fb3 commit 62ac519

File tree

2 files changed

+65
-34
lines changed

2 files changed

+65
-34
lines changed

src/test/util/chainstate.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,23 @@ CreateAndActivateUTXOSnapshot(
109109
0 == WITH_LOCK(node.chainman->GetMutex(), return node.chainman->ActiveHeight()));
110110
}
111111

112-
return node.chainman->ActivateSnapshot(auto_infile, metadata, in_memory_chainstate);
112+
auto& new_active = node.chainman->ActiveChainstate();
113+
auto* tip = new_active.m_chain.Tip();
114+
115+
// Disconnect a block so that the snapshot chainstate will be ahead, otherwise
116+
// it will refuse to activate.
117+
//
118+
// TODO this is a unittest-specific hack, and we should probably rethink how to
119+
// better generate/activate snapshots in unittests.
120+
if (tip->pprev) {
121+
new_active.m_chain.SetTip(*(tip->pprev));
122+
}
123+
124+
bool res = node.chainman->ActivateSnapshot(auto_infile, metadata, in_memory_chainstate);
125+
126+
// Restore the old tip.
127+
new_active.m_chain.SetTip(*tip);
128+
return res;
113129
}
114130

115131

src/validation.cpp

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5230,19 +5230,8 @@ bool ChainstateManager::ActivateSnapshot(
52305230
static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
52315231
}
52325232

5233-
bool snapshot_ok = this->PopulateAndValidateSnapshot(
5234-
*snapshot_chainstate, coins_file, metadata);
5235-
5236-
// If not in-memory, persist the base blockhash for use during subsequent
5237-
// initialization.
5238-
if (!in_memory) {
5239-
LOCK(::cs_main);
5240-
if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
5241-
snapshot_ok = false;
5242-
}
5243-
}
5244-
if (!snapshot_ok) {
5245-
LOCK(::cs_main);
5233+
auto cleanup_bad_snapshot = [&](const char* reason) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
5234+
LogPrintf("[snapshot] activation failed - %s\n", reason);
52465235
this->MaybeRebalanceCaches();
52475236

52485237
// PopulateAndValidateSnapshot can return (in error) before the leveldb datadir
@@ -5259,30 +5248,48 @@ bool ChainstateManager::ActivateSnapshot(
52595248
}
52605249
}
52615250
return false;
5262-
}
5251+
};
52635252

5264-
{
5253+
if (!this->PopulateAndValidateSnapshot(*snapshot_chainstate, coins_file, metadata)) {
52655254
LOCK(::cs_main);
5266-
assert(!m_snapshot_chainstate);
5267-
m_snapshot_chainstate.swap(snapshot_chainstate);
5268-
const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip();
5269-
assert(chaintip_loaded);
5270-
5271-
// Transfer possession of the mempool to the snapshot chianstate.
5272-
// Mempool is empty at this point because we're still in IBD.
5273-
Assert(m_active_chainstate->m_mempool->size() == 0);
5274-
Assert(!m_snapshot_chainstate->m_mempool);
5275-
m_snapshot_chainstate->m_mempool = m_active_chainstate->m_mempool;
5276-
m_active_chainstate->m_mempool = nullptr;
5277-
m_active_chainstate = m_snapshot_chainstate.get();
5278-
m_blockman.m_snapshot_height = this->GetSnapshotBaseHeight();
5279-
5280-
LogPrintf("[snapshot] successfully activated snapshot %s\n", base_blockhash.ToString());
5281-
LogPrintf("[snapshot] (%.2f MB)\n",
5282-
m_snapshot_chainstate->CoinsTip().DynamicMemoryUsage() / (1000 * 1000));
5255+
return cleanup_bad_snapshot("population failed");
5256+
}
52835257

5284-
this->MaybeRebalanceCaches();
5258+
LOCK(::cs_main); // cs_main required for rest of snapshot activation.
5259+
5260+
// Do a final check to ensure that the snapshot chainstate is actually a more
5261+
// work chain than the active chainstate; a user could have loaded a snapshot
5262+
// very late in the IBD process, and we wouldn't want to load a useless chainstate.
5263+
if (!CBlockIndexWorkComparator()(ActiveTip(), snapshot_chainstate->m_chain.Tip())) {
5264+
return cleanup_bad_snapshot("work does not exceed active chainstate");
5265+
}
5266+
// If not in-memory, persist the base blockhash for use during subsequent
5267+
// initialization.
5268+
if (!in_memory) {
5269+
if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
5270+
return cleanup_bad_snapshot("could not write base blockhash");
5271+
}
52855272
}
5273+
5274+
assert(!m_snapshot_chainstate);
5275+
m_snapshot_chainstate.swap(snapshot_chainstate);
5276+
const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip();
5277+
assert(chaintip_loaded);
5278+
5279+
// Transfer possession of the mempool to the snapshot chainstate.
5280+
// Mempool is empty at this point because we're still in IBD.
5281+
Assert(m_active_chainstate->m_mempool->size() == 0);
5282+
Assert(!m_snapshot_chainstate->m_mempool);
5283+
m_snapshot_chainstate->m_mempool = m_active_chainstate->m_mempool;
5284+
m_active_chainstate->m_mempool = nullptr;
5285+
m_active_chainstate = m_snapshot_chainstate.get();
5286+
m_blockman.m_snapshot_height = this->GetSnapshotBaseHeight();
5287+
5288+
LogPrintf("[snapshot] successfully activated snapshot %s\n", base_blockhash.ToString());
5289+
LogPrintf("[snapshot] (%.2f MB)\n",
5290+
m_snapshot_chainstate->CoinsTip().DynamicMemoryUsage() / (1000 * 1000));
5291+
5292+
this->MaybeRebalanceCaches();
52865293
return true;
52875294
}
52885295

@@ -5342,6 +5349,14 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
53425349

53435350
const AssumeutxoData& au_data = *maybe_au_data;
53445351

5352+
// This work comparison is a duplicate check with the one performed later in
5353+
// ActivateSnapshot(), but is done so that we avoid doing the long work of staging
5354+
// a snapshot that isn't actually usable.
5355+
if (WITH_LOCK(::cs_main, return !CBlockIndexWorkComparator()(ActiveTip(), snapshot_start_block))) {
5356+
LogPrintf("[snapshot] activation failed - height does not exceed active chainstate\n");
5357+
return false;
5358+
}
5359+
53455360
COutPoint outpoint;
53465361
Coin coin;
53475362
const uint64_t coins_count = metadata.m_coins_count;

0 commit comments

Comments
 (0)