From be4ac162e846480482a75f6d4aed09b46c878946 Mon Sep 17 00:00:00 2001 From: steviez Date: Thu, 19 Dec 2024 13:48:32 -0600 Subject: [PATCH] validator: Split PoH speed measurement from check Checking the PoH speed requires a Bank to derive the cluster hash rate. By the time a Bank is available, many services have started up and are competing for CPU time. So, split the PoH speed check. Measure the speed very early on in Validator::new(), and check the value later once a Bank is available --- core/src/validator.rs | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/core/src/validator.rs b/core/src/validator.rs index c3318ee070f2bc..76cb93d5e7761f 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -149,6 +149,12 @@ use { tokio::runtime::Runtime as TokioRuntime, }; +// The current hash rate on Solana MNB, Testnet, and Devnet +// If the hash rate on these clusters changes, we might consider updating this +// constant. However, the PoH speed check compares hashes / second so this +// constant does not have to be updated +const POH_SPEED_CHECK_NUM_HASHES: u64 = 10_000_000; + const MAX_COMPLETED_DATA_SETS_IN_CHANNEL: usize = 100_000; const WAIT_FOR_SUPERMAJORITY_THRESHOLD_PERCENT: u64 = 80; // Right now since we reuse the wait for supermajority code, the @@ -555,6 +561,12 @@ impl Validator { let start_time = Instant::now(); + let my_poh_hashes_per_second = if config.no_poh_speed_test { + None + } else { + Some(measure_poh_speed(POH_SPEED_CHECK_NUM_HASHES)) + }; + // Initialize the global rayon pool first to ensure the value in config // is honored. Otherwise, some code accessing the global pool could // cause it to get initialized with Rayon's default (not ours) @@ -768,8 +780,11 @@ impl Validator { ) .map_err(ValidatorError::Other)?; - if !config.no_poh_speed_test { - check_poh_speed(&bank_forks.read().unwrap().root_bank(), None)?; + if let Some(my_hashes_per_second) = my_poh_hashes_per_second { + check_poh_speed( + &bank_forks.read().unwrap().root_bank(), + my_hashes_per_second, + )?; } let (root_slot, hard_forks) = { @@ -1775,19 +1790,25 @@ fn active_vote_account_exists_in_bank(bank: &Bank, vote_account: &Pubkey) -> boo false } -fn check_poh_speed(bank: &Bank, maybe_hash_samples: Option) -> Result<(), ValidatorError> { +// Compute the PoH speed (hashes / second) by measuring the time required to +// compute `num_hashes` hashes +fn measure_poh_speed(num_hashes: u64) -> u64 { + let hash_time = compute_hash_time(num_hashes); + (num_hashes as f64 / hash_time.as_secs_f64()) as u64 +} + +// Compare a computed PoH speed against the target value derived from a Bank +// and error if the computed PoH speed is less than the target speed +// +// Measurement and comparison are split so that the measurement can occur early +// in validator startup before other services start competing for CPU time +fn check_poh_speed(bank: &Bank, my_hashes_per_second: u64) -> Result<(), ValidatorError> { let Some(hashes_per_tick) = bank.hashes_per_tick() else { warn!("Unable to read hashes per tick from Bank, skipping PoH speed check"); return Ok(()); }; - let ticks_per_slot = bank.ticks_per_slot(); let hashes_per_slot = hashes_per_tick * ticks_per_slot; - let hash_samples = maybe_hash_samples.unwrap_or(hashes_per_slot); - - let hash_time = compute_hash_time(hash_samples); - let my_hashes_per_second = (hash_samples as f64 / hash_time.as_secs_f64()) as u64; - let target_slot_duration = Duration::from_nanos(bank.ns_per_slot as u64); let target_hashes_per_second = (hashes_per_slot as f64 / target_slot_duration.as_secs_f64()) as u64; @@ -3254,7 +3275,7 @@ mod tests { ..GenesisConfig::default() }; let bank = Bank::new_for_tests(&genesis_config); - assert!(check_poh_speed(&bank, Some(10_000)).is_err()); + assert!(check_poh_speed(&bank, measure_poh_speed(10_000)).is_err()); } #[test] @@ -3270,6 +3291,6 @@ mod tests { ..GenesisConfig::default() }; let bank = Bank::new_for_tests(&genesis_config); - check_poh_speed(&bank, Some(10_000)).unwrap(); + check_poh_speed(&bank, measure_poh_speed(10_000)).unwrap(); } }