diff --git a/core/src/cluster_tests.rs b/core/src/cluster_tests.rs index 48c720bb8f..ca41f31568 100644 --- a/core/src/cluster_tests.rs +++ b/core/src/cluster_tests.rs @@ -10,7 +10,7 @@ use crate::gossip_service::discover_nodes; use crate::locktower::VOTE_THRESHOLD_DEPTH; use crate::poh_service::PohServiceConfig; use solana_client::thin_client::create_client; -use solana_runtime::bank::MINIMUM_SLOT_LENGTH; +use solana_runtime::epoch_schedule::MINIMUM_SLOT_LENGTH; use solana_sdk::client::SyncClient; use solana_sdk::hash::Hash; use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; diff --git a/core/src/leader_schedule_cache.rs b/core/src/leader_schedule_cache.rs index c234e9e811..05bac81bff 100644 --- a/core/src/leader_schedule_cache.rs +++ b/core/src/leader_schedule_cache.rs @@ -1,7 +1,8 @@ use crate::blocktree::Blocktree; use crate::leader_schedule::LeaderSchedule; use crate::leader_schedule_utils; -use solana_runtime::bank::{Bank, EpochSchedule}; +use solana_runtime::bank::Bank; +use solana_runtime::epoch_schedule::EpochSchedule; use solana_sdk::pubkey::Pubkey; use std::collections::hash_map::Entry; use std::collections::{HashMap, VecDeque}; @@ -168,7 +169,8 @@ mod tests { use crate::genesis_utils::create_genesis_block; use crate::genesis_utils::{create_genesis_block_with_leader, BOOTSTRAP_LEADER_LAMPORTS}; use crate::voting_keypair::tests::new_vote_account; - use solana_runtime::bank::{Bank, EpochSchedule, MINIMUM_SLOT_LENGTH}; + use solana_runtime::bank::Bank; + use solana_runtime::epoch_schedule::{EpochSchedule, MINIMUM_SLOT_LENGTH}; use solana_sdk::signature::{Keypair, KeypairUtil}; use std::sync::mpsc::channel; use std::sync::Arc; diff --git a/core/src/local_cluster.rs b/core/src/local_cluster.rs index 43399b827f..955edcbe9b 100644 --- a/core/src/local_cluster.rs +++ b/core/src/local_cluster.rs @@ -451,7 +451,7 @@ impl Drop for LocalCluster { mod test { use super::*; use crate::storage_stage::STORAGE_ROTATE_TEST_COUNT; - use solana_runtime::bank::MINIMUM_SLOT_LENGTH; + use solana_runtime::epoch_schedule::MINIMUM_SLOT_LENGTH; #[test] fn test_local_cluster_start_and_exit() { diff --git a/core/src/repair_service.rs b/core/src/repair_service.rs index 61bcd50433..84af1a3ebf 100644 --- a/core/src/repair_service.rs +++ b/core/src/repair_service.rs @@ -7,7 +7,7 @@ use crate::cluster_info::ClusterInfo; use crate::result::Result; use crate::service::Service; use solana_metrics::datapoint; -use solana_runtime::bank::EpochSchedule; +use solana_runtime::epoch_schedule::EpochSchedule; use solana_sdk::pubkey::Pubkey; use std::collections::HashSet; use std::net::UdpSocket; diff --git a/core/src/retransmit_stage.rs b/core/src/retransmit_stage.rs index d5fad20635..5e26c5c94d 100644 --- a/core/src/retransmit_stage.rs +++ b/core/src/retransmit_stage.rs @@ -11,7 +11,7 @@ use crate::staking_utils; use crate::streamer::BlobReceiver; use crate::window_service::WindowService; use solana_metrics::{datapoint, inc_new_counter_info}; -use solana_runtime::bank::EpochSchedule; +use solana_runtime::epoch_schedule::EpochSchedule; use solana_sdk::hash::Hash; use std::net::UdpSocket; use std::sync::atomic::AtomicBool; diff --git a/core/src/window_service.rs b/core/src/window_service.rs index e1b446fd87..ce85aab64d 100644 --- a/core/src/window_service.rs +++ b/core/src/window_service.rs @@ -261,7 +261,7 @@ mod test { use crate::packet::{index_blobs, Blob}; use crate::service::Service; use crate::streamer::{blob_receiver, responder}; - use solana_runtime::bank::{Bank, MINIMUM_SLOT_LENGTH}; + use solana_runtime::epoch_schedule::MINIMUM_SLOT_LENGTH; use solana_sdk::hash::Hash; use std::fs::remove_dir_all; use std::net::UdpSocket; diff --git a/core/tests/local_cluster.rs b/core/tests/local_cluster.rs index 616b8fd3fb..a078dd28b3 100644 --- a/core/tests/local_cluster.rs +++ b/core/tests/local_cluster.rs @@ -6,7 +6,7 @@ use solana::fullnode::FullnodeConfig; use solana::gossip_service::discover_nodes; use solana::local_cluster::{ClusterConfig, LocalCluster}; use solana::poh_service::PohServiceConfig; -use solana_runtime::bank::MINIMUM_SLOT_LENGTH; +use solana_runtime::epoch_schedule::MINIMUM_SLOT_LENGTH; use solana_sdk::timing; use std::time::Duration; diff --git a/core/tests/tvu.rs b/core/tests/tvu.rs index 9f220ab681..1a405588df 100644 --- a/core/tests/tvu.rs +++ b/core/tests/tvu.rs @@ -17,7 +17,7 @@ use solana::storage_stage::StorageState; use solana::storage_stage::STORAGE_ROTATE_TEST_COUNT; use solana::streamer; use solana::tvu::{Sockets, Tvu}; -use solana_runtime::bank::MINIMUM_SLOT_LENGTH; +use solana_runtime::epoch_schedule::MINIMUM_SLOT_LENGTH; use solana_sdk::signature::{Keypair, KeypairUtil}; use solana_sdk::system_transaction; use std::fs::remove_dir_all; diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 22ec403e33..1d89423f3b 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -6,6 +6,7 @@ use crate::accounts::{AccountLockType, Accounts}; use crate::accounts_db::{ErrorCounters, InstructionAccounts, InstructionLoaders}; use crate::accounts_index::Fork; use crate::blockhash_queue::BlockhashQueue; +use crate::epoch_schedule::EpochSchedule; use crate::locked_accounts_results::LockedAccountsResults; use crate::message_processor::{MessageProcessor, ProcessInstruction}; use crate::status_cache::StatusCache; @@ -23,111 +24,12 @@ use solana_sdk::signature::{Keypair, Signature}; use solana_sdk::system_transaction; use solana_sdk::timing::{duration_as_ms, duration_as_us, MAX_RECENT_BLOCKHASHES}; use solana_sdk::transaction::{Result, Transaction, TransactionError}; -use solana_vote_api::vote_state::MAX_LOCKOUT_HISTORY; use std::borrow::Borrow; use std::cmp; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, RwLock}; use std::time::Instant; -pub const MINIMUM_SLOT_LENGTH: usize = MAX_LOCKOUT_HISTORY + 1; - -#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] -pub struct EpochSchedule { - /// The maximum number of slots in each epoch. - pub slots_per_epoch: u64, - - /// A number of slots before slot_index 0. Used to calculate finalized staked nodes. - pub stakers_slot_offset: u64, - - /// basically: log2(slots_per_epoch) - log2(MINIMUM_SLOT_LEN) - pub first_normal_epoch: u64, - - /// basically: 2.pow(first_normal_epoch) - MINIMUM_SLOT_LEN - pub first_normal_slot: u64, -} - -impl EpochSchedule { - pub fn new(slots_per_epoch: u64, stakers_slot_offset: u64, warmup: bool) -> Self { - assert!(slots_per_epoch >= MINIMUM_SLOT_LENGTH as u64); - let (first_normal_epoch, first_normal_slot) = if warmup { - let next_power_of_two = slots_per_epoch.next_power_of_two(); - let log2_slots_per_epoch = next_power_of_two - .trailing_zeros() - .saturating_sub(MINIMUM_SLOT_LENGTH.trailing_zeros()); - - ( - u64::from(log2_slots_per_epoch), - next_power_of_two.saturating_sub(MINIMUM_SLOT_LENGTH as u64), - ) - } else { - (0, 0) - }; - EpochSchedule { - slots_per_epoch, - stakers_slot_offset, - first_normal_epoch, - first_normal_slot, - } - } - - /// get the length of the given epoch (in slots) - pub fn get_slots_in_epoch(&self, epoch: u64) -> u64 { - if epoch < self.first_normal_epoch { - 2u64.pow(epoch as u32 + MINIMUM_SLOT_LENGTH.trailing_zeros() as u32) - } else { - self.slots_per_epoch - } - } - - /// get the epoch for which the given slot should save off - /// information about stakers - pub fn get_stakers_epoch(&self, slot: u64) -> u64 { - if slot < self.first_normal_slot { - // until we get to normal slots, behave as if stakers_slot_offset == slots_per_epoch - self.get_epoch_and_slot_index(slot).0 + 1 - } else { - self.first_normal_epoch - + (slot - self.first_normal_slot + self.stakers_slot_offset) / self.slots_per_epoch - } - } - - /// get epoch and offset into the epoch for the given slot - pub fn get_epoch_and_slot_index(&self, slot: u64) -> (u64, u64) { - if slot < self.first_normal_slot { - let epoch = (slot + MINIMUM_SLOT_LENGTH as u64 + 1) - .next_power_of_two() - .trailing_zeros() - - MINIMUM_SLOT_LENGTH.trailing_zeros() - - 1; - - let epoch_len = 2u64.pow(epoch + MINIMUM_SLOT_LENGTH.trailing_zeros()); - - ( - u64::from(epoch), - slot - (epoch_len - MINIMUM_SLOT_LENGTH as u64), - ) - } else { - ( - self.first_normal_epoch + ((slot - self.first_normal_slot) / self.slots_per_epoch), - (slot - self.first_normal_slot) % self.slots_per_epoch, - ) - } - } - - pub fn get_first_slot_in_epoch(&self, epoch: u64) -> u64 { - if epoch <= self.first_normal_epoch { - (2u64.pow(epoch as u32) - 1) * MINIMUM_SLOT_LENGTH as u64 - } else { - (epoch - self.first_normal_epoch) * self.slots_per_epoch + self.first_normal_slot - } - } - - pub fn get_last_slot_in_epoch(&self, epoch: u64) -> u64 { - self.get_first_slot_in_epoch(epoch) + self.get_slots_in_epoch(epoch) - 1 - } -} - /// cache of staking information #[derive(Default, Clone)] pub struct Stakes { @@ -1098,6 +1000,7 @@ impl Drop for Bank { #[cfg(test)] mod tests { use super::*; + use crate::epoch_schedule::MINIMUM_SLOT_LENGTH; use crate::genesis_utils::{create_genesis_block_with_leader, BOOTSTRAP_LEADER_LAMPORTS}; use solana_sdk::genesis_block::create_genesis_block; use solana_sdk::hash; diff --git a/runtime/src/epoch_schedule.rs b/runtime/src/epoch_schedule.rs new file mode 100644 index 0000000000..acd103ed6a --- /dev/null +++ b/runtime/src/epoch_schedule.rs @@ -0,0 +1,99 @@ +use solana_vote_api::vote_state::MAX_LOCKOUT_HISTORY; + +pub const MINIMUM_SLOT_LENGTH: usize = MAX_LOCKOUT_HISTORY + 1; + +#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] +pub struct EpochSchedule { + /// The maximum number of slots in each epoch. + pub slots_per_epoch: u64, + + /// A number of slots before slot_index 0. Used to calculate finalized staked nodes. + pub stakers_slot_offset: u64, + + /// basically: log2(slots_per_epoch) - log2(MINIMUM_SLOT_LEN) + pub first_normal_epoch: u64, + + /// basically: 2.pow(first_normal_epoch) - MINIMUM_SLOT_LEN + pub first_normal_slot: u64, +} + +impl EpochSchedule { + pub fn new(slots_per_epoch: u64, stakers_slot_offset: u64, warmup: bool) -> Self { + assert!(slots_per_epoch >= MINIMUM_SLOT_LENGTH as u64); + let (first_normal_epoch, first_normal_slot) = if warmup { + let next_power_of_two = slots_per_epoch.next_power_of_two(); + let log2_slots_per_epoch = next_power_of_two + .trailing_zeros() + .saturating_sub(MINIMUM_SLOT_LENGTH.trailing_zeros()); + + ( + u64::from(log2_slots_per_epoch), + next_power_of_two.saturating_sub(MINIMUM_SLOT_LENGTH as u64), + ) + } else { + (0, 0) + }; + EpochSchedule { + slots_per_epoch, + stakers_slot_offset, + first_normal_epoch, + first_normal_slot, + } + } + + /// get the length of the given epoch (in slots) + pub fn get_slots_in_epoch(&self, epoch: u64) -> u64 { + if epoch < self.first_normal_epoch { + 2u64.pow(epoch as u32 + MINIMUM_SLOT_LENGTH.trailing_zeros() as u32) + } else { + self.slots_per_epoch + } + } + + /// get the epoch for which the given slot should save off + /// information about stakers + pub fn get_stakers_epoch(&self, slot: u64) -> u64 { + if slot < self.first_normal_slot { + // until we get to normal slots, behave as if stakers_slot_offset == slots_per_epoch + self.get_epoch_and_slot_index(slot).0 + 1 + } else { + self.first_normal_epoch + + (slot - self.first_normal_slot + self.stakers_slot_offset) / self.slots_per_epoch + } + } + + /// get epoch and offset into the epoch for the given slot + pub fn get_epoch_and_slot_index(&self, slot: u64) -> (u64, u64) { + if slot < self.first_normal_slot { + let epoch = (slot + MINIMUM_SLOT_LENGTH as u64 + 1) + .next_power_of_two() + .trailing_zeros() + - MINIMUM_SLOT_LENGTH.trailing_zeros() + - 1; + + let epoch_len = 2u64.pow(epoch + MINIMUM_SLOT_LENGTH.trailing_zeros()); + + ( + u64::from(epoch), + slot - (epoch_len - MINIMUM_SLOT_LENGTH as u64), + ) + } else { + ( + self.first_normal_epoch + ((slot - self.first_normal_slot) / self.slots_per_epoch), + (slot - self.first_normal_slot) % self.slots_per_epoch, + ) + } + } + + pub fn get_first_slot_in_epoch(&self, epoch: u64) -> u64 { + if epoch <= self.first_normal_epoch { + (2u64.pow(epoch as u32) - 1) * MINIMUM_SLOT_LENGTH as u64 + } else { + (epoch - self.first_normal_epoch) * self.slots_per_epoch + self.first_normal_slot + } + } + + pub fn get_last_slot_in_epoch(&self, epoch: u64) -> u64 { + self.get_first_slot_in_epoch(epoch) + self.get_slots_in_epoch(epoch) - 1 + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ccfa06bcb8..b9295ca61c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -6,6 +6,7 @@ pub mod bank; pub mod bank_client; mod blockhash_queue; pub mod bloom; +pub mod epoch_schedule; pub mod genesis_utils; pub mod loader_utils; pub mod locked_accounts_results;