From 6d1b43f1b1c3df2b76f00d3843e46db1efcc8bcc Mon Sep 17 00:00:00 2001 From: carllin Date: Wed, 27 Feb 2019 14:41:46 -0800 Subject: [PATCH] Make leader_schedule a utitlity module named leader_schedule_utils (#2988) --- src/blocktree_processor.rs | 7 +- src/fullnode.rs | 4 +- src/leader_schedule_utils.rs | 127 +++++++++++++++++++++++++++++++ src/leader_scheduler.rs | 141 ----------------------------------- src/lib.rs | 2 +- src/replay_stage.rs | 13 ++-- 6 files changed, 141 insertions(+), 153 deletions(-) create mode 100644 src/leader_schedule_utils.rs delete mode 100644 src/leader_scheduler.rs diff --git a/src/blocktree_processor.rs b/src/blocktree_processor.rs index 9e31319811..bca27f84f3 100644 --- a/src/blocktree_processor.rs +++ b/src/blocktree_processor.rs @@ -1,7 +1,7 @@ use crate::bank_forks::BankForks; use crate::blocktree::Blocktree; use crate::entry::{Entry, EntrySlice}; -use crate::leader_scheduler::LeaderScheduler; +use crate::leader_schedule_utils; use rayon::prelude::*; use solana_metrics::counter::Counter; use solana_runtime::bank::{Bank, BankError, Result}; @@ -180,7 +180,8 @@ pub fn process_blocktree( entry_height += entries.len() as u64; } - let slot_complete = LeaderScheduler::num_ticks_left_in_slot(&bank, bank.tick_height()) == 0; + let slot_complete = + leader_schedule_utils::num_ticks_left_in_slot(&bank, bank.tick_height()) == 0; if !slot_complete || meta.next_slots.is_empty() { // Reached the end of this fork. Record the final entry height and last entry id @@ -202,7 +203,7 @@ pub fn process_blocktree( // This is a fork point, create a new child bank for each fork pending_slots.extend(meta.next_slots.iter().map(|next_slot| { - let leader = LeaderScheduler::default().slot_leader_at(*next_slot, &bank); + let leader = leader_schedule_utils::slot_leader_at(*next_slot, &bank); let child_bank = Bank::new_from_parent_and_id(&bank, leader, *next_slot); trace!("Add child bank for slot={}", next_slot); bank_forks.insert(*next_slot, child_bank); diff --git a/src/fullnode.rs b/src/fullnode.rs index bc382140d0..e5d2e41873 100644 --- a/src/fullnode.rs +++ b/src/fullnode.rs @@ -8,7 +8,7 @@ use crate::entry::create_ticks; use crate::entry::next_entry_mut; use crate::entry::Entry; use crate::gossip_service::GossipService; -use crate::leader_scheduler::LeaderScheduler; +use crate::leader_schedule_utils; use crate::poh_recorder::PohRecorder; use crate::poh_service::{PohService, PohServiceConfig}; use crate::rpc_pubsub_service::PubSubService; @@ -279,7 +279,7 @@ impl Fullnode { rotation_info.leader_id, rotation_info.last_entry_id, ); - let was_leader = LeaderScheduler::default().slot_leader(&rotation_info.bank) == self.id; + let was_leader = leader_schedule_utils::slot_leader(&rotation_info.bank) == self.id; if let Some(ref mut rpc_service) = self.rpc_service { // TODO: This is not the correct bank. Instead TVU should pass along the diff --git a/src/leader_schedule_utils.rs b/src/leader_schedule_utils.rs new file mode 100644 index 0000000000..e033e80c1d --- /dev/null +++ b/src/leader_schedule_utils.rs @@ -0,0 +1,127 @@ +//! 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; + +/// Return the leader schedule for the given epoch. +fn leader_schedule(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. +fn slot_leader_by(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 = leader_schedule(epoch_height, bank); + leader_schedule[slot_index as usize] +} + +/// Return the leader for the current slot. +pub fn slot_leader(bank: &Bank) -> Pubkey { + slot_leader_by(bank, |slot_index, epoch_height, _| { + (slot_index, epoch_height) + }) +} + +/// Return the leader for the given slot. +pub fn slot_leader_at(slot: u64, bank: &Bank) -> Pubkey { + let epoch = slot / bank.slots_per_epoch(); + slot_leader_by(bank, |_, _, _| (slot, epoch)) +} + +/// 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(bank: &Bank) -> Pubkey { + slot_leader_by(bank, prev_slot_leader_index) +} + +/// Return the leader for the slot following the current slot. +pub fn next_slot_leader(bank: &Bank) -> Pubkey { + slot_leader_by(bank, next_slot_leader_index) +} + +// Returns the number of ticks remaining from the specified tick_height to the end of the +// slot implied by the tick_height +pub fn num_ticks_left_in_slot(bank: &Bank, tick_height: u64) -> u64 { + bank.ticks_per_slot() - tick_height % bank.ticks_per_slot() - 1 +} + +#[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); + assert_eq!(slot_leader(&bank), pubkey); + } + + #[test] + fn test_leader_scheduler1_prev_slot_leader_index() { + assert_eq!(prev_slot_leader_index(0, 0, 2), (0, 0)); + assert_eq!(prev_slot_leader_index(1, 0, 2), (0, 0)); + assert_eq!(prev_slot_leader_index(0, 1, 2), (1, 0)); + } + + #[test] + fn test_leader_scheduler1_next_slot_leader_index() { + assert_eq!(next_slot_leader_index(0, 0, 2), (1, 0)); + assert_eq!(next_slot_leader_index(1, 0, 2), (0, 1)); + } +} diff --git a/src/leader_scheduler.rs b/src/leader_scheduler.rs deleted file mode 100644 index 6f32046fb9..0000000000 --- a/src/leader_scheduler.rs +++ /dev/null @@ -1,141 +0,0 @@ -//! 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 LeaderScheduler {} - -impl LeaderScheduler { - /// 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. - 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 leader for the given slot. - pub fn slot_leader_at(&self, slot: u64, bank: &Bank) -> Pubkey { - let epoch = slot / bank.slots_per_epoch(); - self.slot_leader_by(bank, |_, _, _| (slot, epoch)) - } - - /// 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) - } - - // Returns the number of ticks remaining from the specified tick_height to the end of the - // slot implied by the tick_height - pub fn num_ticks_left_in_slot(bank: &Bank, tick_height: u64) -> u64 { - bank.ticks_per_slot() - tick_height % bank.ticks_per_slot() - 1 - } -} - -#[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 = LeaderScheduler::default(); - assert_eq!(leader_scheduler.slot_leader(&bank), pubkey); - } - - #[test] - fn test_leader_scheduler1_prev_slot_leader_index() { - assert_eq!(LeaderScheduler::prev_slot_leader_index(0, 0, 2), (0, 0)); - assert_eq!(LeaderScheduler::prev_slot_leader_index(1, 0, 2), (0, 0)); - assert_eq!(LeaderScheduler::prev_slot_leader_index(0, 1, 2), (1, 0)); - } - - #[test] - fn test_leader_scheduler1_next_slot_leader_index() { - assert_eq!(LeaderScheduler::next_slot_leader_index(0, 0, 2), (1, 0)); - assert_eq!(LeaderScheduler::next_slot_leader_index(1, 0, 2), (0, 1)); - } -} diff --git a/src/lib.rs b/src/lib.rs index 2bdfe73cb5..33f37818b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ pub mod gen_keys; pub mod gossip_service; pub mod leader_confirmation_service; pub mod leader_schedule; -pub mod leader_scheduler; +pub mod leader_schedule_utils; pub mod local_vote_signer_service; pub mod packet; pub mod poh; diff --git a/src/replay_stage.rs b/src/replay_stage.rs index 9dc8da3ef5..45e4e2cf4f 100644 --- a/src/replay_stage.rs +++ b/src/replay_stage.rs @@ -5,7 +5,7 @@ use crate::blocktree::Blocktree; use crate::blocktree_processor::{self, BankForksInfo}; use crate::cluster_info::ClusterInfo; use crate::entry::{Entry, EntryMeta, EntryReceiver, EntrySender, EntrySlice}; -use crate::leader_scheduler::LeaderScheduler; +use crate::leader_schedule_utils; use crate::packet::BlobError; use crate::result::{Error, Result}; use crate::rpc_subscriptions::RpcSubscriptions; @@ -89,8 +89,9 @@ impl ReplayStage { let num_ticks = bank.tick_height(); let slot_height = bank.slot_height(); - let leader_id = LeaderScheduler::default().slot_leader(bank); - let mut num_ticks_to_next_vote = LeaderScheduler::num_ticks_left_in_slot(bank, num_ticks); + let leader_id = leader_schedule_utils::slot_leader(bank); + let mut num_ticks_to_next_vote = + leader_schedule_utils::num_ticks_left_in_slot(bank, num_ticks); let mut entry_tick_height = num_ticks; let mut entries_with_meta = Vec::new(); @@ -216,7 +217,7 @@ impl ReplayStage { let slot = (tick_height + 1) / bank.ticks_per_slot(); let first_tick_in_slot = slot * bank.ticks_per_slot(); - let leader_id = LeaderScheduler::default().slot_leader_at(slot, &bank); + let leader_id = leader_schedule_utils::slot_leader_at(slot, &bank); trace!("node {:?} scheduled as leader for slot {}", leader_id, slot,); let old_bank = bank.clone(); @@ -242,7 +243,7 @@ impl ReplayStage { .unwrap(); let max_tick_height_for_slot = first_tick_in_slot - + LeaderScheduler::num_ticks_left_in_slot(&bank, first_tick_in_slot); + + leader_schedule_utils::num_ticks_left_in_slot(&bank, first_tick_in_slot); (Some(slot), leader_id, max_tick_height_for_slot) }; @@ -346,7 +347,7 @@ impl ReplayStage { let (leader_id, next_slot) = { let slot = (current_tick_height + 1) / bank.ticks_per_slot(); - (LeaderScheduler::default().slot_leader_at(slot, &bank), slot) + (leader_schedule_utils::slot_leader_at(slot, &bank), slot) }; // If we were the leader for the last slot update the last id b/c we