atlas_runtime/
snapshot_bank_utils.rs

1#[cfg(feature = "dev-context-only-utils")]
2use {
3    crate::{
4        bank::BankFieldsToDeserialize,
5        serde_snapshot::fields_from_streams,
6        snapshot_utils::{
7            deserialize_snapshot_data_files, verify_unpacked_snapshots_dir_and_version,
8            SnapshotRootPaths, UnpackedSnapshotsDirAndVersion,
9        },
10    },
11    solana_accounts_db::accounts_file::StorageAccess,
12    tempfile::TempDir,
13};
14use {
15    crate::{
16        bank::{Bank, BankSlotDelta},
17        epoch_stakes::VersionedEpochStakes,
18        runtime_config::RuntimeConfig,
19        serde_snapshot::{
20            self, reconstruct_bank_from_fields, SnapshotAccountsDbFields, SnapshotBankFields,
21        },
22        snapshot_archive_info::{
23            FullSnapshotArchiveInfo, IncrementalSnapshotArchiveInfo, SnapshotArchiveInfoGetter,
24        },
25        snapshot_config::SnapshotConfig,
26        snapshot_hash::SnapshotHash,
27        snapshot_package::{SnapshotKind, SnapshotPackage},
28        snapshot_utils::{
29            self, get_highest_bank_snapshot_post, get_highest_full_snapshot_archive_info,
30            get_highest_incremental_snapshot_archive_info, rebuild_storages_from_snapshot_dir,
31            verify_and_unarchive_snapshots, ArchiveFormat, BankSnapshotInfo, SnapshotError,
32            SnapshotVersion, StorageAndNextAccountsFileId, UnarchivedSnapshots,
33            VerifyEpochStakesError, VerifySlotDeltasError, VerifySlotHistoryError,
34        },
35        status_cache,
36    },
37    log::*,
38    solana_accounts_db::{
39        accounts_db::{AccountsDbConfig, AtomicAccountsFileId},
40        accounts_update_notifier_interface::AccountsUpdateNotifier,
41        utils::remove_dir_contents,
42    },
43    solana_builtins::prototype::BuiltinPrototype,
44    solana_clock::{Epoch, Slot},
45    solana_genesis_config::GenesisConfig,
46    solana_measure::{measure::Measure, measure_time},
47    solana_pubkey::Pubkey,
48    solana_slot_history::{Check, SlotHistory},
49    std::{
50        collections::{HashMap, HashSet},
51        ops::RangeInclusive,
52        path::{Path, PathBuf},
53        sync::{atomic::AtomicBool, Arc},
54    },
55};
56
57#[derive(Debug)]
58pub struct BankFromArchivesTimings {
59    pub untar_full_snapshot_archive_us: u64,
60    pub untar_incremental_snapshot_archive_us: Option<u64>,
61    pub rebuild_bank_us: u64,
62    pub verify_bank_us: u64,
63}
64
65#[derive(Debug)]
66pub struct BankFromDirTimings {
67    pub rebuild_storages_us: u64,
68    pub rebuild_bank_us: u64,
69}
70
71/// Parses out bank specific information from a snapshot archive including the leader schedule.
72/// epoch schedule, etc.
73#[cfg(feature = "dev-context-only-utils")]
74pub fn bank_fields_from_snapshot_archives(
75    full_snapshot_archives_dir: impl AsRef<Path>,
76    incremental_snapshot_archives_dir: impl AsRef<Path>,
77    storage_access: StorageAccess,
78) -> snapshot_utils::Result<BankFieldsToDeserialize> {
79    let full_snapshot_archive_info =
80        get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir).ok_or_else(|| {
81            SnapshotError::NoSnapshotArchives(full_snapshot_archives_dir.as_ref().to_path_buf())
82        })?;
83
84    let incremental_snapshot_archive_info = get_highest_incremental_snapshot_archive_info(
85        &incremental_snapshot_archives_dir,
86        full_snapshot_archive_info.slot(),
87    );
88
89    let temp_unpack_dir = TempDir::new()?;
90    let temp_accounts_dir = TempDir::new()?;
91
92    let account_paths = vec![temp_accounts_dir.path().to_path_buf()];
93
94    let (
95        UnarchivedSnapshots {
96            full_unpacked_snapshots_dir_and_version,
97            incremental_unpacked_snapshots_dir_and_version,
98            ..
99        },
100        _guard,
101    ) = verify_and_unarchive_snapshots(
102        &temp_unpack_dir,
103        &full_snapshot_archive_info,
104        incremental_snapshot_archive_info.as_ref(),
105        &account_paths,
106        storage_access,
107    )?;
108
109    bank_fields_from_snapshots(
110        &full_unpacked_snapshots_dir_and_version,
111        incremental_unpacked_snapshots_dir_and_version.as_ref(),
112    )
113}
114
115#[cfg(feature = "dev-context-only-utils")]
116fn bank_fields_from_snapshots(
117    full_snapshot_unpacked_snapshots_dir_and_version: &UnpackedSnapshotsDirAndVersion,
118    incremental_snapshot_unpacked_snapshots_dir_and_version: Option<
119        &UnpackedSnapshotsDirAndVersion,
120    >,
121) -> snapshot_utils::Result<BankFieldsToDeserialize> {
122    let (snapshot_version, snapshot_root_paths) = snapshot_version_and_root_paths(
123        full_snapshot_unpacked_snapshots_dir_and_version,
124        incremental_snapshot_unpacked_snapshots_dir_and_version,
125    )?;
126
127    info!(
128        "Loading bank from full snapshot {} and incremental snapshot {:?}",
129        snapshot_root_paths.full_snapshot_root_file_path.display(),
130        snapshot_root_paths.incremental_snapshot_root_file_path,
131    );
132
133    deserialize_snapshot_data_files(&snapshot_root_paths, |snapshot_streams| {
134        Ok(match snapshot_version {
135            SnapshotVersion::V1_2_0 => fields_from_streams(snapshot_streams)
136                .map(|(bank_fields, _accountsdb_fields)| bank_fields.collapse_into()),
137        }?)
138    })
139}
140
141/// Rebuild bank from snapshot archives.  Handles either just a full snapshot, or both a full
142/// snapshot and an incremental snapshot.
143#[allow(clippy::too_many_arguments)]
144pub fn bank_from_snapshot_archives(
145    account_paths: &[PathBuf],
146    bank_snapshots_dir: impl AsRef<Path>,
147    full_snapshot_archive_info: &FullSnapshotArchiveInfo,
148    incremental_snapshot_archive_info: Option<&IncrementalSnapshotArchiveInfo>,
149    genesis_config: &GenesisConfig,
150    runtime_config: &RuntimeConfig,
151    debug_keys: Option<Arc<HashSet<Pubkey>>>,
152    additional_builtins: Option<&[BuiltinPrototype]>,
153    limit_load_slot_count_from_snapshot: Option<usize>,
154    accounts_db_skip_shrink: bool,
155    accounts_db_force_initial_clean: bool,
156    verify_index: bool,
157    accounts_db_config: Option<AccountsDbConfig>,
158    accounts_update_notifier: Option<AccountsUpdateNotifier>,
159    exit: Arc<AtomicBool>,
160) -> snapshot_utils::Result<(Bank, BankFromArchivesTimings)> {
161    info!(
162        "Loading bank from full snapshot archive: {}, and incremental snapshot archive: {:?}",
163        full_snapshot_archive_info.path().display(),
164        incremental_snapshot_archive_info
165            .as_ref()
166            .map(
167                |incremental_snapshot_archive_info| incremental_snapshot_archive_info
168                    .path()
169                    .display()
170            )
171    );
172
173    let (
174        UnarchivedSnapshots {
175            full_storage: mut storage,
176            incremental_storage,
177            bank_fields,
178            accounts_db_fields,
179            full_unpacked_snapshots_dir_and_version,
180            incremental_unpacked_snapshots_dir_and_version,
181            full_measure_untar,
182            incremental_measure_untar,
183            next_append_vec_id,
184            ..
185        },
186        _guard,
187    ) = verify_and_unarchive_snapshots(
188        bank_snapshots_dir,
189        full_snapshot_archive_info,
190        incremental_snapshot_archive_info,
191        account_paths,
192        accounts_db_config
193            .as_ref()
194            .map(|config| config.storage_access)
195            .unwrap_or_default(),
196    )?;
197
198    if let Some(incremental_storage) = incremental_storage {
199        storage.extend(incremental_storage);
200    }
201
202    let storage_and_next_append_vec_id = StorageAndNextAccountsFileId {
203        storage,
204        next_append_vec_id,
205    };
206
207    let mut measure_rebuild = Measure::start("rebuild bank from snapshots");
208    let (bank, info) = reconstruct_bank_from_fields(
209        bank_fields,
210        accounts_db_fields,
211        genesis_config,
212        runtime_config,
213        account_paths,
214        storage_and_next_append_vec_id,
215        debug_keys,
216        additional_builtins,
217        limit_load_slot_count_from_snapshot,
218        verify_index,
219        accounts_db_config,
220        accounts_update_notifier,
221        exit,
222    )?;
223    measure_rebuild.stop();
224    info!("{measure_rebuild}");
225
226    verify_epoch_stakes(&bank)?;
227
228    // The status cache is rebuilt from the latest snapshot.  So, if there's an incremental
229    // snapshot, use that.  Otherwise use the full snapshot.
230    let status_cache_path = incremental_unpacked_snapshots_dir_and_version
231        .as_ref()
232        .unwrap_or(&full_unpacked_snapshots_dir_and_version)
233        .unpacked_snapshots_dir
234        .join(snapshot_utils::SNAPSHOT_STATUS_CACHE_FILENAME);
235    info!(
236        "Rebuilding status cache from {}",
237        status_cache_path.display()
238    );
239    let slot_deltas = serde_snapshot::deserialize_status_cache(&status_cache_path)?;
240
241    verify_slot_deltas(slot_deltas.as_slice(), &bank)?;
242
243    bank.status_cache.write().unwrap().append(&slot_deltas);
244
245    let snapshot_archive_info = incremental_snapshot_archive_info.map_or_else(
246        || full_snapshot_archive_info.snapshot_archive_info(),
247        |incremental_snapshot_archive_info| {
248            incremental_snapshot_archive_info.snapshot_archive_info()
249        },
250    );
251    verify_bank_against_expected_slot_hash(
252        &bank,
253        snapshot_archive_info.slot,
254        snapshot_archive_info.hash,
255    )?;
256
257    let mut measure_verify = Measure::start("verify");
258    if !bank.verify_snapshot_bank(
259        accounts_db_skip_shrink || !full_snapshot_archive_info.is_remote(),
260        accounts_db_force_initial_clean,
261        full_snapshot_archive_info.slot(),
262        info.duplicates_lt_hash,
263    ) && limit_load_slot_count_from_snapshot.is_none()
264    {
265        panic!("Snapshot bank for slot {} failed to verify", bank.slot());
266    }
267    measure_verify.stop();
268
269    let timings = BankFromArchivesTimings {
270        untar_full_snapshot_archive_us: full_measure_untar.as_us(),
271        untar_incremental_snapshot_archive_us: incremental_measure_untar
272            .as_ref()
273            .map(Measure::as_us),
274        rebuild_bank_us: measure_rebuild.as_us(),
275        verify_bank_us: measure_verify.as_us(),
276    };
277    datapoint_info!(
278        "bank_from_snapshot_archives",
279        (
280            "untar_full_snapshot_archive_us",
281            timings.untar_full_snapshot_archive_us,
282            i64
283        ),
284        (
285            "untar_incremental_snapshot_archive_us",
286            timings.untar_incremental_snapshot_archive_us,
287            Option<i64>
288        ),
289        ("rebuild_bank_us", timings.rebuild_bank_us, i64),
290        ("verify_bank_us", timings.verify_bank_us, i64),
291    );
292    Ok((bank, timings))
293}
294
295/// Rebuild bank from snapshot archives
296///
297/// This function searches `full_snapshot_archives_dir` and `incremental_snapshot_archives_dir` for
298/// the highest full snapshot and highest corresponding incremental snapshot, then rebuilds the bank.
299#[allow(clippy::too_many_arguments)]
300pub fn bank_from_latest_snapshot_archives(
301    bank_snapshots_dir: impl AsRef<Path>,
302    full_snapshot_archives_dir: impl AsRef<Path>,
303    incremental_snapshot_archives_dir: impl AsRef<Path>,
304    account_paths: &[PathBuf],
305    genesis_config: &GenesisConfig,
306    runtime_config: &RuntimeConfig,
307    debug_keys: Option<Arc<HashSet<Pubkey>>>,
308    additional_builtins: Option<&[BuiltinPrototype]>,
309    limit_load_slot_count_from_snapshot: Option<usize>,
310    accounts_db_skip_shrink: bool,
311    accounts_db_force_initial_clean: bool,
312    verify_index: bool,
313    accounts_db_config: Option<AccountsDbConfig>,
314    accounts_update_notifier: Option<AccountsUpdateNotifier>,
315    exit: Arc<AtomicBool>,
316) -> snapshot_utils::Result<(
317    Bank,
318    FullSnapshotArchiveInfo,
319    Option<IncrementalSnapshotArchiveInfo>,
320)> {
321    let full_snapshot_archive_info =
322        get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir).ok_or_else(|| {
323            SnapshotError::NoSnapshotArchives(full_snapshot_archives_dir.as_ref().to_path_buf())
324        })?;
325
326    let incremental_snapshot_archive_info = get_highest_incremental_snapshot_archive_info(
327        &incremental_snapshot_archives_dir,
328        full_snapshot_archive_info.slot(),
329    );
330
331    let (bank, _) = bank_from_snapshot_archives(
332        account_paths,
333        bank_snapshots_dir.as_ref(),
334        &full_snapshot_archive_info,
335        incremental_snapshot_archive_info.as_ref(),
336        genesis_config,
337        runtime_config,
338        debug_keys,
339        additional_builtins,
340        limit_load_slot_count_from_snapshot,
341        accounts_db_skip_shrink,
342        accounts_db_force_initial_clean,
343        verify_index,
344        accounts_db_config,
345        accounts_update_notifier,
346        exit,
347    )?;
348
349    Ok((
350        bank,
351        full_snapshot_archive_info,
352        incremental_snapshot_archive_info,
353    ))
354}
355
356/// Build bank from a snapshot (a snapshot directory, not a snapshot archive)
357#[allow(clippy::too_many_arguments)]
358pub fn bank_from_snapshot_dir(
359    account_paths: &[PathBuf],
360    bank_snapshot: &BankSnapshotInfo,
361    genesis_config: &GenesisConfig,
362    runtime_config: &RuntimeConfig,
363    debug_keys: Option<Arc<HashSet<Pubkey>>>,
364    additional_builtins: Option<&[BuiltinPrototype]>,
365    limit_load_slot_count_from_snapshot: Option<usize>,
366    verify_index: bool,
367    accounts_db_config: Option<AccountsDbConfig>,
368    accounts_update_notifier: Option<AccountsUpdateNotifier>,
369    exit: Arc<AtomicBool>,
370) -> snapshot_utils::Result<(Bank, BankFromDirTimings)> {
371    info!(
372        "Loading bank from snapshot dir: {}",
373        bank_snapshot.snapshot_dir.display()
374    );
375
376    // Clear the contents of the account paths run directories.  When constructing the bank, the appendvec
377    // files will be extracted from the snapshot hardlink directories into these run/ directories.
378    for path in account_paths {
379        remove_dir_contents(path);
380    }
381
382    let next_append_vec_id = Arc::new(AtomicAccountsFileId::new(0));
383    let storage_access = accounts_db_config
384        .as_ref()
385        .map(|config| config.storage_access)
386        .unwrap_or_default();
387
388    let ((storage, bank_fields, accounts_db_fields), measure_rebuild_storages) = measure_time!(
389        rebuild_storages_from_snapshot_dir(
390            bank_snapshot,
391            account_paths,
392            next_append_vec_id.clone(),
393            storage_access,
394        )?,
395        "rebuild storages from snapshot dir"
396    );
397    info!("{measure_rebuild_storages}");
398
399    let next_append_vec_id =
400        Arc::try_unwrap(next_append_vec_id).expect("this is the only strong reference");
401    let storage_and_next_append_vec_id = StorageAndNextAccountsFileId {
402        storage,
403        next_append_vec_id,
404    };
405    let snapshot_bank_fields = SnapshotBankFields::new(bank_fields, None);
406    let snapshot_accounts_db_fields = SnapshotAccountsDbFields::new(accounts_db_fields, None);
407    let ((bank, _info), measure_rebuild_bank) = measure_time!(
408        reconstruct_bank_from_fields(
409            snapshot_bank_fields,
410            snapshot_accounts_db_fields,
411            genesis_config,
412            runtime_config,
413            account_paths,
414            storage_and_next_append_vec_id,
415            debug_keys,
416            additional_builtins,
417            limit_load_slot_count_from_snapshot,
418            verify_index,
419            accounts_db_config,
420            accounts_update_notifier,
421            exit,
422        )?,
423        "rebuild bank from snapshot"
424    );
425    info!("{measure_rebuild_bank}");
426
427    verify_epoch_stakes(&bank)?;
428
429    let status_cache_path = bank_snapshot
430        .snapshot_dir
431        .join(snapshot_utils::SNAPSHOT_STATUS_CACHE_FILENAME);
432    info!(
433        "Rebuilding status cache from {}",
434        status_cache_path.display()
435    );
436    let slot_deltas = serde_snapshot::deserialize_status_cache(&status_cache_path)?;
437
438    verify_slot_deltas(slot_deltas.as_slice(), &bank)?;
439
440    bank.status_cache.write().unwrap().append(&slot_deltas);
441
442    // We trust our local state, so skip the startup accounts verification.
443    bank.set_initial_accounts_hash_verification_completed();
444
445    let timings = BankFromDirTimings {
446        rebuild_storages_us: measure_rebuild_storages.as_us(),
447        rebuild_bank_us: measure_rebuild_bank.as_us(),
448    };
449    datapoint_info!(
450        "bank_from_snapshot_dir",
451        ("rebuild_storages_us", timings.rebuild_storages_us, i64),
452        ("rebuild_bank_us", timings.rebuild_bank_us, i64),
453    );
454    Ok((bank, timings))
455}
456
457/// follow the prototype of fn bank_from_latest_snapshot_archives, implement the from_dir case
458#[allow(clippy::too_many_arguments)]
459pub fn bank_from_latest_snapshot_dir(
460    bank_snapshots_dir: impl AsRef<Path>,
461    genesis_config: &GenesisConfig,
462    runtime_config: &RuntimeConfig,
463    account_paths: &[PathBuf],
464    debug_keys: Option<Arc<HashSet<Pubkey>>>,
465    additional_builtins: Option<&[BuiltinPrototype]>,
466    limit_load_slot_count_from_snapshot: Option<usize>,
467    verify_index: bool,
468    accounts_db_config: Option<AccountsDbConfig>,
469    accounts_update_notifier: Option<AccountsUpdateNotifier>,
470    exit: Arc<AtomicBool>,
471) -> snapshot_utils::Result<Bank> {
472    let bank_snapshot = get_highest_bank_snapshot_post(&bank_snapshots_dir).ok_or_else(|| {
473        SnapshotError::NoSnapshotSlotDir(bank_snapshots_dir.as_ref().to_path_buf())
474    })?;
475    let (bank, _) = bank_from_snapshot_dir(
476        account_paths,
477        &bank_snapshot,
478        genesis_config,
479        runtime_config,
480        debug_keys,
481        additional_builtins,
482        limit_load_slot_count_from_snapshot,
483        verify_index,
484        accounts_db_config,
485        accounts_update_notifier,
486        exit,
487    )?;
488
489    Ok(bank)
490}
491
492/// Verifies the snapshot's slot and hash matches the bank's
493fn verify_bank_against_expected_slot_hash(
494    bank: &Bank,
495    snapshot_slot: Slot,
496    snapshot_hash: SnapshotHash,
497) -> snapshot_utils::Result<()> {
498    let bank_slot = bank.slot();
499    if bank_slot != snapshot_slot {
500        return Err(SnapshotError::MismatchedSlot(bank_slot, snapshot_slot));
501    }
502
503    let bank_hash = bank.get_snapshot_hash();
504    if bank_hash == snapshot_hash {
505        Ok(())
506    } else {
507        Err(SnapshotError::MismatchedHash(bank_hash, snapshot_hash))
508    }
509}
510
511/// Returns the validated version and root paths for the given snapshots.
512#[cfg(feature = "dev-context-only-utils")]
513fn snapshot_version_and_root_paths(
514    full_snapshot_unpacked_snapshots_dir_and_version: &UnpackedSnapshotsDirAndVersion,
515    incremental_snapshot_unpacked_snapshots_dir_and_version: Option<
516        &UnpackedSnapshotsDirAndVersion,
517    >,
518) -> snapshot_utils::Result<(SnapshotVersion, SnapshotRootPaths)> {
519    let (full_snapshot_version, full_snapshot_root_paths) =
520        verify_unpacked_snapshots_dir_and_version(
521            full_snapshot_unpacked_snapshots_dir_and_version,
522        )?;
523    let (incremental_snapshot_version, incremental_snapshot_root_paths) =
524        if let Some(snapshot_unpacked_snapshots_dir_and_version) =
525            incremental_snapshot_unpacked_snapshots_dir_and_version
526        {
527            Some(verify_unpacked_snapshots_dir_and_version(
528                snapshot_unpacked_snapshots_dir_and_version,
529            )?)
530        } else {
531            None
532        }
533        .unzip();
534
535    let snapshot_version = incremental_snapshot_version.unwrap_or(full_snapshot_version);
536    let snapshot_root_paths = SnapshotRootPaths {
537        full_snapshot_root_file_path: full_snapshot_root_paths.snapshot_path(),
538        incremental_snapshot_root_file_path: incremental_snapshot_root_paths
539            .map(|root_paths| root_paths.snapshot_path()),
540    };
541
542    Ok((snapshot_version, snapshot_root_paths))
543}
544
545/// Verify that the snapshot's slot deltas are not corrupt/invalid
546fn verify_slot_deltas(
547    slot_deltas: &[BankSlotDelta],
548    bank: &Bank,
549) -> std::result::Result<(), VerifySlotDeltasError> {
550    let info = verify_slot_deltas_structural(slot_deltas, bank.slot())?;
551    verify_slot_deltas_with_history(&info.slots, &bank.get_slot_history(), bank.slot())
552}
553
554/// Verify that the snapshot's slot deltas are not corrupt/invalid
555/// These checks are simple/structural
556fn verify_slot_deltas_structural(
557    slot_deltas: &[BankSlotDelta],
558    bank_slot: Slot,
559) -> std::result::Result<VerifySlotDeltasStructuralInfo, VerifySlotDeltasError> {
560    // there should not be more entries than that status cache's max
561    let num_entries = slot_deltas.len();
562    if num_entries > status_cache::MAX_CACHE_ENTRIES {
563        return Err(VerifySlotDeltasError::TooManyEntries(
564            num_entries,
565            status_cache::MAX_CACHE_ENTRIES,
566        ));
567    }
568
569    let mut slots_seen_so_far = HashSet::new();
570    for &(slot, is_root, ..) in slot_deltas {
571        // all entries should be roots
572        if !is_root {
573            return Err(VerifySlotDeltasError::SlotIsNotRoot(slot));
574        }
575
576        // all entries should be for slots less than or equal to the bank's slot
577        if slot > bank_slot {
578            return Err(VerifySlotDeltasError::SlotGreaterThanMaxRoot(
579                slot, bank_slot,
580            ));
581        }
582
583        // there should only be one entry per slot
584        let is_duplicate = !slots_seen_so_far.insert(slot);
585        if is_duplicate {
586            return Err(VerifySlotDeltasError::SlotHasMultipleEntries(slot));
587        }
588    }
589
590    // detect serious logic error for future careless changes. :)
591    assert_eq!(slots_seen_so_far.len(), slot_deltas.len());
592
593    Ok(VerifySlotDeltasStructuralInfo {
594        slots: slots_seen_so_far,
595    })
596}
597
598/// Computed information from `verify_slot_deltas_structural()`, that may be reused/useful later.
599#[derive(Debug, PartialEq, Eq)]
600struct VerifySlotDeltasStructuralInfo {
601    /// All the slots in the slot deltas
602    slots: HashSet<Slot>,
603}
604
605/// Verify that the snapshot's slot deltas are not corrupt/invalid
606/// These checks use the slot history for verification
607fn verify_slot_deltas_with_history(
608    slots_from_slot_deltas: &HashSet<Slot>,
609    slot_history: &SlotHistory,
610    bank_slot: Slot,
611) -> std::result::Result<(), VerifySlotDeltasError> {
612    // ensure the slot history is valid (as much as possible), since we're using it to verify the
613    // slot deltas
614    verify_slot_history(slot_history, bank_slot)?;
615
616    // all slots in the slot deltas should be in the bank's slot history
617    let slot_missing_from_history = slots_from_slot_deltas
618        .iter()
619        .find(|slot| slot_history.check(**slot) != Check::Found);
620    if let Some(slot) = slot_missing_from_history {
621        return Err(VerifySlotDeltasError::SlotNotFoundInHistory(*slot));
622    }
623
624    // all slots in the history should be in the slot deltas (up to MAX_CACHE_ENTRIES)
625    // this ensures nothing was removed from the status cache
626    //
627    // go through the slot history and make sure there's an entry for each slot
628    // note: it's important to go highest-to-lowest since the status cache removes
629    // older entries first
630    // note: we already checked above that `bank_slot == slot_history.newest()`
631    let slot_missing_from_deltas = (slot_history.oldest()..=slot_history.newest())
632        .rev()
633        .filter(|slot| slot_history.check(*slot) == Check::Found)
634        .take(status_cache::MAX_CACHE_ENTRIES)
635        .find(|slot| !slots_from_slot_deltas.contains(slot));
636    if let Some(slot) = slot_missing_from_deltas {
637        return Err(VerifySlotDeltasError::SlotNotFoundInDeltas(slot));
638    }
639
640    Ok(())
641}
642
643/// Verify that the snapshot's SlotHistory is not corrupt/invalid
644fn verify_slot_history(
645    slot_history: &SlotHistory,
646    bank_slot: Slot,
647) -> Result<(), VerifySlotHistoryError> {
648    if slot_history.newest() != bank_slot {
649        return Err(VerifySlotHistoryError::InvalidNewestSlot);
650    }
651
652    if slot_history.bits.len() != solana_slot_history::MAX_ENTRIES {
653        return Err(VerifySlotHistoryError::InvalidNumEntries);
654    }
655
656    Ok(())
657}
658
659/// Verifies the bank's epoch stakes are valid after rebuilding from a snapshot
660fn verify_epoch_stakes(bank: &Bank) -> std::result::Result<(), VerifyEpochStakesError> {
661    // Stakes are required for epochs from the current epoch up-to-and-including the
662    // leader schedule epoch.  In practice this will only be two epochs: the current and the next.
663    // Using a range mirrors how Bank::new_with_paths() seeds the initial epoch stakes.
664    let current_epoch = bank.epoch();
665    let leader_schedule_epoch = bank.get_leader_schedule_epoch(bank.slot());
666    let required_epochs = current_epoch..=leader_schedule_epoch;
667    _verify_epoch_stakes(bank.epoch_stakes_map(), required_epochs)
668}
669
670/// Verifies the bank's epoch stakes are valid after rebuilding from a snapshot
671///
672/// This version of the function exists to facilitate testing.
673/// Normal callers should use `verify_epoch_stakes()`.
674fn _verify_epoch_stakes(
675    epoch_stakes_map: &HashMap<Epoch, VersionedEpochStakes>,
676    required_epochs: RangeInclusive<Epoch>,
677) -> std::result::Result<(), VerifyEpochStakesError> {
678    // Ensure epoch stakes from the snapshot does not contain entries for invalid epochs.
679    // Since epoch stakes are computed for the leader schedule epoch (usually `epoch + 1`),
680    // the snapshot's epoch stakes therefor can have entries for epochs at-or-below the
681    // leader schedule epoch.
682    let max_epoch = *required_epochs.end();
683    if let Some(invalid_epoch) = epoch_stakes_map.keys().find(|epoch| **epoch > max_epoch) {
684        return Err(VerifyEpochStakesError::EpochGreaterThanMax(
685            *invalid_epoch,
686            max_epoch,
687        ));
688    }
689
690    // Ensure epoch stakes contains stakes for all the required epochs
691    if let Some(missing_epoch) = required_epochs
692        .clone()
693        .find(|epoch| !epoch_stakes_map.contains_key(epoch))
694    {
695        return Err(VerifyEpochStakesError::StakesNotFound(
696            missing_epoch,
697            required_epochs,
698        ));
699    }
700
701    Ok(())
702}
703
704/// Convenience function to create a full snapshot archive out of any Bank, regardless of state.
705/// The Bank will be frozen during the process.
706/// This is only called from ledger-tool or tests. Warping is a special case as well.
707///
708/// Requires:
709///     - `bank` is complete
710pub fn bank_to_full_snapshot_archive(
711    bank_snapshots_dir: impl AsRef<Path>,
712    bank: &Bank,
713    snapshot_version: Option<SnapshotVersion>,
714    full_snapshot_archives_dir: impl AsRef<Path>,
715    incremental_snapshot_archives_dir: impl AsRef<Path>,
716    archive_format: ArchiveFormat,
717) -> snapshot_utils::Result<FullSnapshotArchiveInfo> {
718    let snapshot_version = snapshot_version.unwrap_or_default();
719    let temp_bank_snapshots_dir = tempfile::tempdir_in(bank_snapshots_dir)?;
720    bank_to_full_snapshot_archive_with(
721        &temp_bank_snapshots_dir,
722        bank,
723        snapshot_version,
724        full_snapshot_archives_dir,
725        incremental_snapshot_archives_dir,
726        archive_format,
727        false, // we do not intend to fastboot, so skip flushing and hard linking the storages
728    )
729}
730
731/// See bank_to_full_snapshot_archive() for documentation
732///
733/// This fn does *not* create a tmpdir inside `bank_snapshots_dir`
734/// (which is needed by a test)
735fn bank_to_full_snapshot_archive_with(
736    bank_snapshots_dir: impl AsRef<Path>,
737    bank: &Bank,
738    snapshot_version: SnapshotVersion,
739    full_snapshot_archives_dir: impl AsRef<Path>,
740    incremental_snapshot_archives_dir: impl AsRef<Path>,
741    archive_format: ArchiveFormat,
742    should_flush_and_hard_link_storages: bool,
743) -> snapshot_utils::Result<FullSnapshotArchiveInfo> {
744    assert!(bank.is_complete());
745    // set accounts-db's latest full snapshot slot here to ensure zero lamport
746    // accounts are handled properly.
747    bank.rc
748        .accounts
749        .accounts_db
750        .set_latest_full_snapshot_slot(bank.slot());
751    bank.squash(); // Bank may not be a root
752    bank.rehash(); // Bank may have been manually modified by the caller
753    bank.force_flush_accounts_cache();
754    bank.clean_accounts();
755
756    let snapshot_package = SnapshotPackage::new(
757        SnapshotKind::FullSnapshot,
758        bank,
759        bank.get_snapshot_storages(None),
760        bank.status_cache.read().unwrap().root_slot_deltas(),
761    );
762
763    let snapshot_config = SnapshotConfig {
764        full_snapshot_archives_dir: full_snapshot_archives_dir.as_ref().to_path_buf(),
765        incremental_snapshot_archives_dir: incremental_snapshot_archives_dir.as_ref().to_path_buf(),
766        bank_snapshots_dir: bank_snapshots_dir.as_ref().to_path_buf(),
767        archive_format,
768        snapshot_version,
769        ..Default::default()
770    };
771    let snapshot_archive_info = snapshot_utils::serialize_and_archive_snapshot_package(
772        snapshot_package,
773        &snapshot_config,
774        should_flush_and_hard_link_storages,
775    )?;
776
777    Ok(FullSnapshotArchiveInfo::new(snapshot_archive_info))
778}
779
780/// Convenience function to create an incremental snapshot archive out of any Bank, regardless of
781/// state.  The Bank will be frozen during the process.
782/// This is only called from ledger-tool or tests. Warping is a special case as well.
783///
784/// Requires:
785///     - `bank` is complete
786///     - `bank`'s slot is greater than `full_snapshot_slot`
787pub fn bank_to_incremental_snapshot_archive(
788    bank_snapshots_dir: impl AsRef<Path>,
789    bank: &Bank,
790    full_snapshot_slot: Slot,
791    snapshot_version: Option<SnapshotVersion>,
792    full_snapshot_archives_dir: impl AsRef<Path>,
793    incremental_snapshot_archives_dir: impl AsRef<Path>,
794    archive_format: ArchiveFormat,
795) -> snapshot_utils::Result<IncrementalSnapshotArchiveInfo> {
796    let snapshot_version = snapshot_version.unwrap_or_default();
797
798    assert!(bank.is_complete());
799    assert!(bank.slot() > full_snapshot_slot);
800    // set accounts-db's latest full snapshot slot here to ensure zero lamport
801    // accounts are handled properly.
802    bank.rc
803        .accounts
804        .accounts_db
805        .set_latest_full_snapshot_slot(full_snapshot_slot);
806    bank.squash(); // Bank may not be a root
807    bank.rehash(); // Bank may have been manually modified by the caller
808    bank.force_flush_accounts_cache();
809    bank.clean_accounts();
810
811    let snapshot_package = SnapshotPackage::new(
812        SnapshotKind::IncrementalSnapshot(full_snapshot_slot),
813        bank,
814        bank.get_snapshot_storages(Some(full_snapshot_slot)),
815        bank.status_cache.read().unwrap().root_slot_deltas(),
816    );
817
818    // Note: Since the snapshot_storages above are *only* the incremental storages,
819    // this bank snapshot *cannot* be used by fastboot.
820    // Putting the snapshot in a tempdir effectively enforces that.
821    let temp_bank_snapshots_dir = tempfile::tempdir_in(bank_snapshots_dir)?;
822    let snapshot_config = SnapshotConfig {
823        full_snapshot_archives_dir: full_snapshot_archives_dir.as_ref().to_path_buf(),
824        incremental_snapshot_archives_dir: incremental_snapshot_archives_dir.as_ref().to_path_buf(),
825        bank_snapshots_dir: temp_bank_snapshots_dir.path().to_path_buf(),
826        archive_format,
827        snapshot_version,
828        ..Default::default()
829    };
830    let snapshot_archive_info = snapshot_utils::serialize_and_archive_snapshot_package(
831        snapshot_package,
832        &snapshot_config,
833        false, // we do not intend to fastboot, so skip flushing and hard linking the storages
834    )?;
835
836    Ok(IncrementalSnapshotArchiveInfo::new(
837        full_snapshot_slot,
838        snapshot_archive_info,
839    ))
840}
841
842#[cfg(test)]
843mod tests {
844    use {
845        super::*,
846        crate::{
847            bank::{tests::create_simple_test_bank, BankTestConfig},
848            bank_forks::BankForks,
849            snapshot_config::SnapshotConfig,
850            snapshot_utils::{
851                clean_orphaned_account_snapshot_dirs, create_tmp_accounts_dir_for_tests,
852                get_bank_snapshot_dir, get_bank_snapshots, get_bank_snapshots_post,
853                get_bank_snapshots_pre, get_highest_bank_snapshot, get_highest_bank_snapshot_pre,
854                get_highest_loadable_bank_snapshot, get_snapshot_file_name,
855                purge_all_bank_snapshots, purge_bank_snapshot,
856                purge_bank_snapshots_older_than_slot, purge_incomplete_bank_snapshots,
857                purge_old_bank_snapshots, purge_old_bank_snapshots_at_startup,
858                snapshot_storage_rebuilder::get_slot_and_append_vec_id, BankSnapshotKind,
859                BANK_SNAPSHOT_PRE_FILENAME_EXTENSION, SNAPSHOT_FULL_SNAPSHOT_SLOT_FILENAME,
860            },
861            status_cache::Status,
862        },
863        solana_accounts_db::accounts_db::ACCOUNTS_DB_CONFIG_FOR_TESTING,
864        solana_genesis_config::create_genesis_config,
865        solana_keypair::Keypair,
866        solana_native_token::LAMPORTS_PER_SOL,
867        solana_signer::Signer,
868        solana_system_transaction as system_transaction,
869        solana_transaction::sanitized::SanitizedTransaction,
870        std::{
871            fs,
872            sync::{atomic::Ordering, Arc, RwLock},
873        },
874        test_case::test_case,
875    };
876
877    fn create_snapshot_dirs_for_tests(
878        genesis_config: &GenesisConfig,
879        bank_snapshots_dir: impl AsRef<Path>,
880        num_total: usize,
881        num_posts: usize,
882        should_flush_and_hard_link_storages: bool,
883    ) -> Bank {
884        assert!(num_posts <= num_total);
885
886        // We don't need the snapshot archives to live after this function returns,
887        // so let TempDir::drop() handle cleanup.
888        let snapshot_archives_dir = TempDir::new().unwrap();
889
890        let mut bank = Arc::new(Bank::new_for_tests(genesis_config));
891        for i in 0..num_total {
892            let slot = bank.slot() + 1;
893            bank = Arc::new(Bank::new_from_parent(bank, &Pubkey::new_unique(), slot));
894            bank.fill_bank_with_ticks_for_tests();
895
896            bank_to_full_snapshot_archive_with(
897                &bank_snapshots_dir,
898                &bank,
899                SnapshotVersion::default(),
900                &snapshot_archives_dir,
901                &snapshot_archives_dir,
902                SnapshotConfig::default().archive_format,
903                should_flush_and_hard_link_storages,
904            )
905            .unwrap();
906
907            // As a hack, to make a PRE bank snapshot, just rename the POST one.
908            if i >= num_posts {
909                let bank_snapshot_dir = get_bank_snapshot_dir(&bank_snapshots_dir, slot);
910                let post = bank_snapshot_dir.join(get_snapshot_file_name(slot));
911                let pre = post.with_extension(BANK_SNAPSHOT_PRE_FILENAME_EXTENSION);
912                fs::rename(post, pre).unwrap();
913            }
914        }
915
916        Arc::into_inner(bank).unwrap()
917    }
918
919    fn new_bank_from_parent_with_bank_forks(
920        bank_forks: &RwLock<BankForks>,
921        parent: Arc<Bank>,
922        collector_id: &Pubkey,
923        slot: Slot,
924    ) -> Arc<Bank> {
925        let bank = Bank::new_from_parent(parent, collector_id, slot);
926        bank_forks
927            .write()
928            .unwrap()
929            .insert(bank)
930            .clone_without_scheduler()
931    }
932
933    /// Test roundtrip of bank to a full snapshot, then back again.  This test creates the simplest
934    /// bank possible, so the contents of the snapshot archive will be quite minimal.
935    #[test]
936    fn test_roundtrip_bank_to_and_from_full_snapshot_simple() {
937        let genesis_config = GenesisConfig::default();
938        let original_bank = Bank::new_for_tests(&genesis_config);
939
940        original_bank.fill_bank_with_ticks_for_tests();
941
942        let (_tmp_dir, accounts_dir) = create_tmp_accounts_dir_for_tests();
943        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
944        let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
945        let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
946        let snapshot_archive_format = SnapshotConfig::default().archive_format;
947
948        let snapshot_archive_info = bank_to_full_snapshot_archive(
949            &bank_snapshots_dir,
950            &original_bank,
951            None,
952            full_snapshot_archives_dir.path(),
953            incremental_snapshot_archives_dir.path(),
954            snapshot_archive_format,
955        )
956        .unwrap();
957
958        let (roundtrip_bank, _) = bank_from_snapshot_archives(
959            &[accounts_dir],
960            bank_snapshots_dir.path(),
961            &snapshot_archive_info,
962            None,
963            &genesis_config,
964            &RuntimeConfig::default(),
965            None,
966            None,
967            None,
968            false,
969            false,
970            false,
971            Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
972            None,
973            Arc::default(),
974        )
975        .unwrap();
976        roundtrip_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
977        assert_eq!(original_bank, roundtrip_bank);
978    }
979
980    /// This tests handling of obsolete accounts during a full snapshot with obsolete accounts
981    /// marked in the accounts database. This test injects them directly
982    #[test]
983    fn test_roundtrip_bank_to_and_from_full_snapshot_with_obsolete_account() {
984        let collector = Pubkey::new_unique();
985        let key1 = Keypair::new();
986        let key2 = Keypair::new();
987        let key3 = Keypair::new();
988
989        // Create a few accounts
990        let (genesis_config, mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
991        let (bank0, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
992        bank0
993            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
994            .unwrap();
995        bank0
996            .transfer(2 * LAMPORTS_PER_SOL, &mint_keypair, &key2.pubkey())
997            .unwrap();
998        bank0
999            .transfer(3 * LAMPORTS_PER_SOL, &mint_keypair, &key3.pubkey())
1000            .unwrap();
1001        bank0.fill_bank_with_ticks_for_tests();
1002
1003        // Force flush the bank to create the account storage entry
1004        bank0.squash();
1005        bank0.force_flush_accounts_cache();
1006
1007        // Find the account storage entry for slot 0
1008        let target_slot = 0;
1009        let account_storage_entry = bank0
1010            .accounts()
1011            .accounts_db
1012            .storage
1013            .get_slot_storage_entry(target_slot)
1014            .unwrap();
1015
1016        // Find all the accounts in slot 0
1017        let accounts = bank0
1018            .accounts()
1019            .accounts_db
1020            .get_unique_accounts_from_storage(&account_storage_entry);
1021
1022        // Find the offset of pubkey `key1` in the accounts db slot0 and save the offset.
1023        let offset = accounts
1024            .stored_accounts
1025            .iter()
1026            .find(|account| key1.pubkey() == *account.pubkey())
1027            .map(|account| account.index_info.offset())
1028            .expect("Pubkey1 is present in Slot0");
1029
1030        // Create a new slot, and invalidate the account for key1 in slot0
1031        let slot = 1;
1032        let bank1 =
1033            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank0, &collector, slot);
1034        bank1
1035            .transfer(LAMPORTS_PER_SOL, &key3, &key1.pubkey())
1036            .unwrap();
1037
1038        bank1.fill_bank_with_ticks_for_tests();
1039
1040        // Mark the entry for pubkey1 as obsolete in slot0
1041        account_storage_entry.mark_accounts_obsolete(vec![(offset, 0)].into_iter(), slot);
1042
1043        let (_tmp_dir, accounts_dir) = create_tmp_accounts_dir_for_tests();
1044        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1045        let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1046        let snapshot_archive_format = SnapshotConfig::default().archive_format;
1047
1048        let full_snapshot_archive_info = bank_to_full_snapshot_archive(
1049            bank_snapshots_dir.path(),
1050            &bank1,
1051            None,
1052            snapshot_archives_dir.path(),
1053            snapshot_archives_dir.path(),
1054            snapshot_archive_format,
1055        )
1056        .unwrap();
1057
1058        let (roundtrip_bank, _) = bank_from_snapshot_archives(
1059            &[accounts_dir],
1060            bank_snapshots_dir.path(),
1061            &full_snapshot_archive_info,
1062            None,
1063            &genesis_config,
1064            &RuntimeConfig::default(),
1065            None,
1066            None,
1067            None,
1068            false,
1069            false,
1070            false,
1071            Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
1072            None,
1073            Arc::default(),
1074        )
1075        .unwrap();
1076        roundtrip_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
1077        assert_eq!(*bank1, roundtrip_bank);
1078    }
1079
1080    /// Test roundtrip of bank to a full snapshot, then back again.  This test is more involved
1081    /// than the simple version above; creating multiple banks over multiple slots and doing
1082    /// multiple transfers.  So this full snapshot should contain more data.
1083    #[test]
1084    fn test_roundtrip_bank_to_and_from_snapshot_complex() {
1085        let collector = Pubkey::new_unique();
1086        let key1 = Keypair::new();
1087        let key2 = Keypair::new();
1088        let key3 = Keypair::new();
1089        let key4 = Keypair::new();
1090        let key5 = Keypair::new();
1091
1092        let (genesis_config, mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1093        let (bank0, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
1094        bank0
1095            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1096            .unwrap();
1097        bank0
1098            .transfer(2 * LAMPORTS_PER_SOL, &mint_keypair, &key2.pubkey())
1099            .unwrap();
1100        bank0
1101            .transfer(3 * LAMPORTS_PER_SOL, &mint_keypair, &key3.pubkey())
1102            .unwrap();
1103        bank0.fill_bank_with_ticks_for_tests();
1104
1105        let slot = 1;
1106        let bank1 =
1107            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank0, &collector, slot);
1108        bank1
1109            .transfer(3 * LAMPORTS_PER_SOL, &mint_keypair, &key3.pubkey())
1110            .unwrap();
1111        bank1
1112            .transfer(4 * LAMPORTS_PER_SOL, &mint_keypair, &key4.pubkey())
1113            .unwrap();
1114        bank1
1115            .transfer(5 * LAMPORTS_PER_SOL, &mint_keypair, &key5.pubkey())
1116            .unwrap();
1117        bank1.fill_bank_with_ticks_for_tests();
1118
1119        let slot = slot + 1;
1120        let bank2 =
1121            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank1, &collector, slot);
1122        bank2
1123            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1124            .unwrap();
1125        bank2.fill_bank_with_ticks_for_tests();
1126
1127        let slot = slot + 1;
1128        let bank3 =
1129            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank2, &collector, slot);
1130        bank3
1131            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1132            .unwrap();
1133        bank3.fill_bank_with_ticks_for_tests();
1134
1135        let slot = slot + 1;
1136        let bank4 =
1137            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank3, &collector, slot);
1138        bank4
1139            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1140            .unwrap();
1141        bank4.fill_bank_with_ticks_for_tests();
1142
1143        let (_tmp_dir, accounts_dir) = create_tmp_accounts_dir_for_tests();
1144        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1145        let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1146        let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1147        let snapshot_archive_format = SnapshotConfig::default().archive_format;
1148
1149        let full_snapshot_archive_info = bank_to_full_snapshot_archive(
1150            bank_snapshots_dir.path(),
1151            &bank4,
1152            None,
1153            full_snapshot_archives_dir.path(),
1154            incremental_snapshot_archives_dir.path(),
1155            snapshot_archive_format,
1156        )
1157        .unwrap();
1158
1159        let (roundtrip_bank, _) = bank_from_snapshot_archives(
1160            &[accounts_dir],
1161            bank_snapshots_dir.path(),
1162            &full_snapshot_archive_info,
1163            None,
1164            &genesis_config,
1165            &RuntimeConfig::default(),
1166            None,
1167            None,
1168            None,
1169            false,
1170            false,
1171            false,
1172            Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
1173            None,
1174            Arc::default(),
1175        )
1176        .unwrap();
1177        roundtrip_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
1178        assert_eq!(*bank4, roundtrip_bank);
1179    }
1180
1181    /// Test roundtrip of bank to snapshots, then back again, with incremental snapshots.  In this
1182    /// version, build up a few slots and take a full snapshot.  Continue on a few more slots and
1183    /// take an incremental snapshot.  Rebuild the bank from both the incremental snapshot and full
1184    /// snapshot.
1185    ///
1186    /// For the full snapshot, touch all the accounts, but only one for the incremental snapshot.
1187    /// This is intended to mimic the real behavior of transactions, where only a small number of
1188    /// accounts are modified often, which are captured by the incremental snapshot.  The majority
1189    /// of the accounts are not modified often, and are captured by the full snapshot.
1190    #[test]
1191    fn test_roundtrip_bank_to_and_from_incremental_snapshot() {
1192        let collector = Pubkey::new_unique();
1193        let key1 = Keypair::new();
1194        let key2 = Keypair::new();
1195        let key3 = Keypair::new();
1196        let key4 = Keypair::new();
1197        let key5 = Keypair::new();
1198
1199        let (genesis_config, mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1200        let (bank0, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
1201        bank0
1202            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1203            .unwrap();
1204        bank0
1205            .transfer(2 * LAMPORTS_PER_SOL, &mint_keypair, &key2.pubkey())
1206            .unwrap();
1207        bank0
1208            .transfer(3 * LAMPORTS_PER_SOL, &mint_keypair, &key3.pubkey())
1209            .unwrap();
1210        bank0.fill_bank_with_ticks_for_tests();
1211
1212        let slot = 1;
1213        let bank1 =
1214            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank0, &collector, slot);
1215        bank1
1216            .transfer(3 * LAMPORTS_PER_SOL, &mint_keypair, &key3.pubkey())
1217            .unwrap();
1218        bank1
1219            .transfer(4 * LAMPORTS_PER_SOL, &mint_keypair, &key4.pubkey())
1220            .unwrap();
1221        bank1
1222            .transfer(5 * LAMPORTS_PER_SOL, &mint_keypair, &key5.pubkey())
1223            .unwrap();
1224        bank1.fill_bank_with_ticks_for_tests();
1225
1226        let (_tmp_dir, accounts_dir) = create_tmp_accounts_dir_for_tests();
1227        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1228        let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1229        let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1230        let snapshot_archive_format = SnapshotConfig::default().archive_format;
1231
1232        let full_snapshot_slot = slot;
1233        let full_snapshot_archive_info = bank_to_full_snapshot_archive(
1234            bank_snapshots_dir.path(),
1235            &bank1,
1236            None,
1237            full_snapshot_archives_dir.path(),
1238            incremental_snapshot_archives_dir.path(),
1239            snapshot_archive_format,
1240        )
1241        .unwrap();
1242
1243        let slot = slot + 1;
1244        let bank2 =
1245            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank1, &collector, slot);
1246        bank2
1247            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1248            .unwrap();
1249        bank2.fill_bank_with_ticks_for_tests();
1250
1251        let slot = slot + 1;
1252        let bank3 =
1253            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank2, &collector, slot);
1254        bank3
1255            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1256            .unwrap();
1257        bank3.fill_bank_with_ticks_for_tests();
1258
1259        let slot = slot + 1;
1260        let bank4 =
1261            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank3, &collector, slot);
1262        bank4
1263            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1264            .unwrap();
1265        bank4.fill_bank_with_ticks_for_tests();
1266
1267        let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive(
1268            bank_snapshots_dir.path(),
1269            &bank4,
1270            full_snapshot_slot,
1271            None,
1272            full_snapshot_archives_dir.path(),
1273            incremental_snapshot_archives_dir.path(),
1274            snapshot_archive_format,
1275        )
1276        .unwrap();
1277
1278        let (roundtrip_bank, _) = bank_from_snapshot_archives(
1279            &[accounts_dir],
1280            bank_snapshots_dir.path(),
1281            &full_snapshot_archive_info,
1282            Some(&incremental_snapshot_archive_info),
1283            &genesis_config,
1284            &RuntimeConfig::default(),
1285            None,
1286            None,
1287            None,
1288            false,
1289            false,
1290            false,
1291            Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
1292            None,
1293            Arc::default(),
1294        )
1295        .unwrap();
1296        roundtrip_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
1297        assert_eq!(*bank4, roundtrip_bank);
1298    }
1299
1300    /// Test rebuilding bank from the latest snapshot archives
1301    #[test]
1302    fn test_bank_from_latest_snapshot_archives() {
1303        let collector = Pubkey::new_unique();
1304        let key1 = Keypair::new();
1305        let key2 = Keypair::new();
1306        let key3 = Keypair::new();
1307
1308        let (genesis_config, mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1309        let (bank0, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
1310        bank0
1311            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1312            .unwrap();
1313        bank0
1314            .transfer(2 * LAMPORTS_PER_SOL, &mint_keypair, &key2.pubkey())
1315            .unwrap();
1316        bank0
1317            .transfer(3 * LAMPORTS_PER_SOL, &mint_keypair, &key3.pubkey())
1318            .unwrap();
1319        bank0.fill_bank_with_ticks_for_tests();
1320
1321        let slot = 1;
1322        let bank1 =
1323            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank0, &collector, slot);
1324        bank1
1325            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1326            .unwrap();
1327        bank1
1328            .transfer(2 * LAMPORTS_PER_SOL, &mint_keypair, &key2.pubkey())
1329            .unwrap();
1330        bank1
1331            .transfer(3 * LAMPORTS_PER_SOL, &mint_keypair, &key3.pubkey())
1332            .unwrap();
1333        bank1.fill_bank_with_ticks_for_tests();
1334
1335        let (_tmp_dir, accounts_dir) = create_tmp_accounts_dir_for_tests();
1336        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1337        let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1338        let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1339        let snapshot_archive_format = SnapshotConfig::default().archive_format;
1340
1341        let full_snapshot_slot = slot;
1342        bank_to_full_snapshot_archive(
1343            &bank_snapshots_dir,
1344            &bank1,
1345            None,
1346            &full_snapshot_archives_dir,
1347            &incremental_snapshot_archives_dir,
1348            snapshot_archive_format,
1349        )
1350        .unwrap();
1351
1352        let slot = slot + 1;
1353        let bank2 =
1354            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank1, &collector, slot);
1355        bank2
1356            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1357            .unwrap();
1358        bank2.fill_bank_with_ticks_for_tests();
1359
1360        let slot = slot + 1;
1361        let bank3 =
1362            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank2, &collector, slot);
1363        bank3
1364            .transfer(2 * LAMPORTS_PER_SOL, &mint_keypair, &key2.pubkey())
1365            .unwrap();
1366        bank3.fill_bank_with_ticks_for_tests();
1367
1368        let slot = slot + 1;
1369        let bank4 =
1370            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank3, &collector, slot);
1371        bank4
1372            .transfer(3 * LAMPORTS_PER_SOL, &mint_keypair, &key3.pubkey())
1373            .unwrap();
1374        bank4.fill_bank_with_ticks_for_tests();
1375
1376        bank_to_incremental_snapshot_archive(
1377            &bank_snapshots_dir,
1378            &bank4,
1379            full_snapshot_slot,
1380            None,
1381            &full_snapshot_archives_dir,
1382            &incremental_snapshot_archives_dir,
1383            snapshot_archive_format,
1384        )
1385        .unwrap();
1386
1387        let (deserialized_bank, ..) = bank_from_latest_snapshot_archives(
1388            &bank_snapshots_dir,
1389            &full_snapshot_archives_dir,
1390            &incremental_snapshot_archives_dir,
1391            &[accounts_dir],
1392            &genesis_config,
1393            &RuntimeConfig::default(),
1394            None,
1395            None,
1396            None,
1397            false,
1398            false,
1399            false,
1400            Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
1401            None,
1402            Arc::default(),
1403        )
1404        .unwrap();
1405        deserialized_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
1406        assert_eq!(deserialized_bank, *bank4);
1407    }
1408
1409    /// Test that cleaning works well in the edge cases of zero-lamport accounts and snapshots.
1410    /// Here's the scenario:
1411    ///
1412    /// slot 1:
1413    ///     - send some lamports to Account1 (from Account2) to bring it to life
1414    ///     - take a full snapshot
1415    /// slot 2:
1416    ///     - make Account1 have zero lamports (send back to Account2)
1417    ///     - take an incremental snapshot
1418    ///     - ensure deserializing from this snapshot is equal to this bank
1419    /// slot 3:
1420    ///     - remove Account2's reference back to slot 2 by transferring from the mint to Account2
1421    /// slot 4:
1422    ///     - ensure `clean_accounts()` has run and that Account1 is gone
1423    ///     - take another incremental snapshot
1424    ///     - ensure deserializing from this snapshots is equal to this bank
1425    ///     - ensure Account1 hasn't come back from the dead
1426    ///
1427    /// The check at slot 4 will fail with the pre-incremental-snapshot cleaning logic.  Because
1428    /// of the cleaning/purging at slot 4, the incremental snapshot at slot 4 will no longer have
1429    /// information about Account1, but the full snapshost _does_ have info for Account1, which is
1430    /// no longer correct!
1431    #[test]
1432    fn test_incremental_snapshots_handle_zero_lamport_accounts() {
1433        let collector = Pubkey::new_unique();
1434        let key1 = Keypair::new();
1435        let key2 = Keypair::new();
1436
1437        let (_tmp_dir, accounts_dir) = create_tmp_accounts_dir_for_tests();
1438        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1439        let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1440        let incremental_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1441        let snapshot_archive_format = SnapshotConfig::default().archive_format;
1442
1443        let (mut genesis_config, mint_keypair) =
1444            create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1445        // test expects 0 transaction fee
1446        genesis_config.fee_rate_governor = solana_fee_calculator::FeeRateGovernor::new(0, 0);
1447
1448        let lamports_to_transfer = 123_456 * LAMPORTS_PER_SOL;
1449        let (bank0, bank_forks) = Bank::new_with_paths_for_tests(
1450            &genesis_config,
1451            Arc::<RuntimeConfig>::default(),
1452            BankTestConfig::default(),
1453            vec![accounts_dir.clone()],
1454        )
1455        .wrap_with_bank_forks_for_tests();
1456        bank0
1457            .transfer(lamports_to_transfer, &mint_keypair, &key2.pubkey())
1458            .unwrap();
1459        bank0.fill_bank_with_ticks_for_tests();
1460
1461        let slot = 1;
1462        let bank1 =
1463            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank0, &collector, slot);
1464        bank1
1465            .transfer(lamports_to_transfer, &key2, &key1.pubkey())
1466            .unwrap();
1467        bank1.fill_bank_with_ticks_for_tests();
1468
1469        let full_snapshot_slot = slot;
1470        let full_snapshot_archive_info = bank_to_full_snapshot_archive(
1471            bank_snapshots_dir.path(),
1472            &bank1,
1473            None,
1474            full_snapshot_archives_dir.path(),
1475            incremental_snapshot_archives_dir.path(),
1476            snapshot_archive_format,
1477        )
1478        .unwrap();
1479
1480        let slot = slot + 1;
1481        let bank2 =
1482            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank1, &collector, slot);
1483        let blockhash = bank2.last_blockhash();
1484        let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer(
1485            &key1,
1486            &key2.pubkey(),
1487            lamports_to_transfer,
1488            blockhash,
1489        ));
1490        let fee = bank2.get_fee_for_message(tx.message()).unwrap();
1491        let tx = system_transaction::transfer(
1492            &key1,
1493            &key2.pubkey(),
1494            lamports_to_transfer - fee,
1495            blockhash,
1496        );
1497        bank2.process_transaction(&tx).unwrap();
1498        assert_eq!(
1499            bank2.get_balance(&key1.pubkey()),
1500            0,
1501            "Ensure Account1's balance is zero"
1502        );
1503        bank2.fill_bank_with_ticks_for_tests();
1504
1505        // Take an incremental snapshot and then do a roundtrip on the bank and ensure it
1506        // deserializes correctly.
1507        let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive(
1508            bank_snapshots_dir.path(),
1509            &bank2,
1510            full_snapshot_slot,
1511            None,
1512            full_snapshot_archives_dir.path(),
1513            incremental_snapshot_archives_dir.path(),
1514            snapshot_archive_format,
1515        )
1516        .unwrap();
1517        let (deserialized_bank, _) = bank_from_snapshot_archives(
1518            &[accounts_dir.clone()],
1519            bank_snapshots_dir.path(),
1520            &full_snapshot_archive_info,
1521            Some(&incremental_snapshot_archive_info),
1522            &genesis_config,
1523            &RuntimeConfig::default(),
1524            None,
1525            None,
1526            None,
1527            false,
1528            false,
1529            false,
1530            Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
1531            None,
1532            Arc::default(),
1533        )
1534        .unwrap();
1535        deserialized_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
1536        assert_eq!(
1537            deserialized_bank, *bank2,
1538            "Ensure rebuilding from an incremental snapshot works"
1539        );
1540
1541        let slot = slot + 1;
1542        let bank3 =
1543            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank2, &collector, slot);
1544        // Update Account2 so that it no longer holds a reference to slot2
1545        bank3
1546            .transfer(lamports_to_transfer, &mint_keypair, &key2.pubkey())
1547            .unwrap();
1548        bank3.fill_bank_with_ticks_for_tests();
1549
1550        let slot = slot + 1;
1551        let bank4 =
1552            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank3, &collector, slot);
1553        bank4.fill_bank_with_ticks_for_tests();
1554
1555        // Ensure account1 has been cleaned/purged from everywhere
1556        bank4.squash();
1557        bank4.clean_accounts();
1558        assert!(
1559            bank4.get_account_modified_slot(&key1.pubkey()).is_none(),
1560            "Ensure Account1 has been cleaned and purged from AccountsDb"
1561        );
1562
1563        // Take an incremental snapshot and then do a roundtrip on the bank and ensure it
1564        // deserializes correctly
1565        let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive(
1566            bank_snapshots_dir.path(),
1567            &bank4,
1568            full_snapshot_slot,
1569            None,
1570            full_snapshot_archives_dir.path(),
1571            incremental_snapshot_archives_dir.path(),
1572            snapshot_archive_format,
1573        )
1574        .unwrap();
1575
1576        let (deserialized_bank, _) = bank_from_snapshot_archives(
1577            &[accounts_dir],
1578            bank_snapshots_dir.path(),
1579            &full_snapshot_archive_info,
1580            Some(&incremental_snapshot_archive_info),
1581            &genesis_config,
1582            &RuntimeConfig::default(),
1583            None,
1584            None,
1585            None,
1586            false,
1587            false,
1588            false,
1589            Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
1590            None,
1591            Arc::default(),
1592        )
1593        .unwrap();
1594        deserialized_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
1595        assert_eq!(
1596            deserialized_bank, *bank4,
1597            "Ensure rebuilding from an incremental snapshot works",
1598        );
1599        assert!(
1600            deserialized_bank
1601                .get_account_modified_slot(&key1.pubkey())
1602                .is_none(),
1603            "Ensure Account1 has not been brought back from the dead"
1604        );
1605    }
1606
1607    #[test_case(StorageAccess::Mmap)]
1608    #[test_case(StorageAccess::File)]
1609    fn test_bank_fields_from_snapshot(storage_access: StorageAccess) {
1610        let collector = Pubkey::new_unique();
1611        let key1 = Keypair::new();
1612
1613        let (genesis_config, mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1614        let (bank0, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
1615        bank0.fill_bank_with_ticks_for_tests();
1616
1617        let slot = 1;
1618        let bank1 =
1619            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank0, &collector, slot);
1620        bank1.fill_bank_with_ticks_for_tests();
1621
1622        let all_snapshots_dir = tempfile::TempDir::new().unwrap();
1623        let snapshot_archive_format = SnapshotConfig::default().archive_format;
1624
1625        let full_snapshot_slot = slot;
1626        bank_to_full_snapshot_archive(
1627            &all_snapshots_dir,
1628            &bank1,
1629            None,
1630            &all_snapshots_dir,
1631            &all_snapshots_dir,
1632            snapshot_archive_format,
1633        )
1634        .unwrap();
1635
1636        let slot = slot + 1;
1637        let bank2 =
1638            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank1, &collector, slot);
1639        bank2
1640            .transfer(LAMPORTS_PER_SOL, &mint_keypair, &key1.pubkey())
1641            .unwrap();
1642        bank2.fill_bank_with_ticks_for_tests();
1643
1644        bank_to_incremental_snapshot_archive(
1645            &all_snapshots_dir,
1646            &bank2,
1647            full_snapshot_slot,
1648            None,
1649            &all_snapshots_dir,
1650            &all_snapshots_dir,
1651            snapshot_archive_format,
1652        )
1653        .unwrap();
1654
1655        let bank_fields = bank_fields_from_snapshot_archives(
1656            &all_snapshots_dir,
1657            &all_snapshots_dir,
1658            storage_access,
1659        )
1660        .unwrap();
1661        assert_eq!(bank_fields.slot, bank2.slot());
1662        assert_eq!(bank_fields.parent_slot, bank2.parent_slot());
1663    }
1664
1665    #[test]
1666    fn test_bank_snapshot_dir_accounts_hardlinks() {
1667        let bank = Bank::new_for_tests(&GenesisConfig::default());
1668        bank.fill_bank_with_ticks_for_tests();
1669
1670        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1671        let snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1672        bank_to_full_snapshot_archive_with(
1673            &bank_snapshots_dir,
1674            &bank,
1675            SnapshotVersion::default(),
1676            &snapshot_archives_dir,
1677            &snapshot_archives_dir,
1678            SnapshotConfig::default().archive_format,
1679            true,
1680        )
1681        .unwrap();
1682
1683        let accounts_hardlinks_dir = get_bank_snapshot_dir(&bank_snapshots_dir, bank.slot())
1684            .join(snapshot_utils::SNAPSHOT_ACCOUNTS_HARDLINKS);
1685        assert!(fs::metadata(&accounts_hardlinks_dir).is_ok());
1686
1687        let mut hardlink_dirs = Vec::new();
1688        // This directory contain symlinks to all accounts snapshot directories.
1689        for entry in fs::read_dir(accounts_hardlinks_dir).unwrap() {
1690            let entry = entry.unwrap();
1691            let symlink = entry.path();
1692            let dst_path = fs::read_link(symlink).unwrap();
1693            assert!(fs::metadata(&dst_path).is_ok());
1694            hardlink_dirs.push(dst_path);
1695        }
1696
1697        let bank_snapshot_dir = get_bank_snapshot_dir(&bank_snapshots_dir, bank.slot());
1698        assert!(purge_bank_snapshot(bank_snapshot_dir).is_ok());
1699
1700        // When the bank snapshot is removed, all the snapshot hardlink directories should be removed.
1701        assert!(hardlink_dirs.iter().all(|dir| fs::metadata(dir).is_err()));
1702    }
1703
1704    #[test_case(false)]
1705    #[test_case(true)]
1706    fn test_get_highest_bank_snapshot(should_flush_and_hard_link_storages: bool) {
1707        let genesis_config = GenesisConfig::default();
1708        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1709        let _bank = create_snapshot_dirs_for_tests(
1710            &genesis_config,
1711            &bank_snapshots_dir,
1712            4,
1713            0,
1714            should_flush_and_hard_link_storages,
1715        );
1716
1717        let snapshot = get_highest_bank_snapshot(&bank_snapshots_dir).unwrap();
1718        assert_eq!(snapshot.slot, 4);
1719
1720        let complete_flag_file = snapshot
1721            .snapshot_dir
1722            .join(snapshot_utils::SNAPSHOT_STATE_COMPLETE_FILENAME);
1723        fs::remove_file(complete_flag_file).unwrap();
1724        // The incomplete snapshot dir should still exist
1725        let snapshot_dir_4 = snapshot.snapshot_dir;
1726        assert!(snapshot_dir_4.exists());
1727        let snapshot = get_highest_bank_snapshot(&bank_snapshots_dir).unwrap();
1728        assert_eq!(snapshot.slot, 3);
1729
1730        let snapshot_version_file = snapshot
1731            .snapshot_dir
1732            .join(snapshot_utils::SNAPSHOT_VERSION_FILENAME);
1733        fs::remove_file(snapshot_version_file).unwrap();
1734        let snapshot = get_highest_bank_snapshot(&bank_snapshots_dir).unwrap();
1735        assert_eq!(snapshot.slot, 2);
1736
1737        let status_cache_file = snapshot
1738            .snapshot_dir
1739            .join(snapshot_utils::SNAPSHOT_STATUS_CACHE_FILENAME);
1740        fs::remove_file(status_cache_file).unwrap();
1741        let snapshot = get_highest_bank_snapshot(&bank_snapshots_dir).unwrap();
1742        assert_eq!(snapshot.slot, 1);
1743    }
1744
1745    #[test]
1746    fn test_clean_orphaned_account_snapshot_dirs() {
1747        let genesis_config = GenesisConfig::default();
1748        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1749        let _bank =
1750            create_snapshot_dirs_for_tests(&genesis_config, &bank_snapshots_dir, 2, 0, true);
1751
1752        let snapshot_dir_slot_2 = bank_snapshots_dir.path().join("2");
1753        let accounts_link_dir_slot_2 =
1754            snapshot_dir_slot_2.join(snapshot_utils::SNAPSHOT_ACCOUNTS_HARDLINKS);
1755
1756        // the symlinks point to the account snapshot hardlink directories <account_path>/snapshot/<slot>/ for slot 2
1757        // get them via read_link
1758        let hardlink_dirs_slot_2: Vec<PathBuf> = fs::read_dir(accounts_link_dir_slot_2)
1759            .unwrap()
1760            .map(|entry| {
1761                let symlink = entry.unwrap().path();
1762                fs::read_link(symlink).unwrap()
1763            })
1764            .collect();
1765
1766        // remove the bank snapshot directory for slot 2, so the account snapshot slot 2 directories become orphaned
1767        fs::remove_dir_all(snapshot_dir_slot_2).unwrap();
1768
1769        // verify the orphaned account snapshot hardlink directories are still there
1770        assert!(hardlink_dirs_slot_2
1771            .iter()
1772            .all(|dir| fs::metadata(dir).is_ok()));
1773
1774        let account_snapshot_paths: Vec<PathBuf> = hardlink_dirs_slot_2
1775            .iter()
1776            .map(|dir| dir.parent().unwrap().parent().unwrap().to_path_buf())
1777            .collect();
1778        // clean the orphaned hardlink directories
1779        clean_orphaned_account_snapshot_dirs(&bank_snapshots_dir, &account_snapshot_paths).unwrap();
1780
1781        // verify the hardlink directories are gone
1782        assert!(hardlink_dirs_slot_2
1783            .iter()
1784            .all(|dir| fs::metadata(dir).is_err()));
1785    }
1786
1787    // Ensure that `clean_orphaned_account_snapshot_dirs()` works correctly for bank snapshots
1788    // that *do not* hard link the storages into their staging dir.
1789    #[test]
1790    fn test_clean_orphaned_account_snapshot_dirs_no_hard_link() {
1791        let genesis_config = GenesisConfig::default();
1792        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1793        let _bank =
1794            create_snapshot_dirs_for_tests(&genesis_config, &bank_snapshots_dir, 2, 0, false);
1795
1796        // Ensure the bank snapshot dir does exist.
1797        let bank_snapshot_dir = snapshot_utils::get_bank_snapshot_dir(&bank_snapshots_dir, 2);
1798        assert!(fs::exists(&bank_snapshot_dir).unwrap());
1799
1800        // Ensure the accounts hard links dir does *not* exist for this bank snapshot
1801        // (since we asked create_snapshot_dirs_for_tests() to *not* hard link).
1802        let bank_snapshot_accounts_hard_link_dir =
1803            bank_snapshot_dir.join(snapshot_utils::SNAPSHOT_ACCOUNTS_HARDLINKS);
1804        assert!(!fs::exists(&bank_snapshot_accounts_hard_link_dir).unwrap());
1805
1806        // Now make sure clean_orphaned_account_snapshot_dirs() doesn't error.
1807        clean_orphaned_account_snapshot_dirs(&bank_snapshots_dir, &[]).unwrap();
1808    }
1809
1810    #[test_case(false)]
1811    #[test_case(true)]
1812    fn test_purge_incomplete_bank_snapshots(should_flush_and_hard_link_storages: bool) {
1813        let genesis_config = GenesisConfig::default();
1814        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1815        let _bank = create_snapshot_dirs_for_tests(
1816            &genesis_config,
1817            &bank_snapshots_dir,
1818            2,
1819            0,
1820            should_flush_and_hard_link_storages,
1821        );
1822
1823        // remove the "state complete" files so the snapshots will be purged
1824        for slot in [1, 2] {
1825            let bank_snapshot_dir = get_bank_snapshot_dir(&bank_snapshots_dir, slot);
1826            let state_complete_file =
1827                bank_snapshot_dir.join(snapshot_utils::SNAPSHOT_STATE_COMPLETE_FILENAME);
1828            fs::remove_file(state_complete_file).unwrap();
1829        }
1830
1831        purge_incomplete_bank_snapshots(&bank_snapshots_dir);
1832
1833        // ensure the bank snapshots dirs are gone
1834        for slot in [1, 2] {
1835            let bank_snapshot_dir = get_bank_snapshot_dir(&bank_snapshots_dir, slot);
1836            assert!(!bank_snapshot_dir.exists());
1837        }
1838    }
1839
1840    /// Test that snapshots correctly handle zero lamport accounts
1841    ///
1842    /// slot 1:
1843    ///     - send some lamports to Account1 (from Account2) to bring it to life
1844    ///     - Send some lamports to Account3 (from mint) to preserve this slot
1845    ///     - Root slot 1 and flush write cache
1846    /// slot 2:
1847    ///     - make Account1 have zero lamports (send back to Account2)
1848    /// slot 3:
1849    ///     - remove Account2's reference back to slot 2 by transferring from the mint to Account2
1850    ///     - take a full snap shot
1851    ///     - verify that recovery from full snapshot does not bring account1 back to life
1852    #[test_case(StorageAccess::Mmap)]
1853    #[test_case(StorageAccess::File)]
1854    fn test_snapshots_handle_zero_lamport_accounts(storage_access: StorageAccess) {
1855        let collector = Pubkey::new_unique();
1856        let key1 = Keypair::new();
1857        let key2 = Keypair::new();
1858        let key3 = Keypair::new();
1859
1860        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1861        let full_snapshot_archives_dir = tempfile::TempDir::new().unwrap();
1862        let snapshot_archive_format = SnapshotConfig::default().archive_format;
1863
1864        let (genesis_config, mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1865
1866        let lamports_to_transfer = 123_456 * LAMPORTS_PER_SOL;
1867        let bank_test_config = BankTestConfig {
1868            accounts_db_config: AccountsDbConfig {
1869                storage_access,
1870                ..ACCOUNTS_DB_CONFIG_FOR_TESTING
1871            },
1872        };
1873
1874        let bank0 = Bank::new_with_config_for_tests(&genesis_config, bank_test_config);
1875
1876        let (bank0, bank_forks) = Bank::wrap_with_bank_forks_for_tests(bank0);
1877
1878        bank0
1879            .transfer(lamports_to_transfer, &mint_keypair, &key2.pubkey())
1880            .unwrap();
1881
1882        bank0.fill_bank_with_ticks_for_tests();
1883
1884        let slot = 1;
1885        let bank1 =
1886            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank0, &collector, slot);
1887        bank1
1888            .transfer(lamports_to_transfer, &key2, &key1.pubkey())
1889            .unwrap();
1890        bank1
1891            .transfer(lamports_to_transfer, &mint_keypair, &key3.pubkey())
1892            .unwrap();
1893
1894        bank1.fill_bank_with_ticks_for_tests();
1895
1896        // Force rooting of slot1
1897        bank1.squash();
1898        bank1.force_flush_accounts_cache();
1899
1900        let slot = slot + 1;
1901        let bank2 =
1902            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank1, &collector, slot);
1903        let blockhash = bank2.last_blockhash();
1904        let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer(
1905            &key1,
1906            &key2.pubkey(),
1907            lamports_to_transfer,
1908            blockhash,
1909        ));
1910
1911        let fee = bank2.get_fee_for_message(tx.message()).unwrap();
1912        bank2
1913            .transfer(lamports_to_transfer - fee, &key1, &key2.pubkey())
1914            .unwrap();
1915
1916        assert_eq!(
1917            bank2.get_balance(&key1.pubkey()),
1918            0,
1919            "Ensure Account1's balance is zero"
1920        );
1921        bank2.fill_bank_with_ticks_for_tests();
1922
1923        let slot = slot + 1;
1924        let bank3 =
1925            new_bank_from_parent_with_bank_forks(bank_forks.as_ref(), bank2, &collector, slot);
1926        // Update Account2 so that it no longer holds a reference to slot2
1927        bank3
1928            .transfer(lamports_to_transfer, &mint_keypair, &key2.pubkey())
1929            .unwrap();
1930        bank3.fill_bank_with_ticks_for_tests();
1931
1932        assert!(
1933            bank3.get_account_modified_slot(&key1.pubkey()).is_none(),
1934            "Ensure Account1 has been cleaned and purged from AccountsDb"
1935        );
1936
1937        // Take full snapshot and then do a roundtrip on the bank and ensure it
1938        // deserializes correctly
1939        let full_snapshot_archive_info = bank_to_full_snapshot_archive(
1940            bank_snapshots_dir.path(),
1941            &bank3,
1942            None,
1943            full_snapshot_archives_dir.path(),
1944            full_snapshot_archives_dir.path(),
1945            snapshot_archive_format,
1946        )
1947        .unwrap();
1948
1949        let accounts_dir = tempfile::TempDir::new().unwrap();
1950        let other_bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1951        let (deserialized_bank, _) = bank_from_snapshot_archives(
1952            &[accounts_dir.path().to_path_buf()],
1953            other_bank_snapshots_dir.path(),
1954            &full_snapshot_archive_info,
1955            None,
1956            &genesis_config,
1957            &RuntimeConfig::default(),
1958            None,
1959            None,
1960            None,
1961            false,
1962            false,
1963            false,
1964            Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
1965            None,
1966            Arc::default(),
1967        )
1968        .unwrap();
1969
1970        deserialized_bank.wait_for_initial_accounts_hash_verification_completed_for_tests();
1971
1972        assert!(
1973            deserialized_bank
1974                .get_account_modified_slot(&key1.pubkey())
1975                .is_none(),
1976            "Ensure Account1 has not been brought back from the dead"
1977        );
1978
1979        assert_eq!(*bank3, deserialized_bank);
1980    }
1981
1982    #[test_case(StorageAccess::Mmap)]
1983    #[test_case(StorageAccess::File)]
1984    fn test_bank_from_snapshot_dir(storage_access: StorageAccess) {
1985        let genesis_config = GenesisConfig::default();
1986        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
1987        let bank = create_snapshot_dirs_for_tests(&genesis_config, &bank_snapshots_dir, 3, 0, true);
1988
1989        let bank_snapshot = get_highest_bank_snapshot(&bank_snapshots_dir).unwrap();
1990        let account_paths = &bank.rc.accounts.accounts_db.paths;
1991
1992        let (bank_constructed, ..) = bank_from_snapshot_dir(
1993            account_paths,
1994            &bank_snapshot,
1995            &genesis_config,
1996            &RuntimeConfig::default(),
1997            None,
1998            None,
1999            None,
2000            false,
2001            Some(AccountsDbConfig {
2002                storage_access,
2003                ..ACCOUNTS_DB_CONFIG_FOR_TESTING
2004            }),
2005            None,
2006            Arc::default(),
2007        )
2008        .unwrap();
2009
2010        bank_constructed.wait_for_initial_accounts_hash_verification_completed_for_tests();
2011        assert_eq!(bank_constructed, bank);
2012
2013        // Verify that the next_append_vec_id tracking is correct
2014        let mut max_id = 0;
2015        for path in account_paths {
2016            fs::read_dir(path).unwrap().for_each(|entry| {
2017                let path = entry.unwrap().path();
2018                let filename = path.file_name().unwrap();
2019                let (_slot, append_vec_id) =
2020                    get_slot_and_append_vec_id(filename.to_str().unwrap()).unwrap();
2021                max_id = std::cmp::max(max_id, append_vec_id);
2022            });
2023        }
2024        let next_id = bank.accounts().accounts_db.next_id.load(Ordering::Relaxed) as usize;
2025        assert_eq!(max_id, next_id - 1);
2026    }
2027
2028    #[test]
2029    fn test_bank_from_latest_snapshot_dir() {
2030        let genesis_config = GenesisConfig::default();
2031        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2032        let bank = create_snapshot_dirs_for_tests(&genesis_config, &bank_snapshots_dir, 3, 3, true);
2033
2034        let account_paths = &bank.rc.accounts.accounts_db.paths;
2035
2036        let deserialized_bank = bank_from_latest_snapshot_dir(
2037            &bank_snapshots_dir,
2038            &genesis_config,
2039            &RuntimeConfig::default(),
2040            account_paths,
2041            None,
2042            None,
2043            None,
2044            false,
2045            Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
2046            None,
2047            Arc::default(),
2048        )
2049        .unwrap();
2050
2051        assert_eq!(
2052            deserialized_bank, bank,
2053            "Ensure rebuilding bank from the highest snapshot dir results in the highest bank",
2054        );
2055    }
2056
2057    #[test_case(false)]
2058    #[test_case(true)]
2059    fn test_purge_all_bank_snapshots(should_flush_and_hard_link_storages: bool) {
2060        let genesis_config = GenesisConfig::default();
2061        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2062        let _bank = create_snapshot_dirs_for_tests(
2063            &genesis_config,
2064            &bank_snapshots_dir,
2065            10,
2066            5,
2067            should_flush_and_hard_link_storages,
2068        );
2069        // Keep bank in this scope so that its account_paths tmp dirs are not released, and purge_all_bank_snapshots
2070        // can clear the account hardlinks correctly.
2071
2072        assert_eq!(get_bank_snapshots(&bank_snapshots_dir).len(), 10);
2073        purge_all_bank_snapshots(&bank_snapshots_dir);
2074        assert_eq!(get_bank_snapshots(&bank_snapshots_dir).len(), 0);
2075    }
2076
2077    #[test_case(false)]
2078    #[test_case(true)]
2079    fn test_purge_old_bank_snapshots(should_flush_and_hard_link_storages: bool) {
2080        let genesis_config = GenesisConfig::default();
2081        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2082        let _bank = create_snapshot_dirs_for_tests(
2083            &genesis_config,
2084            &bank_snapshots_dir,
2085            10,
2086            5,
2087            should_flush_and_hard_link_storages,
2088        );
2089        // Keep bank in this scope so that its account_paths tmp dirs are not released, and purge_old_bank_snapshots
2090        // can clear the account hardlinks correctly.
2091
2092        assert_eq!(get_bank_snapshots(&bank_snapshots_dir).len(), 10);
2093
2094        purge_old_bank_snapshots(&bank_snapshots_dir, 3, Some(BankSnapshotKind::Pre));
2095        assert_eq!(get_bank_snapshots_pre(&bank_snapshots_dir).len(), 3);
2096
2097        purge_old_bank_snapshots(&bank_snapshots_dir, 2, Some(BankSnapshotKind::Post));
2098        assert_eq!(get_bank_snapshots_post(&bank_snapshots_dir).len(), 2);
2099
2100        assert_eq!(get_bank_snapshots(&bank_snapshots_dir).len(), 5);
2101
2102        purge_old_bank_snapshots(&bank_snapshots_dir, 2, None);
2103        assert_eq!(get_bank_snapshots(&bank_snapshots_dir).len(), 2);
2104
2105        purge_old_bank_snapshots(&bank_snapshots_dir, 0, None);
2106        assert_eq!(get_bank_snapshots(&bank_snapshots_dir).len(), 0);
2107    }
2108
2109    #[test_case(false)]
2110    #[test_case(true)]
2111    fn test_purge_bank_snapshots_older_than_slot(should_flush_and_hard_link_storages: bool) {
2112        let genesis_config = GenesisConfig::default();
2113        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2114
2115        // The bank must stay in scope to ensure the temp dirs that it holds are not dropped
2116        let _bank = create_snapshot_dirs_for_tests(
2117            &genesis_config,
2118            &bank_snapshots_dir,
2119            9,
2120            6,
2121            should_flush_and_hard_link_storages,
2122        );
2123        let bank_snapshots_before = get_bank_snapshots(&bank_snapshots_dir);
2124
2125        purge_bank_snapshots_older_than_slot(&bank_snapshots_dir, 0);
2126        let bank_snapshots_after = get_bank_snapshots(&bank_snapshots_dir);
2127        assert_eq!(bank_snapshots_before.len(), bank_snapshots_after.len());
2128
2129        purge_bank_snapshots_older_than_slot(&bank_snapshots_dir, 3);
2130        let bank_snapshots_after = get_bank_snapshots(&bank_snapshots_dir);
2131        assert_eq!(bank_snapshots_before.len(), bank_snapshots_after.len() + 2);
2132
2133        purge_bank_snapshots_older_than_slot(&bank_snapshots_dir, 8);
2134        let bank_snapshots_after = get_bank_snapshots(&bank_snapshots_dir);
2135        assert_eq!(bank_snapshots_before.len(), bank_snapshots_after.len() + 7);
2136
2137        purge_bank_snapshots_older_than_slot(&bank_snapshots_dir, Slot::MAX);
2138        let bank_snapshots_after = get_bank_snapshots(&bank_snapshots_dir);
2139        assert_eq!(bank_snapshots_before.len(), bank_snapshots_after.len() + 9);
2140        assert!(bank_snapshots_after.is_empty());
2141    }
2142
2143    #[test_case(false)]
2144    #[test_case(true)]
2145    fn test_purge_old_bank_snapshots_at_startup(should_flush_and_hard_link_storages: bool) {
2146        let genesis_config = GenesisConfig::default();
2147        let bank_snapshots_dir = tempfile::TempDir::new().unwrap();
2148
2149        // The bank must stay in scope to ensure the temp dirs that it holds are not dropped
2150        let _bank = create_snapshot_dirs_for_tests(
2151            &genesis_config,
2152            &bank_snapshots_dir,
2153            9,
2154            6,
2155            should_flush_and_hard_link_storages,
2156        );
2157
2158        purge_old_bank_snapshots_at_startup(&bank_snapshots_dir);
2159
2160        let bank_snapshots_pre = get_bank_snapshots_pre(&bank_snapshots_dir);
2161        assert!(bank_snapshots_pre.is_empty());
2162
2163        let bank_snapshots_post = get_bank_snapshots_post(&bank_snapshots_dir);
2164        assert_eq!(bank_snapshots_post.len(), 1);
2165        assert_eq!(bank_snapshots_post.first().unwrap().slot, 6);
2166    }
2167
2168    #[test]
2169    fn test_verify_slot_deltas_structural_bad_too_many_entries() {
2170        let bank_slot = status_cache::MAX_CACHE_ENTRIES as Slot + 1;
2171        let slot_deltas: Vec<_> = (0..bank_slot)
2172            .map(|slot| (slot, true, Status::default()))
2173            .collect();
2174
2175        let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
2176        assert_eq!(
2177            result,
2178            Err(VerifySlotDeltasError::TooManyEntries(
2179                status_cache::MAX_CACHE_ENTRIES + 1,
2180                status_cache::MAX_CACHE_ENTRIES
2181            )),
2182        );
2183    }
2184
2185    #[test]
2186    fn test_verify_slot_deltas_structural_good() {
2187        // NOTE: slot deltas do not need to be sorted
2188        let slot_deltas = vec![
2189            (222, true, Status::default()),
2190            (333, true, Status::default()),
2191            (111, true, Status::default()),
2192        ];
2193
2194        let bank_slot = 333;
2195        let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
2196        assert_eq!(
2197            result,
2198            Ok(VerifySlotDeltasStructuralInfo {
2199                slots: HashSet::from([111, 222, 333])
2200            })
2201        );
2202    }
2203
2204    #[test]
2205    fn test_verify_slot_deltas_structural_bad_slot_not_root() {
2206        let slot_deltas = vec![
2207            (111, true, Status::default()),
2208            (222, false, Status::default()), // <-- slot is not a root
2209            (333, true, Status::default()),
2210        ];
2211
2212        let bank_slot = 333;
2213        let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
2214        assert_eq!(result, Err(VerifySlotDeltasError::SlotIsNotRoot(222)));
2215    }
2216
2217    #[test]
2218    fn test_verify_slot_deltas_structural_bad_slot_greater_than_bank() {
2219        let slot_deltas = vec![
2220            (222, true, Status::default()),
2221            (111, true, Status::default()),
2222            (555, true, Status::default()), // <-- slot is greater than the bank slot
2223        ];
2224
2225        let bank_slot = 444;
2226        let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
2227        assert_eq!(
2228            result,
2229            Err(VerifySlotDeltasError::SlotGreaterThanMaxRoot(
2230                555, bank_slot
2231            )),
2232        );
2233    }
2234
2235    #[test]
2236    fn test_verify_slot_deltas_structural_bad_slot_has_multiple_entries() {
2237        let slot_deltas = vec![
2238            (111, true, Status::default()),
2239            (222, true, Status::default()),
2240            (111, true, Status::default()), // <-- slot is a duplicate
2241        ];
2242
2243        let bank_slot = 222;
2244        let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot);
2245        assert_eq!(
2246            result,
2247            Err(VerifySlotDeltasError::SlotHasMultipleEntries(111)),
2248        );
2249    }
2250
2251    #[test]
2252    fn test_verify_slot_deltas_with_history_good() {
2253        let mut slots_from_slot_deltas = HashSet::default();
2254        let mut slot_history = SlotHistory::default();
2255        // note: slot history expects slots to be added in numeric order
2256        for slot in [0, 111, 222, 333, 444] {
2257            slots_from_slot_deltas.insert(slot);
2258            slot_history.add(slot);
2259        }
2260
2261        let bank_slot = 444;
2262        let result =
2263            verify_slot_deltas_with_history(&slots_from_slot_deltas, &slot_history, bank_slot);
2264        assert_eq!(result, Ok(()));
2265    }
2266
2267    #[test]
2268    fn test_verify_slot_deltas_with_history_bad_slot_not_in_history() {
2269        let slots_from_slot_deltas = HashSet::from([
2270            0, // slot history has slot 0 added by default
2271            444, 222,
2272        ]);
2273        let mut slot_history = SlotHistory::default();
2274        slot_history.add(444); // <-- slot history is missing slot 222
2275
2276        let bank_slot = 444;
2277        let result =
2278            verify_slot_deltas_with_history(&slots_from_slot_deltas, &slot_history, bank_slot);
2279
2280        assert_eq!(
2281            result,
2282            Err(VerifySlotDeltasError::SlotNotFoundInHistory(222)),
2283        );
2284    }
2285
2286    #[test]
2287    fn test_verify_slot_deltas_with_history_bad_slot_not_in_deltas() {
2288        let slots_from_slot_deltas = HashSet::from([
2289            0, // slot history has slot 0 added by default
2290            444, 222,
2291            // <-- slot deltas is missing slot 333
2292        ]);
2293        let mut slot_history = SlotHistory::default();
2294        slot_history.add(222);
2295        slot_history.add(333);
2296        slot_history.add(444);
2297
2298        let bank_slot = 444;
2299        let result =
2300            verify_slot_deltas_with_history(&slots_from_slot_deltas, &slot_history, bank_slot);
2301
2302        assert_eq!(
2303            result,
2304            Err(VerifySlotDeltasError::SlotNotFoundInDeltas(333)),
2305        );
2306    }
2307
2308    #[test]
2309    fn test_verify_slot_history_good() {
2310        let mut slot_history = SlotHistory::default();
2311        // note: slot history expects slots to be added in numeric order
2312        for slot in [0, 111, 222, 333, 444] {
2313            slot_history.add(slot);
2314        }
2315
2316        let bank_slot = 444;
2317        let result = verify_slot_history(&slot_history, bank_slot);
2318        assert_eq!(result, Ok(()));
2319    }
2320
2321    #[test]
2322    fn test_verify_slot_history_bad_invalid_newest_slot() {
2323        let slot_history = SlotHistory::default();
2324        let bank_slot = 444;
2325        let result = verify_slot_history(&slot_history, bank_slot);
2326        assert_eq!(result, Err(VerifySlotHistoryError::InvalidNewestSlot));
2327    }
2328
2329    #[test]
2330    fn test_verify_slot_history_bad_invalid_num_entries() {
2331        let mut slot_history = SlotHistory::default();
2332        slot_history.bits.truncate(slot_history.bits.len() - 1);
2333
2334        let bank_slot = 0;
2335        let result = verify_slot_history(&slot_history, bank_slot);
2336        assert_eq!(result, Err(VerifySlotHistoryError::InvalidNumEntries));
2337    }
2338
2339    #[test]
2340    fn test_verify_epoch_stakes_good() {
2341        let bank = create_simple_test_bank(100 * LAMPORTS_PER_SOL);
2342        assert_eq!(verify_epoch_stakes(&bank), Ok(()));
2343    }
2344
2345    #[test]
2346    fn test_verify_epoch_stakes_bad() {
2347        let bank = create_simple_test_bank(100 * LAMPORTS_PER_SOL);
2348        let current_epoch = bank.epoch();
2349        let leader_schedule_epoch = bank.get_leader_schedule_epoch(bank.slot());
2350        let required_epochs = current_epoch..=leader_schedule_epoch;
2351
2352        // insert an invalid epoch into the epoch stakes
2353        {
2354            let mut epoch_stakes_map = bank.epoch_stakes_map().clone();
2355            let invalid_epoch = *required_epochs.end() + 1;
2356            epoch_stakes_map.insert(
2357                invalid_epoch,
2358                bank.epoch_stakes(bank.epoch()).cloned().unwrap(),
2359            );
2360
2361            assert_eq!(
2362                _verify_epoch_stakes(&epoch_stakes_map, required_epochs.clone()),
2363                Err(VerifyEpochStakesError::EpochGreaterThanMax(
2364                    invalid_epoch,
2365                    *required_epochs.end(),
2366                )),
2367            );
2368        }
2369
2370        // remove required stakes
2371        {
2372            for removed_epoch in required_epochs.clone() {
2373                let mut epoch_stakes_map = bank.epoch_stakes_map().clone();
2374                let removed_stakes = epoch_stakes_map.remove(&removed_epoch);
2375                assert!(removed_stakes.is_some());
2376
2377                assert_eq!(
2378                    _verify_epoch_stakes(&epoch_stakes_map, required_epochs.clone()),
2379                    Err(VerifyEpochStakesError::StakesNotFound(
2380                        removed_epoch,
2381                        required_epochs.clone(),
2382                    )),
2383                );
2384            }
2385        }
2386    }
2387
2388    #[test]
2389    fn test_get_highest_loadable_bank_snapshot() {
2390        let bank_snapshots_dir = TempDir::new().unwrap();
2391        let snapshot_archives_dir = TempDir::new().unwrap();
2392
2393        let snapshot_config = SnapshotConfig {
2394            bank_snapshots_dir: bank_snapshots_dir.as_ref().to_path_buf(),
2395            full_snapshot_archives_dir: snapshot_archives_dir.as_ref().to_path_buf(),
2396            incremental_snapshot_archives_dir: snapshot_archives_dir.as_ref().to_path_buf(),
2397            ..Default::default()
2398        };
2399        let load_only_snapshot_config = SnapshotConfig {
2400            bank_snapshots_dir: snapshot_config.bank_snapshots_dir.clone(),
2401            full_snapshot_archives_dir: snapshot_config.full_snapshot_archives_dir.clone(),
2402            incremental_snapshot_archives_dir: snapshot_config
2403                .incremental_snapshot_archives_dir
2404                .clone(),
2405            ..SnapshotConfig::new_load_only()
2406        };
2407
2408        let genesis_config = GenesisConfig::default();
2409        let mut bank = Arc::new(Bank::new_for_tests(&genesis_config));
2410        let mut full_snapshot_archive_info = None;
2411
2412        // take some snapshots, and archive them
2413        // note the `+1` at the end; we'll turn it into a PRE afterwards
2414        for _ in 0..snapshot_config
2415            .maximum_full_snapshot_archives_to_retain
2416            .get()
2417            + 1
2418        {
2419            let slot = bank.slot() + 1;
2420            bank = Arc::new(Bank::new_from_parent(bank, &Pubkey::default(), slot));
2421            bank.fill_bank_with_ticks_for_tests();
2422            full_snapshot_archive_info = Some(
2423                bank_to_full_snapshot_archive_with(
2424                    &snapshot_config.bank_snapshots_dir,
2425                    &bank,
2426                    snapshot_config.snapshot_version,
2427                    &snapshot_config.full_snapshot_archives_dir,
2428                    &snapshot_config.incremental_snapshot_archives_dir,
2429                    snapshot_config.archive_format,
2430                    false,
2431                )
2432                .unwrap(),
2433            );
2434        }
2435
2436        // As a hack, to make a PRE bank snapshot, just rename the last POST one.
2437        let slot = bank.slot();
2438        let bank_snapshot_dir = get_bank_snapshot_dir(&bank_snapshots_dir, slot);
2439        let post = bank_snapshot_dir.join(get_snapshot_file_name(slot));
2440        let pre = post.with_extension(BANK_SNAPSHOT_PRE_FILENAME_EXTENSION);
2441        fs::rename(post, pre).unwrap();
2442
2443        // ...and we also need to delete the last snapshot archive
2444        fs::remove_file(full_snapshot_archive_info.unwrap().path()).unwrap();
2445
2446        let highest_full_snapshot_archive =
2447            get_highest_full_snapshot_archive_info(&snapshot_archives_dir).unwrap();
2448        let highest_bank_snapshot_post =
2449            get_highest_bank_snapshot_post(&bank_snapshots_dir).unwrap();
2450        let highest_bank_snapshot_pre = get_highest_bank_snapshot_pre(&bank_snapshots_dir).unwrap();
2451
2452        // we want a bank snapshot PRE with the highest slot to ensure get_highest_loadable()
2453        // correctly skips bank snapshots PRE
2454        assert!(highest_bank_snapshot_pre.slot > highest_bank_snapshot_post.slot);
2455
2456        // 1. call get_highest_loadable() but bad snapshot dir, so returns None
2457        assert!(get_highest_loadable_bank_snapshot(&SnapshotConfig::default()).is_none());
2458
2459        // 2. the 'storages flushed' file hasn't been written yet, so get_highest_loadable() should return NONE
2460        assert!(get_highest_loadable_bank_snapshot(&snapshot_config).is_none());
2461
2462        // 3. write 'storages flushed' file, get_highest_loadable(), should return highest_bank_snapshot_post_slot
2463        snapshot_utils::write_storages_flushed_file(&highest_bank_snapshot_post.snapshot_dir)
2464            .unwrap();
2465        let bank_snapshot = get_highest_loadable_bank_snapshot(&snapshot_config).unwrap();
2466        assert_eq!(bank_snapshot, highest_bank_snapshot_post);
2467
2468        // 4. delete highest full snapshot archive, get_highest_loadable() should return NONE
2469        fs::remove_file(highest_full_snapshot_archive.path()).unwrap();
2470        assert!(get_highest_loadable_bank_snapshot(&snapshot_config).is_none());
2471
2472        // 5. get_highest_loadable(), but with a load-only snapshot config, should return Some()
2473        let bank_snapshot = get_highest_loadable_bank_snapshot(&load_only_snapshot_config).unwrap();
2474        assert_eq!(bank_snapshot, highest_bank_snapshot_post);
2475
2476        // 6. delete highest bank snapshot, get_highest_loadable() should return NONE
2477        fs::remove_dir_all(&highest_bank_snapshot_post.snapshot_dir).unwrap();
2478        assert!(get_highest_loadable_bank_snapshot(&snapshot_config).is_none());
2479
2480        // 7. write 'storages flushed' file, get_highest_loadable() should return Some() again, with slot-1
2481        snapshot_utils::write_storages_flushed_file(get_bank_snapshot_dir(
2482            &snapshot_config.bank_snapshots_dir,
2483            highest_bank_snapshot_post.slot - 1,
2484        ))
2485        .unwrap();
2486        let bank_snapshot = get_highest_loadable_bank_snapshot(&snapshot_config).unwrap();
2487        assert_eq!(bank_snapshot.slot, highest_bank_snapshot_post.slot - 1);
2488
2489        // 8. delete the full snapshot slot file, get_highest_loadable() should return NONE
2490        fs::remove_file(
2491            bank_snapshot
2492                .snapshot_dir
2493                .join(SNAPSHOT_FULL_SNAPSHOT_SLOT_FILENAME),
2494        )
2495        .unwrap();
2496        assert!(get_highest_loadable_bank_snapshot(&snapshot_config).is_none());
2497
2498        // 9. however, a load-only snapshot config should return Some() again
2499        let bank_snapshot2 =
2500            get_highest_loadable_bank_snapshot(&load_only_snapshot_config).unwrap();
2501        assert_eq!(bank_snapshot2, bank_snapshot);
2502    }
2503}