solana_runtime/
snapshot_bank_utils.rs

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