From 63014a833b7531d561e0542e43eb78ff30d8e996 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Thu, 29 Jul 2021 12:40:45 -0500 Subject: [PATCH] Add snapshot_utils::bank_from_latest_snapshot_archives() While reviewing PR #18565, as issue was brought up to refactor some code around verifying the bank after rebuilding from snapshots. A new top-level function has been added to get the latest snapshot archives and load the bank then verify. Additionally, new tests have been written and existing tests have been updated to use this new function. Fixes #18973 While resolving the issue, it became clear there was some additional low-hanging fruit this change enabled. Specifically, the functions `bank_to_xxx_snapshot_archive()` now return their respective `SnapshotArchiveInfo`. And on the flip side, `bank_from_snapshot_archives()` now takes `SnapshotArchiveInfo`s instead of separate paths and archive formats. This bundling simplifies bank rebuilding. --- core/src/validator.rs | 32 +-- core/tests/snapshots.rs | 81 ++----- ledger-tool/src/main.rs | 4 +- ledger/src/bank_forks_utils.rs | 55 +---- runtime/src/snapshot_utils.rs | 396 +++++++++++++++++++++++++-------- 5 files changed, 357 insertions(+), 211 deletions(-) diff --git a/core/src/validator.rs b/core/src/validator.rs index 77ba0c8982c3c9..81e3c92dd13976 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -1198,20 +1198,24 @@ fn new_banks_from_ledger( ); leader_schedule_cache.set_root(&bank_forks.root_bank()); - let archive_file = solana_runtime::snapshot_utils::bank_to_full_snapshot_archive( - ledger_path, - &bank_forks.root_bank(), - None, - &snapshot_config.snapshot_package_output_path, - snapshot_config.archive_format, - Some(bank_forks.root_bank().get_thread_pool()), - snapshot_config.maximum_snapshots_to_retain, - ) - .unwrap_or_else(|err| { - error!("Unable to create snapshot: {}", err); - abort(); - }); - info!("created snapshot: {}", archive_file.display()); + let full_snapshot_archive_info = + solana_runtime::snapshot_utils::bank_to_full_snapshot_archive( + ledger_path, + &bank_forks.root_bank(), + None, + &snapshot_config.snapshot_package_output_path, + snapshot_config.archive_format, + Some(bank_forks.root_bank().get_thread_pool()), + snapshot_config.maximum_snapshots_to_retain, + ) + .unwrap_or_else(|err| { + error!("Unable to create snapshot: {}", err); + abort(); + }); + info!( + "created snapshot: {}", + full_snapshot_archive_info.path().display() + ); } let tower = post_process_restored_tower( diff --git a/core/tests/snapshots.rs b/core/tests/snapshots.rs index 5be4eb825fa744..17761f6a43b2f8 100644 --- a/core/tests/snapshots.rs +++ b/core/tests/snapshots.rs @@ -158,7 +158,16 @@ mod tests { let old_last_bank = old_bank_forks.get(old_last_slot).unwrap(); - let check_hash_calculation = false; + let full_snapshot_archive_path = snapshot_utils::build_full_snapshot_archive_path( + snapshot_package_output_path.to_path_buf(), + old_last_bank.slot(), + &old_last_bank.get_accounts_hash(), + ArchiveFormat::TarBzip2, + ); + let full_snapshot_archive_info = + snapshot_utils::FullSnapshotArchiveInfo::new_from_path(full_snapshot_archive_path) + .unwrap(); + let (deserialized_bank, _timing) = snapshot_utils::bank_from_snapshot_archives( account_paths, &[], @@ -167,14 +176,8 @@ mod tests { .as_ref() .unwrap() .snapshot_path, - snapshot_utils::build_full_snapshot_archive_path( - snapshot_package_output_path.to_path_buf(), - old_last_bank.slot(), - &old_last_bank.get_accounts_hash(), - ArchiveFormat::TarBzip2, - ), + &full_snapshot_archive_info, None, - ArchiveFormat::TarBzip2, old_genesis_config, None, None, @@ -182,7 +185,7 @@ mod tests { false, None, accounts_db::AccountShrinkThreshold::default(), - check_hash_calculation, + false, false, ) .unwrap(); @@ -713,9 +716,8 @@ mod tests { ) .unwrap(); - restore_from_incremental_snapshot_and_check_banks_are_equal( + restore_from_snapshots_and_check_banks_are_equal( &bank, - last_full_snapshot_slot.unwrap(), &snapshot_test_config.snapshot_config, snapshot_test_config.accounts_dir.path().to_path_buf(), &snapshot_test_config.genesis_config_info.genesis_config, @@ -780,57 +782,17 @@ mod tests { Ok(()) } - fn restore_from_incremental_snapshot_and_check_banks_are_equal( + fn restore_from_snapshots_and_check_banks_are_equal( bank: &Bank, - last_full_snapshot_slot: Slot, snapshot_config: &SnapshotConfig, accounts_dir: PathBuf, genesis_config: &GenesisConfig, ) -> snapshot_utils::Result<()> { - let ( - full_snapshot_archive_slot, - (incremental_snapshot_archive_base_slot, incremental_snapshot_archive_slot), - deserialized_bank, - ) = restore_from_incremental_snapshot(snapshot_config, accounts_dir, genesis_config)?; - - assert_eq!( - full_snapshot_archive_slot, - incremental_snapshot_archive_base_slot - ); - assert_eq!(full_snapshot_archive_slot, last_full_snapshot_slot); - assert_eq!(incremental_snapshot_archive_slot, bank.slot(),); - assert_eq!(*bank, deserialized_bank); - - Ok(()) - } - - fn restore_from_incremental_snapshot( - snapshot_config: &SnapshotConfig, - accounts_dir: PathBuf, - genesis_config: &GenesisConfig, - ) -> snapshot_utils::Result<(Slot, (Slot, Slot), Bank)> { - let full_snapshot_archive_info = snapshot_utils::get_highest_full_snapshot_archive_info( + let (deserialized_bank, _) = snapshot_utils::bank_from_latest_snapshot_archives( + &snapshot_config.snapshot_path, &snapshot_config.snapshot_package_output_path, - ) - .ok_or_else(|| Error::new(ErrorKind::Other, "no full snapshot"))?; - - let incremental_snapshot_archive_info = - snapshot_utils::get_highest_incremental_snapshot_archive_info( - &snapshot_config.snapshot_package_output_path, - *full_snapshot_archive_info.slot(), - ) - .ok_or_else(|| Error::new(ErrorKind::Other, "no incremental snapshot"))?; - - info!("Restoring bank from full snapshot slot: {}, and incremental snapshot slot: {} (with base slot: {})", - full_snapshot_archive_info.slot(), incremental_snapshot_archive_info.slot(), incremental_snapshot_archive_info.base_slot()); - - let (deserialized_bank, _) = snapshot_utils::bank_from_snapshot_archives( &[accounts_dir], &[], - &snapshot_config.snapshot_path, - full_snapshot_archive_info.path(), - Some(incremental_snapshot_archive_info.path()), - snapshot_config.archive_format, genesis_config, None, None, @@ -842,13 +804,8 @@ mod tests { false, )?; - Ok(( - *full_snapshot_archive_info.slot(), - ( - *incremental_snapshot_archive_info.base_slot(), - *incremental_snapshot_archive_info.slot(), - ), - deserialized_bank, - )) + assert_eq!(bank, &deserialized_bank); + + Ok(()) } } diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index d628a65c03b219..7199e7466b90b9 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -2198,7 +2198,7 @@ fn main() { bank.slot(), ); - let archive_file = snapshot_utils::bank_to_full_snapshot_archive( + let full_snapshot_archive_info = snapshot_utils::bank_to_full_snapshot_archive( ledger_path, &bank, Some(snapshot_version), @@ -2216,7 +2216,7 @@ fn main() { "Successfully created snapshot for slot {}, hash {}: {}", bank.slot(), bank.hash(), - archive_file.display(), + full_snapshot_archive_info.path().display(), ); println!( "Shred version: {}", diff --git a/ledger/src/bank_forks_utils.rs b/ledger/src/bank_forks_utils.rs index 7be4056d8e0be6..be339903335727 100644 --- a/ledger/src/bank_forks_utils.rs +++ b/ledger/src/bank_forks_utils.rs @@ -8,11 +8,7 @@ use crate::{ }; use log::*; use solana_entry::entry::VerifyRecyclers; -use solana_runtime::{ - bank_forks::BankForks, - snapshot_config::SnapshotConfig, - snapshot_utils::{self, FullSnapshotArchiveInfo}, -}; +use solana_runtime::{bank_forks::BankForks, snapshot_config::SnapshotConfig, snapshot_utils}; use solana_sdk::{clock::Slot, genesis_config::GenesisConfig, hash::Hash}; use std::{fs, path::PathBuf, process, result}; @@ -44,20 +40,18 @@ pub fn load( transaction_status_sender: Option<&TransactionStatusSender>, cache_block_meta_sender: Option<&CacheBlockMetaSender>, ) -> LoadResult { - if let Some(snapshot_config) = snapshot_config.as_ref() { + if let Some(snapshot_config) = snapshot_config { info!( - "Initializing snapshot path: {:?}", - snapshot_config.snapshot_path + "Initializing snapshot path: {}", + snapshot_config.snapshot_path.display() ); let _ = fs::remove_dir_all(&snapshot_config.snapshot_path); fs::create_dir_all(&snapshot_config.snapshot_path) .expect("Couldn't create snapshot directory"); - if let Some(full_snapshot_archive_info) = - snapshot_utils::get_highest_full_snapshot_archive_info( - &snapshot_config.snapshot_package_output_path, - ) - { + if let Some(_) = snapshot_utils::get_highest_full_snapshot_archive_info( + &snapshot_config.snapshot_package_output_path, + ) { return load_from_snapshot( genesis_config, blockstore, @@ -67,7 +61,6 @@ pub fn load( process_options, transaction_status_sender, cache_block_meta_sender, - &full_snapshot_archive_info, ); } else { info!("No snapshot package available; will load from genesis"); @@ -115,26 +108,18 @@ fn load_from_snapshot( process_options: ProcessOptions, transaction_status_sender: Option<&TransactionStatusSender>, cache_block_meta_sender: Option<&CacheBlockMetaSender>, - full_snapshot_archive_info: &FullSnapshotArchiveInfo, ) -> LoadResult { - info!( - "Loading snapshot package: {:?}", - full_snapshot_archive_info.path() - ); - // Fail hard here if snapshot fails to load, don't silently continue if account_paths.is_empty() { error!("Account paths not present when booting from snapshot"); process::exit(1); } - let (deserialized_bank, timings) = snapshot_utils::bank_from_snapshot_archives( + let (deserialized_bank, timings) = snapshot_utils::bank_from_latest_snapshot_archives( + &snapshot_config.snapshot_path, + &snapshot_config.snapshot_package_output_path, &account_paths, &process_options.frozen_accounts, - &snapshot_config.snapshot_path, - full_snapshot_archive_info.path(), - None, - *full_snapshot_archive_info.archive_format(), genesis_config, process_options.debug_keys.clone(), Some(&crate::builtins::get(process_options.bpf_jit)), @@ -146,30 +131,14 @@ fn load_from_snapshot( process_options.verify_index, ) .expect("Load from snapshot failed"); - if let Some(shrink_paths) = shrink_paths { - deserialized_bank.set_shrink_paths(shrink_paths); - } let deserialized_bank_slot_and_hash = ( deserialized_bank.slot(), deserialized_bank.get_accounts_hash(), ); - if deserialized_bank_slot_and_hash - != ( - *full_snapshot_archive_info.slot(), - *full_snapshot_archive_info.hash(), - ) - { - error!( - "Snapshot has mismatch:\narchive: {:?}\ndeserialized: {:?}", - ( - full_snapshot_archive_info.slot(), - full_snapshot_archive_info.hash() - ), - deserialized_bank_slot_and_hash - ); - process::exit(1); + if let Some(shrink_paths) = shrink_paths { + deserialized_bank.set_shrink_paths(shrink_paths); } to_loadresult( diff --git a/runtime/src/snapshot_utils.rs b/runtime/src/snapshot_utils.rs index 05f62205fb2622..ef98f95bf775fd 100644 --- a/runtime/src/snapshot_utils.rs +++ b/runtime/src/snapshot_utils.rs @@ -40,7 +40,7 @@ use { }; /// Common information about a snapshot archive -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Clone)] struct SnapshotArchiveInfo { /// Path to the snapshot archive file path: PathBuf, @@ -56,21 +56,25 @@ struct SnapshotArchiveInfo { } /// Information about a full snapshot archive: its path, slot, hash, and archive format -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Clone)] pub struct FullSnapshotArchiveInfo(SnapshotArchiveInfo); impl FullSnapshotArchiveInfo { /// Parse the path to a full snapshot archive and return a new `FullSnapshotArchiveInfo` - fn new_from_path(path: PathBuf) -> Result { + pub fn new_from_path(path: PathBuf) -> Result { let filename = path_to_file_name_str(path.as_path())?; let (slot, hash, archive_format) = parse_full_snapshot_archive_filename(filename)?; - Ok(Self(SnapshotArchiveInfo { + Ok(Self::new(path, slot, hash, archive_format)) + } + + fn new(path: PathBuf, slot: Slot, hash: Hash, archive_format: ArchiveFormat) -> Self { + Self(SnapshotArchiveInfo { path, slot, hash, archive_format, - })) + }) } pub fn path(&self) -> &PathBuf { @@ -104,7 +108,7 @@ impl Ord for FullSnapshotArchiveInfo { } /// Information about an incremental snapshot archive: its path, slot, base slot, hash, and archive format -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Clone)] pub struct IncrementalSnapshotArchiveInfo { /// The slot that the incremental snapshot was based from. This is the same as the full /// snapshot slot used when making the incremental snapshot. @@ -117,12 +121,22 @@ pub struct IncrementalSnapshotArchiveInfo { impl IncrementalSnapshotArchiveInfo { /// Parse the path to an incremental snapshot archive and return a new `IncrementalSnapshotArchiveInfo` - fn new_from_path(path: PathBuf) -> Result { + pub fn new_from_path(path: PathBuf) -> Result { let filename = path_to_file_name_str(path.as_path())?; let (base_slot, slot, hash, archive_format) = parse_incremental_snapshot_archive_filename(filename)?; - Ok(Self { + Ok(Self::new(path, base_slot, slot, hash, archive_format)) + } + + fn new( + path: PathBuf, + base_slot: Slot, + slot: Slot, + hash: Hash, + archive_format: ArchiveFormat, + ) -> Self { + Self { base_slot, inner: SnapshotArchiveInfo { path, @@ -130,7 +144,7 @@ impl IncrementalSnapshotArchiveInfo { hash, archive_format, }, - }) + } } pub fn path(&self) -> &PathBuf { @@ -145,11 +159,11 @@ impl IncrementalSnapshotArchiveInfo { &self.inner.slot } - fn _hash(&self) -> &Hash { + fn hash(&self) -> &Hash { &self.inner.hash } - fn _archive_format(&self) -> &ArchiveFormat { + fn archive_format(&self) -> &ArchiveFormat { &self.inner.archive_format } } @@ -324,6 +338,12 @@ pub enum SnapshotError { #[error("snapshots are incompatible: full snapshot slot ({0}) and incremental snapshot base slot ({1}) do not match")] MismatchedBaseSlot(Slot, Slot), + + #[error("no snapshot archives to load from")] + NoSnapshotArchives, + + #[error("snapshot has mismatch: deserialized bank: {:?}, snapshot archive info: {:?}", .0, .1)] + MismatchedSlotHash((Slot, Hash), (Slot, Hash)), } pub type Result = std::result::Result; @@ -990,13 +1010,12 @@ const PARALLEL_UNTAR_READERS_DEFAULT: usize = 4; /// Rebuild bank from snapshot archives. Handles either just a full snapshot, or both a full /// snapshot and an incremental snapshot. #[allow(clippy::too_many_arguments)] -pub fn bank_from_snapshot_archives

