solana_runtime/
snapshot_bank_utils.rs

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