diff --git a/Cargo.lock b/Cargo.lock index 49c12f9258..e907998344 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2251,7 +2251,6 @@ dependencies = [ "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 0933fa908a..d4d833a6c1 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -15,7 +15,6 @@ fnv = "1.0.6" hashbrown = "0.1.8" log = "0.4.2" rand = "0.6.5" -rand_chacha = "0.1.1" serde = "1.0.88" serde_derive = "1.0.88" serde_json = "1.0.38" diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 195a07776c..1b3a708206 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -5,7 +5,6 @@ use crate::accounts::{Accounts, ErrorCounters, InstructionAccounts, InstructionLoaders}; use crate::last_id_queue::LastIdQueue; -use crate::leader_schedule::LeaderSchedule; use crate::runtime::{self, RuntimeError}; use crate::status_cache::StatusCache; use bincode::serialize; @@ -723,7 +722,9 @@ impl Bank { } /// Return the checkpointed stakes that should be used to generate a leader schedule. - fn staked_nodes_at_slot(&self, slot_height: u64) -> HashMap { + fn staked_nodes_at_slot(&self, current_slot_height: u64) -> HashMap { + let slot_height = current_slot_height.saturating_sub(self.stakers_slot_offset); + let parents = self.parents(); let mut banks = vec![self]; banks.extend(parents.iter().map(|x| x.as_ref())); @@ -741,87 +742,6 @@ impl Bank { self.ticks_per_slot } - /// Return the number of slots per tick that should be used calls to epoch_height(). - pub fn slots_per_epoch(&self) -> u64 { - self.slots_per_epoch - } - - /// Return the checkpointed stakes that should be used to generate a leader schedule. - fn staked_nodes_at_epoch(&self, epoch_height: u64) -> HashMap { - let epoch_slot_height = epoch_height * self.slots_per_epoch(); - let slot_height = epoch_slot_height.saturating_sub(self.stakers_slot_offset); - self.staked_nodes_at_slot(slot_height) - } - - /// Return the leader schedule for the given epoch. - fn leader_schedule(&self, epoch_height: u64) -> LeaderSchedule { - let stakes = self.staked_nodes_at_epoch(epoch_height); - let mut seed = [0u8; 32]; - seed[0..8].copy_from_slice(&epoch_height.to_le_bytes()); - let stakes: Vec<_> = stakes.into_iter().collect(); - LeaderSchedule::new(&stakes, seed, self.slots_per_epoch()) - } - - /// Return the leader for the slot at the slot_index and epoch_height returned - /// by the given function. - pub fn slot_leader_by(&self, get_slot_index: F) -> Pubkey - where - F: Fn(u64, u64, u64) -> (u64, u64), - { - let (slot_index, epoch_height) = get_slot_index( - self.slot_index(), - self.epoch_height(), - self.slots_per_epoch(), - ); - let leader_schedule = self.leader_schedule(epoch_height); - leader_schedule[slot_index as usize] - } - - /// Return the leader for the current slot. - pub fn slot_leader(&self) -> Pubkey { - self.slot_leader_by(|slot_index, epoch_height, _| (slot_index, epoch_height)) - } - - /// Return the epoch height and slot index of the slot before the current slot. - fn prev_slot_leader_index( - slot_index: u64, - epoch_height: u64, - slots_per_epoch: u64, - ) -> (u64, u64) { - if epoch_height == 0 && slot_index == 0 { - return (0, 0); - } - - if slot_index == 0 { - (slots_per_epoch - 1, epoch_height - 1) - } else { - (slot_index - 1, epoch_height) - } - } - - /// Return the slot_index and epoch height of the slot following the current slot. - fn next_slot_leader_index( - slot_index: u64, - epoch_height: u64, - slots_per_epoch: u64, - ) -> (u64, u64) { - if slot_index + 1 == slots_per_epoch { - (0, epoch_height + 1) - } else { - (slot_index + 1, epoch_height) - } - } - - /// Return the leader for the slot before the current slot. - pub fn prev_slot_leader(&self) -> Pubkey { - self.slot_leader_by(Self::prev_slot_leader_index) - } - - /// Return the leader for the slot following the current slot. - pub fn next_slot_leader(&self) -> Pubkey { - self.slot_leader_by(Self::next_slot_leader_index) - } - /// Return the number of ticks since genesis. pub fn tick_height(&self) -> u64 { self.last_id_queue.read().unwrap().tick_height() @@ -837,6 +757,17 @@ impl Bank { self.tick_height() / self.ticks_per_slot() } + /// Return the number of slots per tick. + pub fn slots_per_epoch(&self) -> u64 { + self.slots_per_epoch + } + + /// Return the checkpointed stakes that should be used to generate a leader schedule. + pub fn staked_nodes_at_epoch(&self, epoch_height: u64) -> HashMap { + let epoch_slot_height = epoch_height * self.slots_per_epoch(); + self.staked_nodes_at_slot(epoch_slot_height) + } + /// Return the number of slots since the last epoch boundary. pub fn slot_index(&self) -> u64 { self.slot_height() % self.slots_per_epoch() @@ -1262,23 +1193,7 @@ mod tests { let mut expected = HashMap::new(); expected.insert(pubkey, bootstrap_tokens - 1); let bank = Bank::new_from_parent(&Arc::new(bank)); - assert_eq!(bank.staked_nodes_at_epoch(bank.epoch_height()), expected,); - } - - #[test] - fn test_bank_leader_schedule_basic() { - let pubkey = Keypair::new().pubkey(); - let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(2, pubkey, 2); - let bank = Bank::new(&genesis_block); - - let ids_and_stakes: Vec<_> = bank.staked_nodes().into_iter().collect(); - let mut seed = [0u8; 32]; - seed[0..8].copy_from_slice(&bank.epoch_height().to_le_bytes()); - let leader_schedule = LeaderSchedule::new(&ids_and_stakes, seed, bank.slots_per_epoch()); - - assert_eq!(leader_schedule[0], pubkey); - assert_eq!(leader_schedule[1], pubkey); - assert_eq!(leader_schedule[2], pubkey); + assert_eq!(bank.staked_nodes_at_slot(bank.slot_height()), expected); } #[test] @@ -1535,24 +1450,4 @@ mod tests { bank.squash(); } } - - #[test] - fn test_bank_slot_leader_basic() { - let pubkey = Keypair::new().pubkey(); - let bank = Bank::new(&GenesisBlock::new_with_leader(2, pubkey, 2).0); - assert_eq!(bank.slot_leader(), pubkey); - } - - #[test] - fn test_bank_prev_slot_leader_index() { - assert_eq!(Bank::prev_slot_leader_index(0, 0, 2), (0, 0)); - assert_eq!(Bank::prev_slot_leader_index(1, 0, 2), (0, 0)); - assert_eq!(Bank::prev_slot_leader_index(0, 1, 2), (1, 0)); - } - - #[test] - fn test_bank_next_slot_leader_index() { - assert_eq!(Bank::next_slot_leader_index(0, 0, 2), (1, 0)); - assert_eq!(Bank::next_slot_leader_index(1, 0, 2), (0, 1)); - } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e070e3e6de..a797fa286b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2,7 +2,6 @@ mod accounts; pub mod bank; pub mod bloom; mod last_id_queue; -mod leader_schedule; mod runtime; mod status_cache; diff --git a/runtime/src/leader_schedule.rs b/src/leader_schedule.rs similarity index 100% rename from runtime/src/leader_schedule.rs rename to src/leader_schedule.rs diff --git a/src/leader_scheduler1.rs b/src/leader_scheduler1.rs new file mode 100644 index 0000000000..c47a51be43 --- /dev/null +++ b/src/leader_scheduler1.rs @@ -0,0 +1,129 @@ +//! The `bank` module tracks client accounts and the progress of on-chain +//! programs. It offers a high-level API that signs transactions +//! on behalf of the caller, and a low-level API for when they have +//! already been signed and verified. + +use crate::leader_schedule::LeaderSchedule; +use solana_runtime::bank::Bank; +use solana_sdk::pubkey::Pubkey; + +#[derive(Default)] +pub struct LeaderScheduler1 {} + +impl LeaderScheduler1 { + /// Return the leader schedule for the given epoch. + fn leader_schedule(&self, epoch_height: u64, bank: &Bank) -> LeaderSchedule { + let stakes = bank.staked_nodes_at_epoch(epoch_height); + let mut seed = [0u8; 32]; + seed[0..8].copy_from_slice(&epoch_height.to_le_bytes()); + let stakes: Vec<_> = stakes.into_iter().collect(); + LeaderSchedule::new(&stakes, seed, bank.slots_per_epoch()) + } + + /// Return the leader for the slot at the slot_index and epoch_height returned + /// by the given function. + pub fn slot_leader_by(&self, bank: &Bank, get_slot_index: F) -> Pubkey + where + F: Fn(u64, u64, u64) -> (u64, u64), + { + let (slot_index, epoch_height) = get_slot_index( + bank.slot_index(), + bank.epoch_height(), + bank.slots_per_epoch(), + ); + let leader_schedule = self.leader_schedule(epoch_height, bank); + leader_schedule[slot_index as usize] + } + + /// Return the leader for the current slot. + pub fn slot_leader(&self, bank: &Bank) -> Pubkey { + self.slot_leader_by(bank, |slot_index, epoch_height, _| { + (slot_index, epoch_height) + }) + } + + /// Return the epoch height and slot index of the slot before the current slot. + fn prev_slot_leader_index( + slot_index: u64, + epoch_height: u64, + slots_per_epoch: u64, + ) -> (u64, u64) { + if epoch_height == 0 && slot_index == 0 { + return (0, 0); + } + + if slot_index == 0 { + (slots_per_epoch - 1, epoch_height - 1) + } else { + (slot_index - 1, epoch_height) + } + } + + /// Return the slot_index and epoch height of the slot following the current slot. + fn next_slot_leader_index( + slot_index: u64, + epoch_height: u64, + slots_per_epoch: u64, + ) -> (u64, u64) { + if slot_index + 1 == slots_per_epoch { + (0, epoch_height + 1) + } else { + (slot_index + 1, epoch_height) + } + } + + /// Return the leader for the slot before the current slot. + pub fn prev_slot_leader(&self, bank: &Bank) -> Pubkey { + self.slot_leader_by(bank, Self::prev_slot_leader_index) + } + + /// Return the leader for the slot following the current slot. + pub fn next_slot_leader(&self, bank: &Bank) -> Pubkey { + self.slot_leader_by(bank, Self::next_slot_leader_index) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use solana_sdk::genesis_block::GenesisBlock; + use solana_sdk::signature::{Keypair, KeypairUtil}; + + #[test] + fn test_leader_schedule_via_bank() { + let pubkey = Keypair::new().pubkey(); + let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(2, pubkey, 2); + let bank = Bank::new(&genesis_block); + + let ids_and_stakes: Vec<_> = bank.staked_nodes().into_iter().collect(); + let seed = [0u8; 32]; + let leader_schedule = + LeaderSchedule::new(&ids_and_stakes, seed, genesis_block.slots_per_epoch); + + assert_eq!(leader_schedule[0], pubkey); + assert_eq!(leader_schedule[1], pubkey); + assert_eq!(leader_schedule[2], pubkey); + } + + #[test] + fn test_leader_scheduler1_basic() { + let pubkey = Keypair::new().pubkey(); + let genesis_block = GenesisBlock::new_with_leader(2, pubkey, 2).0; + let bank = Bank::new(&genesis_block); + let leader_scheduler = LeaderScheduler1::default(); + assert_eq!(leader_scheduler.slot_leader(&bank), pubkey); + } + + #[test] + fn test_leader_scheduler1_prev_slot_leader_index() { + assert_eq!(LeaderScheduler1::prev_slot_leader_index(0, 0, 2), (0, 0)); + assert_eq!(LeaderScheduler1::prev_slot_leader_index(1, 0, 2), (0, 0)); + assert_eq!(LeaderScheduler1::prev_slot_leader_index(0, 1, 2), (1, 0)); + } + + #[test] + fn test_leader_scheduler1_next_slot_leader_index() { + assert_eq!(LeaderScheduler1::next_slot_leader_index(0, 0, 2), (1, 0)); + assert_eq!(LeaderScheduler1::next_slot_leader_index(1, 0, 2), (0, 1)); + } +} diff --git a/src/lib.rs b/src/lib.rs index 72e4f81af0..3591f8d419 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,9 @@ pub mod fullnode; pub mod gen_keys; pub mod gossip_service; pub mod leader_confirmation_service; +pub mod leader_schedule; pub mod leader_scheduler; +pub mod leader_scheduler1; pub mod local_vote_signer_service; pub mod packet; pub mod poh;