@@ -5230,19 +5230,8 @@ bool ChainstateManager::ActivateSnapshot(
5230
5230
static_cast <size_t >(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
5231
5231
}
5232
5232
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);
5246
5235
this ->MaybeRebalanceCaches ();
5247
5236
5248
5237
// PopulateAndValidateSnapshot can return (in error) before the leveldb datadir
@@ -5259,30 +5248,48 @@ bool ChainstateManager::ActivateSnapshot(
5259
5248
}
5260
5249
}
5261
5250
return false ;
5262
- }
5251
+ };
5263
5252
5264
- {
5253
+ if (! this -> PopulateAndValidateSnapshot (*snapshot_chainstate, coins_file, metadata)) {
5265
5254
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
+ }
5283
5257
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
+ }
5285
5272
}
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 ();
5286
5293
return true ;
5287
5294
}
5288
5295
@@ -5342,6 +5349,14 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
5342
5349
5343
5350
const AssumeutxoData& au_data = *maybe_au_data;
5344
5351
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
+
5345
5360
COutPoint outpoint;
5346
5361
Coin coin;
5347
5362
const uint64_t coins_count = metadata.m_coins_count ;
0 commit comments