diff --git a/core/src/accounts_hash_verifier.rs b/core/src/accounts_hash_verifier.rs index 9817d0713..c3cf4368a 100644 --- a/core/src/accounts_hash_verifier.rs +++ b/core/src/accounts_hash_verifier.rs @@ -6,8 +6,8 @@ use crate::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES}; use solana_ledger::{ - snapshot_package::SnapshotPackage, snapshot_package::SnapshotPackageReceiver, - snapshot_package::SnapshotPackageSender, + snapshot_package::AccountsPackage, snapshot_package::AccountsPackageReceiver, + snapshot_package::AccountsPackageSender, }; use solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey}; use std::collections::{HashMap, HashSet}; @@ -27,13 +27,14 @@ pub struct AccountsHashVerifier { impl AccountsHashVerifier { pub fn new( - snapshot_package_receiver: SnapshotPackageReceiver, - snapshot_package_sender: Option, + accounts_package_receiver: AccountsPackageReceiver, + accounts_package_sender: Option, exit: &Arc, cluster_info: &Arc>, trusted_validators: Option>, halt_on_trusted_validators_accounts_hash_mismatch: bool, fault_injection_rate_slots: u64, + snapshot_interval_slots: u64, ) -> Self { let exit = exit.clone(); let cluster_info = cluster_info.clone(); @@ -46,17 +47,18 @@ impl AccountsHashVerifier { break; } - match snapshot_package_receiver.recv_timeout(Duration::from_secs(1)) { - Ok(snapshot_package) => { - Self::process_snapshot( - snapshot_package, + match accounts_package_receiver.recv_timeout(Duration::from_secs(1)) { + Ok(accounts_package) => { + Self::process_accounts_package( + accounts_package, &cluster_info, &trusted_validators, halt_on_trusted_validators_accounts_hash_mismatch, - &snapshot_package_sender, + &accounts_package_sender, &mut hashes, &exit, fault_injection_rate_slots, + snapshot_interval_slots, ); } Err(RecvTimeoutError::Disconnected) => break, @@ -70,28 +72,29 @@ impl AccountsHashVerifier { } } - fn process_snapshot( - snapshot_package: SnapshotPackage, + fn process_accounts_package( + accounts_package: AccountsPackage, cluster_info: &Arc>, trusted_validators: &Option>, halt_on_trusted_validator_accounts_hash_mismatch: bool, - snapshot_package_sender: &Option, + accounts_package_sender: &Option, hashes: &mut Vec<(Slot, Hash)>, exit: &Arc, fault_injection_rate_slots: u64, + snapshot_interval_slots: u64, ) { if fault_injection_rate_slots != 0 - && snapshot_package.root % fault_injection_rate_slots == 0 + && accounts_package.root % fault_injection_rate_slots == 0 { // For testing, publish an invalid hash to gossip. use rand::{thread_rng, Rng}; use solana_sdk::hash::extend_and_hash; - warn!("inserting fault at slot: {}", snapshot_package.root); + warn!("inserting fault at slot: {}", accounts_package.root); let rand = thread_rng().gen_range(0, 10); - let hash = extend_and_hash(&snapshot_package.hash, &[rand]); - hashes.push((snapshot_package.root, hash)); + let hash = extend_and_hash(&accounts_package.hash, &[rand]); + hashes.push((accounts_package.root, hash)); } else { - hashes.push((snapshot_package.root, snapshot_package.hash)); + hashes.push((accounts_package.root, accounts_package.hash)); } while hashes.len() > MAX_SNAPSHOT_HASHES { @@ -107,8 +110,11 @@ impl AccountsHashVerifier { exit.store(true, Ordering::Relaxed); } } - if let Some(sender) = snapshot_package_sender.as_ref() { - if sender.send(snapshot_package).is_err() {} + + if accounts_package.block_height % snapshot_interval_slots == 0 { + if let Some(sender) = accounts_package_sender.as_ref() { + if sender.send(accounts_package).is_err() {} + } } cluster_info @@ -225,8 +231,9 @@ mod tests { let mut hashes = vec![]; for i in 0..MAX_SNAPSHOT_HASHES + 1 { let snapshot_links = TempDir::new().unwrap(); - let snapshot_package = SnapshotPackage { + let accounts_package = AccountsPackage { hash: hash(&[i as u8]), + block_height: 100 + i as u64, root: 100 + i as u64, slot_deltas: vec![], snapshot_links, @@ -235,8 +242,8 @@ mod tests { compression: CompressionType::Bzip2, }; - AccountsHashVerifier::process_snapshot( - snapshot_package, + AccountsHashVerifier::process_accounts_package( + accounts_package, &cluster_info, &Some(trusted_validators.clone()), false, @@ -244,6 +251,7 @@ mod tests { &mut hashes, &exit, 0, + 100, ); } let cluster_info_r = cluster_info.read().unwrap(); diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index a184f4f77..0d5032010 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -20,7 +20,7 @@ use solana_ledger::{ blockstore_processor::{self, BlockstoreProcessorError, TransactionStatusSender}, entry::VerifyRecyclers, leader_schedule_cache::LeaderScheduleCache, - snapshot_package::SnapshotPackageSender, + snapshot_package::AccountsPackageSender, }; use solana_measure::thread_mem_usage; use solana_metrics::inc_new_counter_info; @@ -97,7 +97,7 @@ pub struct ReplayStageConfig { pub subscriptions: Arc, pub leader_schedule_cache: Arc, pub latest_root_senders: Vec>, - pub accounts_hash_sender: Option, + pub accounts_hash_sender: Option, pub block_commitment_cache: Arc>, pub transaction_status_sender: Option, pub rewards_recorder_sender: Option, @@ -690,7 +690,7 @@ impl ReplayStage { leader_schedule_cache: &Arc, root_bank_sender: &Sender>>, lockouts_sender: &Sender, - accounts_hash_sender: &Option, + accounts_hash_sender: &Option, latest_root_senders: &[Sender], all_pubkeys: &mut HashSet>, subscriptions: &Arc, @@ -1485,7 +1485,7 @@ impl ReplayStage { new_root: u64, bank_forks: &RwLock, progress: &mut ProgressMap, - accounts_hash_sender: &Option, + accounts_hash_sender: &Option, all_pubkeys: &mut HashSet>, ) { let old_epoch = bank_forks.read().unwrap().root_bank().epoch(); diff --git a/core/src/snapshot_packager_service.rs b/core/src/snapshot_packager_service.rs index 11e6b715c..57875d98e 100644 --- a/core/src/snapshot_packager_service.rs +++ b/core/src/snapshot_packager_service.rs @@ -1,5 +1,5 @@ use crate::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES}; -use solana_ledger::{snapshot_package::SnapshotPackageReceiver, snapshot_utils}; +use solana_ledger::{snapshot_package::AccountsPackageReceiver, snapshot_utils}; use solana_sdk::{clock::Slot, hash::Hash}; use std::{ sync::{ @@ -17,7 +17,7 @@ pub struct SnapshotPackagerService { impl SnapshotPackagerService { pub fn new( - snapshot_package_receiver: SnapshotPackageReceiver, + snapshot_package_receiver: AccountsPackageReceiver, starting_snapshot_hash: Option<(Slot, Hash)>, exit: &Arc, cluster_info: &Arc>, @@ -86,7 +86,7 @@ mod tests { use bincode::serialize_into; use solana_ledger::bank_forks::CompressionType; use solana_ledger::{ - snapshot_package::SnapshotPackage, + snapshot_package::AccountsPackage, snapshot_utils::{self, SNAPSHOT_STATUS_CACHE_FILE_NAME}, }; use solana_runtime::{ @@ -172,7 +172,8 @@ mod tests { &(42, Hash::default()), &CompressionType::Bzip2, ); - let snapshot_package = SnapshotPackage::new( + let snapshot_package = AccountsPackage::new( + 5, 5, vec![], link_snapshots_dir, diff --git a/core/src/tvu.rs b/core/src/tvu.rs index d3b89483a..424f25415 100644 --- a/core/src/tvu.rs +++ b/core/src/tvu.rs @@ -26,7 +26,7 @@ use solana_ledger::{ bank_forks::BankForks, blockstore::{Blockstore, CompletedSlotsReceiver}, blockstore_processor::TransactionStatusSender, - snapshot_package::SnapshotPackageSender, + snapshot_package::AccountsPackageSender, }; use solana_sdk::{ pubkey::Pubkey, @@ -98,7 +98,7 @@ impl Tvu { cfg: Option>, transaction_status_sender: Option, rewards_recorder_sender: Option, - snapshot_package_sender: Option, + snapshot_package_sender: Option, vote_tracker: Arc, retransmit_slots_sender: RetransmitSlotsSender, tvu_config: TvuConfig, @@ -165,6 +165,14 @@ impl Tvu { let (ledger_cleanup_slot_sender, ledger_cleanup_slot_receiver) = channel(); + let snapshot_interval_slots = { + if let Some(config) = bank_forks.read().unwrap().snapshot_config() { + config.snapshot_interval_slots + } else { + std::u64::MAX + } + }; + info!("snapshot_interval_slots: {}", snapshot_interval_slots); let (accounts_hash_sender, accounts_hash_receiver) = channel(); let accounts_hash_verifier = AccountsHashVerifier::new( accounts_hash_receiver, @@ -174,6 +182,7 @@ impl Tvu { tvu_config.trusted_validators.clone(), tvu_config.halt_on_trusted_validators_accounts_hash_mismatch, tvu_config.accounts_hash_fault_injection_slots, + snapshot_interval_slots, ); let replay_stage_config = ReplayStageConfig { diff --git a/core/src/validator.rs b/core/src/validator.rs index f07fb5fed..9a4bc6798 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -81,6 +81,7 @@ pub struct ValidatorConfig { pub accounts_hash_fault_injection_slots: u64, // 0 = no fault injection pub frozen_accounts: Vec, pub no_rocksdb_compaction: bool, + pub accounts_hash_interval_slots: u64, } impl Default for ValidatorConfig { @@ -107,6 +108,7 @@ impl Default for ValidatorConfig { accounts_hash_fault_injection_slots: 0, frozen_accounts: vec![], no_rocksdb_compaction: false, + accounts_hash_interval_slots: std::u64::MAX, } } } @@ -622,6 +624,7 @@ fn new_banks_from_blockstore( leader_schedule_cache.set_fixed_leader_schedule(config.fixed_leader_schedule.clone()); bank_forks.set_snapshot_config(config.snapshot_config.clone()); + bank_forks.set_accounts_hash_interval_slots(config.accounts_hash_interval_slots); ( genesis_config, diff --git a/core/tests/bank_forks.rs b/core/tests/bank_forks.rs index 24c286354..40bf9d420 100644 --- a/core/tests/bank_forks.rs +++ b/core/tests/bank_forks.rs @@ -38,7 +38,7 @@ mod tests { genesis_config_info: GenesisConfigInfo, } - fn setup_snapshot_test(snapshot_interval_slots: usize) -> SnapshotTestConfig { + fn setup_snapshot_test(snapshot_interval_slots: u64) -> SnapshotTestConfig { let accounts_dir = TempDir::new().unwrap(); let snapshot_dir = TempDir::new().unwrap(); let snapshot_output_path = TempDir::new().unwrap(); @@ -50,6 +50,7 @@ mod tests { ); bank0.freeze(); let mut bank_forks = BankForks::new(0, bank0); + bank_forks.accounts_hash_interval_slots = snapshot_interval_slots; let snapshot_config = SnapshotConfig { snapshot_interval_slots, @@ -265,7 +266,7 @@ mod tests { }; bank_forks - .generate_snapshot(slot, &vec![], &package_sender) + .generate_accounts_package(slot, &vec![], &package_sender) .unwrap(); if slot == saved_slot as u64 { @@ -371,7 +372,7 @@ mod tests { let (snapshot_sender, _snapshot_receiver) = channel(); // Make sure this test never clears bank.slots_since_snapshot let mut snapshot_test_config = - setup_snapshot_test(add_root_interval * num_set_roots * 2); + setup_snapshot_test((add_root_interval * num_set_roots * 2) as u64); let mut current_bank = snapshot_test_config.bank_forks[0].clone(); let snapshot_sender = Some(snapshot_sender); for _ in 0..num_set_roots { diff --git a/ledger/src/bank_forks.rs b/ledger/src/bank_forks.rs index 4a221d781..a0c85973b 100644 --- a/ledger/src/bank_forks.rs +++ b/ledger/src/bank_forks.rs @@ -1,6 +1,6 @@ //! The `bank_forks` module implments BankForks a DAG of checkpointed Banks -use crate::snapshot_package::{SnapshotPackageSendError, SnapshotPackageSender}; +use crate::snapshot_package::{AccountsPackageSendError, AccountsPackageSender}; use crate::snapshot_utils::{self, SnapshotError}; use log::*; use solana_measure::measure::Measure; @@ -27,7 +27,7 @@ pub enum CompressionType { #[derive(Clone, Debug, Eq, PartialEq)] pub struct SnapshotConfig { // Generate a new snapshot every this many slots - pub snapshot_interval_slots: usize, + pub snapshot_interval_slots: u64, // Where to store the latest packaged snapshot pub snapshot_package_output_path: PathBuf, @@ -43,8 +43,8 @@ pub enum BankForksError { #[error("snapshot error")] SnapshotError(#[from] SnapshotError), - #[error("snapshot package send error")] - SnapshotPackageSendError(#[from] SnapshotPackageSendError), + #[error("accounts package send error")] + AccountsPackageSendError(#[from] AccountsPackageSendError), } type Result = std::result::Result; @@ -54,6 +54,9 @@ pub struct BankForks { root: Slot, pub snapshot_config: Option, last_snapshot_slot: Slot, + + pub accounts_hash_interval_slots: Slot, + last_accounts_hash_slot: Slot, } impl Index for BankForks { @@ -74,6 +77,8 @@ impl BankForks { root: 0, snapshot_config: None, last_snapshot_slot: bank_slot, + accounts_hash_interval_slots: std::u64::MAX, + last_accounts_hash_slot: bank_slot, } } @@ -159,6 +164,8 @@ impl BankForks { working_bank, snapshot_config: None, last_snapshot_slot: root, + accounts_hash_interval_slots: std::u64::MAX, + last_accounts_hash_slot: root, } } @@ -178,7 +185,7 @@ impl BankForks { pub fn set_root( &mut self, root: Slot, - snapshot_package_sender: &Option, + accounts_package_sender: &Option, ) { let old_epoch = self.root_bank().epoch(); self.root = root; @@ -209,43 +216,46 @@ impl BankForks { .last() .map(|bank| bank.transaction_count()) .unwrap_or(0); - // Generate each snapshot at a fixed interval + // Calculate the accounts hash at a fixed interval let mut is_root_bank_squashed = false; - if self.snapshot_config.is_some() && snapshot_package_sender.is_some() { - let config = self.snapshot_config.as_ref().unwrap(); - let mut banks = vec![root_bank]; - let parents = root_bank.parents(); - banks.extend(parents.iter()); - for bank in banks.iter() { - let bank_slot = bank.slot(); - if bank.block_height() % (config.snapshot_interval_slots as u64) == 0 { - // Generate a snapshot if snapshots are configured and it's been an appropriate number - // of banks since the last snapshot - if bank_slot > self.last_snapshot_slot { - bank.squash(); - is_root_bank_squashed = bank_slot == root; - let mut snapshot_time = Measure::start("total-snapshot-ms"); - let r = self.generate_snapshot( - bank_slot, - &bank.src.roots(), - snapshot_package_sender.as_ref().unwrap(), - ); - if r.is_err() { - warn!( - "Error generating snapshot for bank: {}, err: {:?}", - bank_slot, r - ); - } else { - self.last_snapshot_slot = bank_slot; - } + let mut banks = vec![root_bank]; + let parents = root_bank.parents(); + banks.extend(parents.iter()); + for bank in banks.iter() { + let bank_slot = bank.slot(); + if bank.block_height() % self.accounts_hash_interval_slots == 0 + && bank_slot > self.last_accounts_hash_slot + { + self.last_accounts_hash_slot = bank_slot; + bank.squash(); + is_root_bank_squashed = bank_slot == root; - // Cleanup outdated snapshots - self.purge_old_snapshots(); - snapshot_time.stop(); - inc_new_counter_info!("total-snapshot-ms", snapshot_time.as_ms() as usize); + bank.clean_accounts(); + bank.update_accounts_hash(); + + if self.snapshot_config.is_some() && accounts_package_sender.is_some() { + // Generate an accounts package + let mut snapshot_time = Measure::start("total-snapshot-ms"); + let r = self.generate_accounts_package( + bank_slot, + &bank.src.roots(), + accounts_package_sender.as_ref().unwrap(), + ); + if r.is_err() { + warn!( + "Error generating snapshot for bank: {}, err: {:?}", + bank_slot, r + ); + } else { + self.last_snapshot_slot = bank_slot; } - break; + + // Cleanup outdated snapshots + self.purge_old_snapshots(); + snapshot_time.stop(); + inc_new_counter_info!("total-snapshot-ms", snapshot_time.as_ms() as usize); } + break; } } if !is_root_bank_squashed { @@ -282,11 +292,11 @@ impl BankForks { } } - pub fn generate_snapshot( + pub fn generate_accounts_package( &self, root: Slot, slots_to_snapshot: &[Slot], - snapshot_package_sender: &SnapshotPackageSender, + accounts_package_sender: &AccountsPackageSender, ) -> Result<()> { let config = self.snapshot_config.as_ref().unwrap(); @@ -319,8 +329,7 @@ impl BankForks { config.compression.clone(), )?; - // Send the package to the packaging thread - snapshot_package_sender.send(package)?; + accounts_package_sender.send(package)?; Ok(()) } @@ -338,6 +347,10 @@ impl BankForks { pub fn snapshot_config(&self) -> &Option { &self.snapshot_config } + + pub fn set_accounts_hash_interval_slots(&mut self, accounts_interval_slots: u64) { + self.accounts_hash_interval_slots = accounts_interval_slots; + } } #[cfg(test)] diff --git a/ledger/src/snapshot_package.rs b/ledger/src/snapshot_package.rs index 32f12e34d..a426ff2e9 100644 --- a/ledger/src/snapshot_package.rs +++ b/ledger/src/snapshot_package.rs @@ -8,13 +8,14 @@ use std::{ }; use tempfile::TempDir; -pub type SnapshotPackageSender = Sender; -pub type SnapshotPackageReceiver = Receiver; -pub type SnapshotPackageSendError = SendError; +pub type AccountsPackageSender = Sender; +pub type AccountsPackageReceiver = Receiver; +pub type AccountsPackageSendError = SendError; #[derive(Debug)] -pub struct SnapshotPackage { +pub struct AccountsPackage { pub root: Slot, + pub block_height: Slot, pub slot_deltas: Vec, pub snapshot_links: TempDir, pub storages: SnapshotStorages, @@ -23,9 +24,10 @@ pub struct SnapshotPackage { pub compression: CompressionType, } -impl SnapshotPackage { +impl AccountsPackage { pub fn new( root: Slot, + block_height: u64, slot_deltas: Vec, snapshot_links: TempDir, storages: SnapshotStorages, @@ -35,6 +37,7 @@ impl SnapshotPackage { ) -> Self { Self { root, + block_height, slot_deltas, snapshot_links, storages, diff --git a/ledger/src/snapshot_utils.rs b/ledger/src/snapshot_utils.rs index 8adb93aeb..ba930e5bd 100644 --- a/ledger/src/snapshot_utils.rs +++ b/ledger/src/snapshot_utils.rs @@ -1,6 +1,6 @@ use crate::bank_forks::CompressionType; use crate::hardened_unpack::{unpack_snapshot, UnpackError}; -use crate::snapshot_package::SnapshotPackage; +use crate::snapshot_package::AccountsPackage; use bincode::serialize_into; use bzip2::bufread::BzDecoder; use flate2::read::GzDecoder; @@ -93,7 +93,7 @@ pub fn package_snapshot, Q: AsRef>( snapshot_package_output_path: P, snapshot_storages: SnapshotStorages, compression: CompressionType, -) -> Result { +) -> Result { // Hard link all the snapshots we need for this package let snapshot_hard_links_dir = tempfile::tempdir_in(snapshot_path)?; @@ -104,8 +104,8 @@ pub fn package_snapshot, Q: AsRef>( snapshot_storages.len() ); - // Any errors from this point on will cause the above SnapshotPackage to drop, clearing - // any temporary state created for the SnapshotPackage (like the snapshot_hard_links_dir) + // Any errors from this point on will cause the above AccountsPackage to drop, clearing + // any temporary state created for the AccountsPackage (like the snapshot_hard_links_dir) snapshot_files.copy_snapshot_directory(snapshot_hard_links_dir.path())?; let snapshot_package_output_file = get_snapshot_archive_path( @@ -114,8 +114,9 @@ pub fn package_snapshot, Q: AsRef>( &compression, ); - let package = SnapshotPackage::new( + let package = AccountsPackage::new( bank.slot(), + bank.block_height(), bank.src.slot_deltas(slots_to_snapshot), snapshot_hard_links_dir, snapshot_storages, @@ -136,7 +137,7 @@ fn get_compression_ext(compression: &CompressionType) -> (&'static str, &'static } } -pub fn archive_snapshot_package(snapshot_package: &SnapshotPackage) -> Result<()> { +pub fn archive_snapshot_package(snapshot_package: &AccountsPackage) -> Result<()> { info!( "Generating snapshot archive for slot {}", snapshot_package.root @@ -354,8 +355,6 @@ pub fn add_snapshot>( bank: &Bank, snapshot_storages: &[SnapshotStorage], ) -> Result { - bank.clean_accounts(); - bank.update_accounts_hash(); let slot = bank.slot(); // snapshot_path/slot let slot_snapshot_dir = get_bank_snapshot_dir(snapshot_path, slot); diff --git a/local-cluster/tests/local_cluster.rs b/local-cluster/tests/local_cluster.rs index 2433259cf..8b687de76 100644 --- a/local-cluster/tests/local_cluster.rs +++ b/local-cluster/tests/local_cluster.rs @@ -1260,7 +1260,7 @@ struct SnapshotValidatorConfig { } fn setup_snapshot_validator_config( - snapshot_interval_slots: usize, + snapshot_interval_slots: u64, num_account_paths: usize, ) -> SnapshotValidatorConfig { // Create the snapshot config @@ -1281,6 +1281,7 @@ fn setup_snapshot_validator_config( validator_config.rpc_config.enable_validator_exit = true; validator_config.snapshot_config = Some(snapshot_config); validator_config.account_paths = account_storage_paths; + validator_config.accounts_hash_interval_slots = snapshot_interval_slots; SnapshotValidatorConfig { _snapshot_dir: snapshot_dir, diff --git a/validator/src/main.rs b/validator/src/main.rs index 73d9afb6a..af2c2cf01 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -399,6 +399,15 @@ fn download_then_check_genesis_hash( Ok(genesis_config.hash()) } +fn is_snapshot_config_invalid( + snapshot_interval_slots: u64, + accounts_hash_interval_slots: u64, +) -> bool { + snapshot_interval_slots != 0 + && (snapshot_interval_slots < accounts_hash_interval_slots + || snapshot_interval_slots % accounts_hash_interval_slots != 0) +} + #[allow(clippy::cognitive_complexity)] pub fn main() { let default_dynamic_port_range = @@ -609,6 +618,14 @@ pub fn main() { .default_value("100") .help("Number of slots between generating snapshots, 0 to disable snapshots"), ) + .arg( + clap::Arg::with_name("accounts_hash_interval_slots") + .long("accounts-hash-slots") + .value_name("ACCOUNTS_HASH_INTERVAL_SLOTS") + .takes_value(true) + .default_value("100") + .help("Number of slots between generating accounts hash."), + ) .arg( clap::Arg::with_name("limit_ledger_size") .long("limit-ledger-size") @@ -850,7 +867,7 @@ pub fn main() { }) .collect(); - let snapshot_interval_slots = value_t_or_exit!(matches, "snapshot_interval_slots", usize); + let snapshot_interval_slots = value_t_or_exit!(matches, "snapshot_interval_slots", u64); let snapshot_path = ledger_path.clone().join("snapshot"); fs::create_dir_all(&snapshot_path).unwrap_or_else(|err| { eprintln!( @@ -874,13 +891,30 @@ pub fn main() { snapshot_interval_slots: if snapshot_interval_slots > 0 { snapshot_interval_slots } else { - std::usize::MAX + std::u64::MAX }, snapshot_path, snapshot_package_output_path: ledger_path.clone(), compression: snapshot_compression, }); + validator_config.accounts_hash_interval_slots = + value_t_or_exit!(matches, "accounts_hash_interval_slots", u64); + if validator_config.accounts_hash_interval_slots == 0 { + eprintln!("Accounts hash interval should not be 0."); + exit(1); + } + if is_snapshot_config_invalid( + snapshot_interval_slots, + validator_config.accounts_hash_interval_slots, + ) { + eprintln!("Invalid snapshot interval provided ({}), must be a multiple of accounts_hash_interval_slots ({})", + snapshot_interval_slots, + validator_config.accounts_hash_interval_slots, + ); + exit(1); + } + if matches.is_present("limit_ledger_size") { let limit_ledger_size = value_t_or_exit!(matches, "limit_ledger_size", u64); if limit_ledger_size < DEFAULT_MIN_MAX_LEDGER_SHREDS { @@ -1188,3 +1222,17 @@ pub fn main() { validator.join().expect("validator exit"); info!("Validator exiting.."); } + +#[cfg(test)] +pub mod tests { + use super::*; + + #[test] + fn test_interval_check() { + assert!(!is_snapshot_config_invalid(0, 100)); + assert!(is_snapshot_config_invalid(1, 100)); + assert!(is_snapshot_config_invalid(230, 100)); + assert!(!is_snapshot_config_invalid(500, 100)); + assert!(!is_snapshot_config_invalid(5, 5)); + } +}