( +pub fn bank_from_snapshot_archives( account_paths: &[PathBuf], frozen_account_pubkeys: &[Pubkey], - snapshots_dir: &Path, - full_snapshot_archive_path: P, - incremental_snapshot_archive_path: Option

, - archive_format: ArchiveFormat, + snapshots_dir: impl AsRef, + full_snapshot_archive_info: &FullSnapshotArchiveInfo, + incremental_snapshot_archive_info: Option<&IncrementalSnapshotArchiveInfo>, genesis_config: &GenesisConfig, debug_keys: Option>>, additional_builtins: Option<&Builtins>, @@ -1006,39 +1025,36 @@ pub fn bank_from_snapshot_archives

( shrink_ratio: AccountShrinkThreshold, test_hash_calculation: bool, verify_index: bool, -) -> Result<(Bank, BankFromArchiveTimings)> -where - P: AsRef + std::marker::Sync, -{ +) -> Result<(Bank, BankFromArchiveTimings)> { + check_are_snapshots_compatible( + full_snapshot_archive_info, + incremental_snapshot_archive_info, + )?; + let parallel_divisions = std::cmp::min( PARALLEL_UNTAR_READERS_DEFAULT, std::cmp::max(1, num_cpus::get() / 4), ); let unarchived_full_snapshot = unarchive_snapshot( - snapshots_dir, + &snapshots_dir, TMP_FULL_SNAPSHOT_PREFIX, - &full_snapshot_archive_path, + full_snapshot_archive_info.path(), "snapshot untar", account_paths, - archive_format, + *full_snapshot_archive_info.archive_format(), parallel_divisions, )?; let mut unarchived_incremental_snapshot = - if let Some(incremental_snapshot_archive_path) = incremental_snapshot_archive_path { - check_are_snapshots_compatible( - &full_snapshot_archive_path, - &incremental_snapshot_archive_path, - )?; - + if let Some(incremental_snapshot_archive_info) = incremental_snapshot_archive_info { let unarchived_incremental_snapshot = unarchive_snapshot( - snapshots_dir, + &snapshots_dir, TMP_INCREMENTAL_SNAPSHOT_PREFIX, - &incremental_snapshot_archive_path, + incremental_snapshot_archive_info.path(), "incremental snapshot untar", account_paths, - archive_format, + *incremental_snapshot_archive_info.archive_format(), parallel_divisions, )?; Some(unarchived_incremental_snapshot) @@ -1096,6 +1112,87 @@ where Ok((bank, timings)) } +/// Rebuild bank from snapshot archives. This function searches `snapshot_archives_dir` for the +/// highest full snapshot and highest corresponding incremental snapshot, then rebuilds the bank. +#[allow(clippy::too_many_arguments)] +pub fn bank_from_latest_snapshot_archives( + snapshots_dir: impl AsRef, + snapshot_archives_dir: impl AsRef, + account_paths: &[PathBuf], + frozen_account_pubkeys: &[Pubkey], + genesis_config: &GenesisConfig, + debug_keys: Option>>, + additional_builtins: Option<&Builtins>, + account_secondary_indexes: AccountSecondaryIndexes, + accounts_db_caching_enabled: bool, + limit_load_slot_count_from_snapshot: Option, + shrink_ratio: AccountShrinkThreshold, + test_hash_calculation: bool, + verify_index: bool, +) -> Result<(Bank, BankFromArchiveTimings)> { + let full_snapshot_archive_info = get_highest_full_snapshot_archive_info(&snapshot_archives_dir) + .ok_or(SnapshotError::NoSnapshotArchives)?; + + let incremental_snapshot_archive_info = get_highest_incremental_snapshot_archive_info( + &snapshot_archives_dir, + *full_snapshot_archive_info.slot(), + ); + + info!( + "Loading bank from full snapshot: {}, and incremental snapshot: {:?}", + full_snapshot_archive_info.path().display(), + incremental_snapshot_archive_info + .as_ref() + .map(|isai| isai.path().display()) + ); + + let (bank, timings) = bank_from_snapshot_archives( + account_paths, + frozen_account_pubkeys, + snapshots_dir.as_ref(), + &full_snapshot_archive_info, + incremental_snapshot_archive_info.as_ref(), + genesis_config, + debug_keys, + additional_builtins, + account_secondary_indexes, + accounts_db_caching_enabled, + limit_load_slot_count_from_snapshot, + shrink_ratio, + test_hash_calculation, + verify_index, + )?; + + check_bank_with_snapshot_archive_info( + &bank, + *incremental_snapshot_archive_info + .as_ref() + .map_or(full_snapshot_archive_info.slot(), |isai| isai.slot()), + *incremental_snapshot_archive_info + .as_ref() + .map_or(full_snapshot_archive_info.hash(), |isai| isai.hash()), + )?; + + Ok((bank, timings)) +} + +/// Check to make sure the deserialized bank's slot and hash matches the snapshot archive's slot +/// and hash +fn check_bank_with_snapshot_archive_info( + bank: &Bank, + snapshot_archive_info_slot: Slot, + snapshot_archive_info_hash: Hash, +) -> Result<()> { + let bank_slot = bank.slot(); + let bank_hash = bank.get_accounts_hash(); + (bank_slot == snapshot_archive_info_slot && bank_hash == snapshot_archive_info_hash) + .then(|| ()) + .ok_or(SnapshotError::MismatchedSlotHash( + (bank_slot, bank_hash), + (snapshot_archive_info_slot, snapshot_archive_info_hash), + )) +} + /// Perform the common tasks when unarchiving a snapshot. Handles creating the temporary /// directories, untaring, reading the version file, and then returning those fields plus the /// unpacked append vec map. @@ -1147,31 +1244,26 @@ where }) } -/// Check if an incremental snapshot is compatible with a full snapshot. This function parses the -/// paths to see if the incremental snapshot's base slot is the same as the full snapshot's slot. -/// Return an error if they are incompatible (or if the paths cannot be parsed), otherwise return a -/// tuple of the full snapshot slot and the incremental snapshot slot. -fn check_are_snapshots_compatible

( - full_snapshot_archive_path: P, - incremental_snapshot_archive_path: P, -) -> Result<()> -where - P: AsRef, -{ - let full_snapshot_filename = path_to_file_name_str(full_snapshot_archive_path.as_ref())?; - let (full_snapshot_slot, _, _) = parse_full_snapshot_archive_filename(full_snapshot_filename)?; +/// Check if an incremental snapshot is compatible with a full snapshot. This is done by checking +/// if the incremental snapshot's base slot is the same as the full snapshot's slot. +fn check_are_snapshots_compatible( + full_snapshot_archive_info: &FullSnapshotArchiveInfo, + incremental_snapshot_archive_info: Option<&IncrementalSnapshotArchiveInfo>, +) -> Result<()> { + if incremental_snapshot_archive_info.is_none() { + return Ok(()); + } - let incremental_snapshot_filename = - path_to_file_name_str(incremental_snapshot_archive_path.as_ref())?; - let (incremental_snapshot_base_slot, _, _, _) = - parse_incremental_snapshot_archive_filename(incremental_snapshot_filename)?; + let incremental_snapshot_archive_info = incremental_snapshot_archive_info.unwrap(); - (full_snapshot_slot == incremental_snapshot_base_slot) + (full_snapshot_archive_info.slot() == incremental_snapshot_archive_info.base_slot()) .then(|| ()) - .ok_or(SnapshotError::MismatchedBaseSlot( - full_snapshot_slot, - incremental_snapshot_base_slot, - )) + .ok_or_else(|| { + SnapshotError::MismatchedBaseSlot( + *full_snapshot_archive_info.slot(), + *incremental_snapshot_archive_info.base_slot(), + ) + }) } /// Get the `&str` from a `&Path` @@ -1757,15 +1849,15 @@ pub fn snapshot_bank( /// /// Requires: /// - `bank` is complete -pub fn bank_to_full_snapshot_archive, Q: AsRef>( - snapshots_dir: P, +pub fn bank_to_full_snapshot_archive( + snapshots_dir: impl AsRef, bank: &Bank, snapshot_version: Option, - snapshot_package_output_path: Q, + snapshot_package_output_path: impl AsRef, archive_format: ArchiveFormat, thread_pool: Option<&ThreadPool>, maximum_snapshots_to_retain: usize, -) -> Result { +) -> Result { let snapshot_version = snapshot_version.unwrap_or_default(); assert!(bank.is_complete()); @@ -1798,16 +1890,16 @@ pub fn bank_to_full_snapshot_archive, Q: AsRef>( /// Requires: /// - `bank` is complete /// - `bank`'s slot is greater than `full_snapshot_slot` -pub fn bank_to_incremental_snapshot_archive, Q: AsRef>( - snapshots_dir: P, +pub fn bank_to_incremental_snapshot_archive( + snapshots_dir: impl AsRef, bank: &Bank, full_snapshot_slot: Slot, snapshot_version: Option, - snapshot_package_output_path: Q, + snapshot_package_output_path: impl AsRef, archive_format: ArchiveFormat, thread_pool: Option<&ThreadPool>, maximum_snapshots_to_retain: usize, -) -> Result { +) -> Result { let snapshot_version = snapshot_version.unwrap_or_default(); assert!(bank.is_complete()); @@ -1847,7 +1939,7 @@ pub fn package_process_and_archive_full_snapshot( snapshot_version: SnapshotVersion, thread_pool: Option<&ThreadPool>, maximum_snapshots_to_retain: usize, -) -> Result { +) -> Result { let package = package_full_snapshot( bank, bank_snapshot_info, @@ -1860,12 +1952,19 @@ pub fn package_process_and_archive_full_snapshot( None, )?; - process_and_archive_snapshot_package_pre( + let package = process_and_archive_snapshot_package_pre( package, thread_pool, None, maximum_snapshots_to_retain, - ) + )?; + + Ok(FullSnapshotArchiveInfo::new( + package.tar_output_file, + package.slot, + package.hash, + package.archive_format, + )) } /// Helper function to hold shared code to package, process, and archive incremental snapshots @@ -1881,7 +1980,7 @@ pub fn package_process_and_archive_incremental_snapshot( snapshot_version: SnapshotVersion, thread_pool: Option<&ThreadPool>, maximum_snapshots_to_retain: usize, -) -> Result { +) -> Result { let package = package_incremental_snapshot( bank, incremental_snapshot_base_slot, @@ -1895,12 +1994,20 @@ pub fn package_process_and_archive_incremental_snapshot( None, )?; - process_and_archive_snapshot_package_pre( + let package = process_and_archive_snapshot_package_pre( package, thread_pool, Some(incremental_snapshot_base_slot), maximum_snapshots_to_retain, - ) + )?; + + Ok(IncrementalSnapshotArchiveInfo::new( + package.tar_output_file, + incremental_snapshot_base_slot, + package.slot, + package.hash, + package.archive_format, + )) } /// Helper function to hold shared code to process and archive snapshot packages @@ -1909,13 +2016,13 @@ fn process_and_archive_snapshot_package_pre( thread_pool: Option<&ThreadPool>, incremental_snapshot_base_slot: Option, maximum_snapshots_to_retain: usize, -) -> Result { +) -> Result { let package = process_accounts_package_pre(package_pre, thread_pool, incremental_snapshot_base_slot); archive_snapshot_package(&package, maximum_snapshots_to_retain)?; - Ok(package.tar_output_file) + Ok(package) } pub fn process_accounts_package_pre( @@ -2254,25 +2361,40 @@ mod tests { let slot2: Slot = 5678; let slot3: Slot = 999_999; - assert!(check_are_snapshots_compatible( - &format!("/dir/snapshot-{}-{}.tar", slot1, Hash::new_unique()), - &format!( + let full_snapshot_archive_info = FullSnapshotArchiveInfo::new_from_path(PathBuf::from( + format!("/dir/snapshot-{}-{}.tar", slot1, Hash::new_unique()), + )) + .unwrap(); + + assert!(check_are_snapshots_compatible(&full_snapshot_archive_info, None,).is_ok()); + + let incremental_snapshot_archive_info = + IncrementalSnapshotArchiveInfo::new_from_path(PathBuf::from(format!( "/dir/incremental-snapshot-{}-{}-{}.tar", slot1, slot2, Hash::new_unique() - ), + ))) + .unwrap(); + + assert!(check_are_snapshots_compatible( + &full_snapshot_archive_info, + Some(&incremental_snapshot_archive_info) ) .is_ok()); - assert!(check_are_snapshots_compatible( - &format!("/dir/snapshot-{}-{}.tar", slot1, Hash::new_unique()), - &format!( + let incremental_snapshot_archive_info = + IncrementalSnapshotArchiveInfo::new_from_path(PathBuf::from(format!( "/dir/incremental-snapshot-{}-{}-{}.tar", slot2, slot3, Hash::new_unique() - ), + ))) + .unwrap(); + + assert!(check_are_snapshots_compatible( + &full_snapshot_archive_info, + Some(&incremental_snapshot_archive_info) ) .is_err()); } @@ -2583,8 +2705,8 @@ mod tests { let snapshot_archives_dir = tempfile::TempDir::new().unwrap(); let snapshot_archive_format = ArchiveFormat::Tar; - let snapshot_archive_path = bank_to_full_snapshot_archive( - snapshots_dir.path(), + let snapshot_archive_info = bank_to_full_snapshot_archive( + &snapshots_dir, &original_bank, None, snapshot_archives_dir.path(), @@ -2598,9 +2720,8 @@ mod tests { &[PathBuf::from(accounts_dir.path())], &[], snapshots_dir.path(), - &snapshot_archive_path, + &snapshot_archive_info, None, - snapshot_archive_format, &genesis_config, None, None, @@ -2673,7 +2794,7 @@ mod tests { let snapshot_archives_dir = tempfile::TempDir::new().unwrap(); let snapshot_archive_format = ArchiveFormat::Tar; - let full_snapshot_archive_path = bank_to_full_snapshot_archive( + let full_snapshot_archive_info = bank_to_full_snapshot_archive( snapshots_dir.path(), &bank4, None, @@ -2688,9 +2809,8 @@ mod tests { &[PathBuf::from(accounts_dir.path())], &[], snapshots_dir.path(), - &full_snapshot_archive_path, + &full_snapshot_archive_info, None, - snapshot_archive_format, &genesis_config, None, None, @@ -2749,7 +2869,7 @@ mod tests { let snapshot_archive_format = ArchiveFormat::Tar; let full_snapshot_slot = slot; - let full_snapshot_archive_path = bank_to_full_snapshot_archive( + let full_snapshot_archive_info = bank_to_full_snapshot_archive( snapshots_dir.path(), &bank1, None, @@ -2781,7 +2901,7 @@ mod tests { bank4.register_tick(&Hash::new_unique()); } - let incremental_snapshot_archive_path = bank_to_incremental_snapshot_archive( + let incremental_snapshot_archive_info = bank_to_incremental_snapshot_archive( snapshots_dir.path(), &bank4, full_snapshot_slot, @@ -2797,9 +2917,8 @@ mod tests { &[PathBuf::from(accounts_dir.path())], &[], snapshots_dir.path(), - &full_snapshot_archive_path, - Some(&incremental_snapshot_archive_path), - snapshot_archive_format, + &full_snapshot_archive_info, + Some(&incremental_snapshot_archive_info), &genesis_config, None, None, @@ -2814,4 +2933,101 @@ mod tests { assert_eq!(*bank4, roundtrip_bank); } + + /// Test rebuilding bank from the latest snapshot archives + #[test] + fn test_bank_from_latest_snapshot_archives() { + solana_logger::setup(); + let collector = Pubkey::new_unique(); + let key1 = Keypair::new(); + let key2 = Keypair::new(); + let key3 = Keypair::new(); + + let (genesis_config, mint_keypair) = create_genesis_config(1_000_000); + let bank0 = Arc::new(Bank::new(&genesis_config)); + bank0.transfer(1, &mint_keypair, &key1.pubkey()).unwrap(); + bank0.transfer(2, &mint_keypair, &key2.pubkey()).unwrap(); + bank0.transfer(3, &mint_keypair, &key3.pubkey()).unwrap(); + while !bank0.is_complete() { + bank0.register_tick(&Hash::new_unique()); + } + + let slot = 1; + let bank1 = Arc::new(Bank::new_from_parent(&bank0, &collector, slot)); + bank1.transfer(1, &mint_keypair, &key1.pubkey()).unwrap(); + bank1.transfer(2, &mint_keypair, &key2.pubkey()).unwrap(); + bank1.transfer(3, &mint_keypair, &key3.pubkey()).unwrap(); + while !bank1.is_complete() { + bank1.register_tick(&Hash::new_unique()); + } + + let accounts_dir = tempfile::TempDir::new().unwrap(); + let snapshots_dir = tempfile::TempDir::new().unwrap(); + let snapshot_archives_dir = tempfile::TempDir::new().unwrap(); + let snapshot_archive_format = ArchiveFormat::Tar; + + let full_snapshot_slot = slot; + bank_to_full_snapshot_archive( + &snapshots_dir, + &bank1, + None, + &snapshot_archives_dir, + snapshot_archive_format, + None, + std::usize::MAX, + ) + .unwrap(); + + let slot = slot + 1; + let bank2 = Arc::new(Bank::new_from_parent(&bank1, &collector, slot)); + bank2.transfer(1, &mint_keypair, &key1.pubkey()).unwrap(); + while !bank2.is_complete() { + bank2.register_tick(&Hash::new_unique()); + } + + let slot = slot + 1; + let bank3 = Arc::new(Bank::new_from_parent(&bank2, &collector, slot)); + bank3.transfer(2, &mint_keypair, &key2.pubkey()).unwrap(); + while !bank3.is_complete() { + bank3.register_tick(&Hash::new_unique()); + } + + let slot = slot + 1; + let bank4 = Arc::new(Bank::new_from_parent(&bank3, &collector, slot)); + bank4.transfer(3, &mint_keypair, &key3.pubkey()).unwrap(); + while !bank4.is_complete() { + bank4.register_tick(&Hash::new_unique()); + } + + bank_to_incremental_snapshot_archive( + &snapshots_dir, + &bank4, + full_snapshot_slot, + None, + &snapshot_archives_dir, + snapshot_archive_format, + None, + std::usize::MAX, + ) + .unwrap(); + + let (deserialized_bank, _) = bank_from_latest_snapshot_archives( + &snapshots_dir, + &snapshot_archives_dir, + &[accounts_dir.as_ref().to_path_buf()], + &[], + &genesis_config, + None, + None, + AccountSecondaryIndexes::default(), + false, + None, + AccountShrinkThreshold::default(), + false, + false, + ) + .unwrap(); + + assert_eq!(deserialized_bank, *bank4); + } }