aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
authorJames O'Beirne <james.obeirne@pm.me>2023-09-17 13:56:12 -0400
committerJames O'Beirne <james.obeirne@pm.me>2023-09-30 06:41:21 -0400
commit62ac519e718eb7a31dca1102a96ba219fbc7f95d (patch)
tree8fd7e2e8ee1472038c7c2b4d603fd1b1f3e818ab /src/validation.cpp
parent9511fb3616b7bbe1d0d2f54a45ea0a650ba0367b (diff)
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 <ryan@ofsky.org>
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp81
1 files changed, 48 insertions, 33 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index d72b017cc4..82aafd97f8 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -5230,19 +5230,8 @@ bool ChainstateManager::ActivateSnapshot(
static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
}
- bool snapshot_ok = this->PopulateAndValidateSnapshot(
- *snapshot_chainstate, coins_file, metadata);
-
- // If not in-memory, persist the base blockhash for use during subsequent
- // initialization.
- if (!in_memory) {
- LOCK(::cs_main);
- if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
- snapshot_ok = false;
- }
- }
- if (!snapshot_ok) {
- LOCK(::cs_main);
+ auto cleanup_bad_snapshot = [&](const char* reason) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ LogPrintf("[snapshot] activation failed - %s\n", reason);
this->MaybeRebalanceCaches();
// PopulateAndValidateSnapshot can return (in error) before the leveldb datadir
@@ -5259,30 +5248,48 @@ bool ChainstateManager::ActivateSnapshot(
}
}
return false;
- }
+ };
- {
+ if (!this->PopulateAndValidateSnapshot(*snapshot_chainstate, coins_file, metadata)) {
LOCK(::cs_main);
- assert(!m_snapshot_chainstate);
- m_snapshot_chainstate.swap(snapshot_chainstate);
- const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip();
- assert(chaintip_loaded);
-
- // Transfer possession of the mempool to the snapshot chianstate.
- // Mempool is empty at this point because we're still in IBD.
- Assert(m_active_chainstate->m_mempool->size() == 0);
- Assert(!m_snapshot_chainstate->m_mempool);
- m_snapshot_chainstate->m_mempool = m_active_chainstate->m_mempool;
- m_active_chainstate->m_mempool = nullptr;
- m_active_chainstate = m_snapshot_chainstate.get();
- m_blockman.m_snapshot_height = this->GetSnapshotBaseHeight();
-
- LogPrintf("[snapshot] successfully activated snapshot %s\n", base_blockhash.ToString());
- LogPrintf("[snapshot] (%.2f MB)\n",
- m_snapshot_chainstate->CoinsTip().DynamicMemoryUsage() / (1000 * 1000));
+ return cleanup_bad_snapshot("population failed");
+ }
- this->MaybeRebalanceCaches();
+ LOCK(::cs_main); // cs_main required for rest of snapshot activation.
+
+ // Do a final check to ensure that the snapshot chainstate is actually a more
+ // work chain than the active chainstate; a user could have loaded a snapshot
+ // very late in the IBD process, and we wouldn't want to load a useless chainstate.
+ if (!CBlockIndexWorkComparator()(ActiveTip(), snapshot_chainstate->m_chain.Tip())) {
+ return cleanup_bad_snapshot("work does not exceed active chainstate");
+ }
+ // If not in-memory, persist the base blockhash for use during subsequent
+ // initialization.
+ if (!in_memory) {
+ if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
+ return cleanup_bad_snapshot("could not write base blockhash");
+ }
}
+
+ assert(!m_snapshot_chainstate);
+ m_snapshot_chainstate.swap(snapshot_chainstate);
+ const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip();
+ assert(chaintip_loaded);
+
+ // Transfer possession of the mempool to the snapshot chainstate.
+ // Mempool is empty at this point because we're still in IBD.
+ Assert(m_active_chainstate->m_mempool->size() == 0);
+ Assert(!m_snapshot_chainstate->m_mempool);
+ m_snapshot_chainstate->m_mempool = m_active_chainstate->m_mempool;
+ m_active_chainstate->m_mempool = nullptr;
+ m_active_chainstate = m_snapshot_chainstate.get();
+ m_blockman.m_snapshot_height = this->GetSnapshotBaseHeight();
+
+ LogPrintf("[snapshot] successfully activated snapshot %s\n", base_blockhash.ToString());
+ LogPrintf("[snapshot] (%.2f MB)\n",
+ m_snapshot_chainstate->CoinsTip().DynamicMemoryUsage() / (1000 * 1000));
+
+ this->MaybeRebalanceCaches();
return true;
}
@@ -5342,6 +5349,14 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
const AssumeutxoData& au_data = *maybe_au_data;
+ // This work comparison is a duplicate check with the one performed later in
+ // ActivateSnapshot(), but is done so that we avoid doing the long work of staging
+ // a snapshot that isn't actually usable.
+ if (WITH_LOCK(::cs_main, return !CBlockIndexWorkComparator()(ActiveTip(), snapshot_start_block))) {
+ LogPrintf("[snapshot] activation failed - height does not exceed active chainstate\n");
+ return false;
+ }
+
COutPoint outpoint;
Coin coin;
const uint64_t coins_count = metadata.m_coins_count;