leader_scheduler: replace older terminology with ticks, slots and epochs

This commit is contained in:
Michael Vines
2019-02-07 09:31:09 -08:00
committed by Grimes
parent cdb2a7bef3
commit 1f0b3f954a
7 changed files with 226 additions and 334 deletions

View File

@ -370,7 +370,7 @@ mod test {
// Mock the tick height to look like the tick height right after a leader transition // Mock the tick height to look like the tick height right after a leader transition
leader_scheduler.set_leader_schedule(vec![leader_keypair.pubkey()]); leader_scheduler.set_leader_schedule(vec![leader_keypair.pubkey()]);
let start_tick_height = 0; let start_tick_height = 0;
let max_tick_height = start_tick_height + leader_scheduler.seed_rotation_interval; let max_tick_height = start_tick_height + leader_scheduler.ticks_per_epoch;
let entry_height = 2 * start_tick_height; let entry_height = 2 * start_tick_height;
let leader_scheduler = Arc::new(RwLock::new(leader_scheduler)); let leader_scheduler = Arc::new(RwLock::new(leader_scheduler));

View File

@ -55,7 +55,7 @@ pub fn repair(
// 1) The replay stage hasn't caught up to the "consumed" entries we sent, // 1) The replay stage hasn't caught up to the "consumed" entries we sent,
// in which case it will eventually catch up // in which case it will eventually catch up
// //
// 2) We are on the border between seed_rotation_intervals, so the // 2) We are on the border between ticks_per_epochs, so the
// schedule won't be known until the entry on that cusp is received // schedule won't be known until the entry on that cusp is received
// by the replay stage (which comes after this stage). Hence, the next // by the replay stage (which comes after this stage). Hence, the next
// leader at the beginning of that next epoch will not know they are the // leader at the beginning of that next epoch will not know they are the

View File

@ -615,15 +615,12 @@ mod tests {
// Once the bootstrap leader hits the second epoch, because there are no other choices in // Once the bootstrap leader hits the second epoch, because there are no other choices in
// the active set, this leader will remain the leader in the second epoch. In the second // the active set, this leader will remain the leader in the second epoch. In the second
// epoch, check that the same leader knows to shut down and restart as a leader again. // epoch, check that the same leader knows to shut down and restart as a leader again.
let leader_rotation_interval = 5; let ticks_per_slot = 5;
let num_slots_per_epoch = 2; let slots_per_epoch = 2;
let seed_rotation_interval = num_slots_per_epoch * leader_rotation_interval; let ticks_per_epoch = slots_per_epoch * ticks_per_slot;
let active_window_length = 10 * seed_rotation_interval; let active_window_length = 10 * ticks_per_epoch;
let leader_scheduler_config = LeaderSchedulerConfig::new( let leader_scheduler_config =
leader_rotation_interval, LeaderSchedulerConfig::new(ticks_per_slot, slots_per_epoch, active_window_length);
seed_rotation_interval,
active_window_length,
);
let bootstrap_leader_keypair = Arc::new(bootstrap_leader_keypair); let bootstrap_leader_keypair = Arc::new(bootstrap_leader_keypair);
let voting_keypair = VotingKeypair::new_local(&bootstrap_leader_keypair); let voting_keypair = VotingKeypair::new_local(&bootstrap_leader_keypair);
@ -646,10 +643,7 @@ mod tests {
// cluster it will continue to be the leader // cluster it will continue to be the leader
assert_eq!( assert_eq!(
rotation_receiver.recv().unwrap(), rotation_receiver.recv().unwrap(),
( (FullnodeReturnType::LeaderToLeaderRotation, ticks_per_slot)
FullnodeReturnType::LeaderToLeaderRotation,
leader_rotation_interval
)
); );
bootstrap_leader_exit(); bootstrap_leader_exit();
} }
@ -659,11 +653,12 @@ mod tests {
solana_logger::setup(); solana_logger::setup();
let mut fullnode_config = FullnodeConfig::default(); let mut fullnode_config = FullnodeConfig::default();
let leader_rotation_interval = 16; let ticks_per_slot = 16;
let slots_per_epoch = 2;
fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new( fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new(
leader_rotation_interval, ticks_per_slot,
leader_rotation_interval * 2, slots_per_epoch,
leader_rotation_interval * 2, ticks_per_slot * slots_per_epoch,
); );
// Create the leader and validator nodes // Create the leader and validator nodes
@ -676,7 +671,7 @@ mod tests {
0, 0,
// Generate enough ticks for two epochs to flush the bootstrap_leader's vote at // Generate enough ticks for two epochs to flush the bootstrap_leader's vote at
// tick_height = 0 from the leader scheduler's active window // tick_height = 0 from the leader scheduler's active window
leader_rotation_interval * 4, ticks_per_slot * 4,
"test_wrong_role_transition", "test_wrong_role_transition",
); );
let bootstrap_leader_info = bootstrap_leader_node.info.clone(); let bootstrap_leader_info = bootstrap_leader_node.info.clone();
@ -749,13 +744,14 @@ mod tests {
info!("validator: {:?}", validator_info.id); info!("validator: {:?}", validator_info.id);
// Set the leader scheduler for the validator // Set the leader scheduler for the validator
let leader_rotation_interval = 10; let ticks_per_slot = 10;
let slots_per_epoch = 4;
let mut fullnode_config = FullnodeConfig::default(); let mut fullnode_config = FullnodeConfig::default();
fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new( fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new(
leader_rotation_interval, ticks_per_slot,
leader_rotation_interval * 4, slots_per_epoch,
leader_rotation_interval * 4, ticks_per_slot * slots_per_epoch,
); );
let voting_keypair = VotingKeypair::new_local(&validator_keypair); let voting_keypair = VotingKeypair::new_local(&validator_keypair);
@ -769,12 +765,7 @@ mod tests {
&fullnode_config, &fullnode_config,
); );
let blobs_to_send = fullnode_config let blobs_to_send = slots_per_epoch * ticks_per_slot + ticks_per_slot;
.leader_scheduler_config
.seed_rotation_interval
+ fullnode_config
.leader_scheduler_config
.leader_rotation_interval;
// Send blobs to the validator from our mock leader // Send blobs to the validator from our mock leader
let t_responder = { let t_responder = {
@ -817,12 +808,7 @@ mod tests {
let (bank, entry_height, _, _, _, _) = let (bank, entry_height, _, _, _, _) =
new_bank_from_ledger(&validator_ledger_path, &LeaderSchedulerConfig::default()); new_bank_from_ledger(&validator_ledger_path, &LeaderSchedulerConfig::default());
assert!( assert!(bank.tick_height() >= bank.leader_scheduler.read().unwrap().ticks_per_epoch);
bank.tick_height()
>= fullnode_config
.leader_scheduler_config
.seed_rotation_interval
);
assert!(entry_height >= ledger_initial_len); assert!(entry_height >= ledger_initial_len);
@ -851,13 +837,14 @@ mod tests {
let leader_node_info = leader_node.info.clone(); let leader_node_info = leader_node.info.clone();
// Set the leader scheduler for the validator // Set the leader scheduler for the validator
let leader_rotation_interval = 5; let ticks_per_slot = 5;
let slots_per_epoch = 2;
let mut fullnode_config = FullnodeConfig::default(); let mut fullnode_config = FullnodeConfig::default();
fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new( fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new(
leader_rotation_interval, ticks_per_slot,
leader_rotation_interval * 2, slots_per_epoch,
leader_rotation_interval * 2, ticks_per_slot * slots_per_epoch,
); );
let voting_keypair = VotingKeypair::new_local(&leader_keypair); let voting_keypair = VotingKeypair::new_local(&leader_keypair);
@ -895,17 +882,14 @@ mod tests {
let (rotation_sender, rotation_receiver) = channel(); let (rotation_sender, rotation_receiver) = channel();
let leader_exit = leader.run(Some(rotation_sender)); let leader_exit = leader.run(Some(rotation_sender));
let expected_rotations = vec![ let expected_rotations = vec![
(FullnodeReturnType::LeaderToLeaderRotation, ticks_per_slot),
( (
FullnodeReturnType::LeaderToLeaderRotation, FullnodeReturnType::LeaderToLeaderRotation,
leader_rotation_interval, 2 * ticks_per_slot,
),
(
FullnodeReturnType::LeaderToLeaderRotation,
2 * leader_rotation_interval,
), ),
( (
FullnodeReturnType::LeaderToValidatorRotation, FullnodeReturnType::LeaderToValidatorRotation,
3 * leader_rotation_interval, 3 * ticks_per_slot,
), ),
]; ];

View File

@ -23,34 +23,25 @@ pub const DEFAULT_TICKS_PER_SLOT: u64 = 8;
*/ */
pub const DEFAULT_TICKS_PER_SLOT: u64 = 64; // TODO: DEFAULT_TICKS_PER_SLOT = 8 causes instability in the integration tests. pub const DEFAULT_TICKS_PER_SLOT: u64 = 64; // TODO: DEFAULT_TICKS_PER_SLOT = 8 causes instability in the integration tests.
pub const DEFAULT_SLOTS_PER_EPOCH: u64 = 64; pub const DEFAULT_SLOTS_PER_EPOCH: u64 = 64;
pub const DEFAULT_SEED_ROTATION_INTERVAL: u64 = DEFAULT_SLOTS_PER_EPOCH * DEFAULT_TICKS_PER_SLOT; pub const DEFAULT_ACTIVE_WINDOW_TICK_LENGTH: u64 = DEFAULT_SLOTS_PER_EPOCH * DEFAULT_TICKS_PER_SLOT;
pub const DEFAULT_ACTIVE_WINDOW_LENGTH: u64 = DEFAULT_SEED_ROTATION_INTERVAL;
#[derive(Clone)] #[derive(Clone)]
pub struct LeaderSchedulerConfig { pub struct LeaderSchedulerConfig {
// The interval at which to rotate the leader, should be much less than pub ticks_per_slot: u64,
// seed_rotation_interval pub slots_per_epoch: u64,
pub leader_rotation_interval: u64,
// The interval at which to generate the seed used for ranking the validators // The tick length of the acceptable window for determining live validators
pub seed_rotation_interval: u64, pub active_window_tick_length: u64,
// The length of the acceptable window for determining live validators
pub active_window_length: u64,
} }
// Used to toggle leader rotation in fullnode so that tests that don't // Used to toggle leader rotation in fullnode so that tests that don't
// need leader rotation don't break // need leader rotation don't break
impl LeaderSchedulerConfig { impl LeaderSchedulerConfig {
pub fn new( pub fn new(ticks_per_slot: u64, slots_per_epoch: u64, active_window_tick_length: u64) -> Self {
leader_rotation_interval: u64,
seed_rotation_interval: u64,
active_window_length: u64,
) -> Self {
LeaderSchedulerConfig { LeaderSchedulerConfig {
leader_rotation_interval, ticks_per_slot,
seed_rotation_interval, slots_per_epoch,
active_window_length, active_window_tick_length,
} }
} }
} }
@ -58,9 +49,9 @@ impl LeaderSchedulerConfig {
impl Default for LeaderSchedulerConfig { impl Default for LeaderSchedulerConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
leader_rotation_interval: DEFAULT_TICKS_PER_SLOT, ticks_per_slot: DEFAULT_TICKS_PER_SLOT,
seed_rotation_interval: DEFAULT_SEED_ROTATION_INTERVAL, slots_per_epoch: DEFAULT_SLOTS_PER_EPOCH,
active_window_length: DEFAULT_ACTIVE_WINDOW_LENGTH, active_window_tick_length: DEFAULT_ACTIVE_WINDOW_TICK_LENGTH,
} }
} }
} }
@ -68,15 +59,15 @@ impl Default for LeaderSchedulerConfig {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct LeaderScheduler { pub struct LeaderScheduler {
// A leader slot duration in ticks // A leader slot duration in ticks
pub leader_rotation_interval: u64, pub ticks_per_slot: u64,
// Duration of an epoch (one or more slots) in ticks. // Duration of an epoch (one or more slots) in ticks.
// This value must be divisible by leader_rotation_interval // This value must be divisible by ticks_per_slot
pub seed_rotation_interval: u64, pub ticks_per_epoch: u64,
// The length of time in ticks for which a vote qualifies a candidate for leader // The length of time in ticks for which a vote qualifies a candidate for leader
// selection // selection
pub active_window_length: u64, active_window_tick_length: u64,
// Round-robin ordering of the validators for the current epoch at epoch_schedule[0], and the // Round-robin ordering of the validators for the current epoch at epoch_schedule[0], and the
// previous epoch at epoch_schedule[1] // previous epoch at epoch_schedule[1]
@ -92,31 +83,33 @@ pub struct LeaderScheduler {
// The LeaderScheduler implements a schedule for leaders as follows: // The LeaderScheduler implements a schedule for leaders as follows:
// //
// 1) After the first seed is generated, this signals the beginning of actual leader rotation. // 1) After the first seed is generated, this signals the beginning of actual leader rotation.
// From this point on, every seed_rotation_interval PoH counts we generate the seed based // From this point on, every ticks_per_epoch PoH counts we generate the seed based
// on the PoH height, and use it to do a weighted sample from the set // on the PoH height, and use it to do a weighted sample from the set
// of validators based on current stake weight. This gets you the bootstrap leader A for // of validators based on current stake weight. This gets you the bootstrap leader A for
// the next leader_rotation_interval PoH counts. On the same PoH count we generate the seed, // the next ticks_per_slot PoH counts. On the same PoH count we generate the seed,
// we also order the validators based on their current stake weight, and starting // we also order the validators based on their current stake weight, and starting
// from leader A, we then pick the next leader sequentially every leader_rotation_interval // from leader A, we then pick the next leader sequentially every ticks_per_slot
// PoH counts based on this fixed ordering, so the next // PoH counts based on this fixed ordering, so the next
// seed_rotation_interval / leader_rotation_interval leaders are determined. // ticks_per_epoch / ticks_per_slot leaders are determined.
// //
// 2) When we we hit the next seed rotation PoH height, step 1) is executed again to // 2) When we we hit the next seed rotation PoH height, step 1) is executed again to
// calculate the leader schedule for the upcoming seed_rotation_interval PoH counts. // calculate the leader schedule for the upcoming ticks_per_epoch PoH counts.
impl LeaderScheduler { impl LeaderScheduler {
pub fn new(config: &LeaderSchedulerConfig) -> Self { pub fn new(config: &LeaderSchedulerConfig) -> Self {
let leader_rotation_interval = config.leader_rotation_interval; let ticks_per_slot = config.ticks_per_slot;
let seed_rotation_interval = config.seed_rotation_interval; let ticks_per_epoch = config.ticks_per_slot * config.slots_per_epoch;
let active_window_length = config.active_window_length; let active_window_tick_length = config.active_window_tick_length;
// Enforced invariants // Enforced invariants
assert!(seed_rotation_interval >= leader_rotation_interval); assert!(ticks_per_slot > 0);
assert!(seed_rotation_interval % leader_rotation_interval == 0); assert!(ticks_per_epoch >= ticks_per_slot);
assert!(ticks_per_epoch % ticks_per_slot == 0);
assert!(active_window_tick_length > 0);
LeaderScheduler { LeaderScheduler {
leader_rotation_interval, ticks_per_slot,
seed_rotation_interval, ticks_per_epoch,
active_window_length, active_window_tick_length,
seed: 0, seed: 0,
epoch_schedule: [Vec::new(), Vec::new()], epoch_schedule: [Vec::new(), Vec::new()],
current_epoch: 0, current_epoch: 0,
@ -124,17 +117,17 @@ impl LeaderScheduler {
} }
pub fn tick_height_to_slot(&self, tick_height: u64) -> u64 { pub fn tick_height_to_slot(&self, tick_height: u64) -> u64 {
tick_height / self.leader_rotation_interval tick_height / self.ticks_per_slot
} }
fn tick_height_to_epoch(&self, tick_height: u64) -> u64 { fn tick_height_to_epoch(&self, tick_height: u64) -> u64 {
tick_height / self.seed_rotation_interval tick_height / self.ticks_per_epoch
} }
// Returns the number of ticks remaining from the specified tick_height to the end of the // Returns the number of ticks remaining from the specified tick_height to the end of the
// current slot // current slot
pub fn num_ticks_left_in_slot(&self, tick_height: u64) -> u64 { pub fn num_ticks_left_in_slot(&self, tick_height: u64) -> u64 {
self.leader_rotation_interval - tick_height % self.leader_rotation_interval - 1 self.ticks_per_slot - tick_height % self.ticks_per_slot - 1
} }
// Inform the leader scheduler about the current tick height of the cluster. It may generate a // Inform the leader scheduler about the current tick height of the cluster. It may generate a
@ -154,7 +147,7 @@ impl LeaderScheduler {
// the current slot, so that generally the schedule applies to the range [slot N tick 1, // the current slot, so that generally the schedule applies to the range [slot N tick 1,
// slot N+1 tick 0). The schedule is shifted right 1 tick from the slot rotation interval so that // slot N+1 tick 0). The schedule is shifted right 1 tick from the slot rotation interval so that
// the next leader is always known *before* a rotation occurs // the next leader is always known *before* a rotation occurs
if tick_height == 0 || tick_height % self.seed_rotation_interval == 1 { if tick_height == 0 || tick_height % self.ticks_per_epoch == 1 {
self.generate_schedule(tick_height, bank); self.generate_schedule(tick_height, bank);
} }
} }
@ -162,7 +155,7 @@ impl LeaderScheduler {
// Returns the leader for the requested slot, or None if the slot is out of the schedule bounds // Returns the leader for the requested slot, or None if the slot is out of the schedule bounds
pub fn get_leader_for_slot(&self, slot: u64) -> Option<Pubkey> { pub fn get_leader_for_slot(&self, slot: u64) -> Option<Pubkey> {
trace!("get_leader_for_slot: slot {}", slot); trace!("get_leader_for_slot: slot {}", slot);
let tick_height = slot * self.leader_rotation_interval; let tick_height = slot * self.ticks_per_slot;
let epoch = self.tick_height_to_epoch(tick_height); let epoch = self.tick_height_to_epoch(tick_height);
trace!( trace!(
"get_leader_for_slot: tick_height={} slot={} epoch={} (ce={})", "get_leader_for_slot: tick_height={} slot={} epoch={} (ce={})",
@ -191,8 +184,8 @@ impl LeaderScheduler {
panic!("leader_schedule is empty"); // Should never happen panic!("leader_schedule is empty"); // Should never happen
} }
let first_tick_in_epoch = epoch * self.seed_rotation_interval; let first_tick_in_epoch = epoch * self.ticks_per_epoch;
let slot_index = (tick_height - first_tick_in_epoch) / self.leader_rotation_interval; let slot_index = (tick_height - first_tick_in_epoch) / self.ticks_per_slot;
// Round robin through each node in the schedule // Round robin through each node in the schedule
Some(schedule[slot_index as usize % schedule.len()]) Some(schedule[slot_index as usize % schedule.len()])
@ -204,7 +197,7 @@ impl LeaderScheduler {
// process_transaction(), case VoteInstruction::RegisterAccount), we can use a vector. // process_transaction(), case VoteInstruction::RegisterAccount), we can use a vector.
fn get_active_set(&mut self, tick_height: u64, bank: &Bank) -> HashSet<Pubkey> { fn get_active_set(&mut self, tick_height: u64, bank: &Bank) -> HashSet<Pubkey> {
let upper_bound = tick_height; let upper_bound = tick_height;
let lower_bound = tick_height.saturating_sub(self.active_window_length); let lower_bound = tick_height.saturating_sub(self.active_window_tick_length);
trace!( trace!(
"get_active_set: vote bounds ({}, {})", "get_active_set: vote bounds ({}, {})",
lower_bound, lower_bound,
@ -303,8 +296,7 @@ impl LeaderScheduler {
let next_slot_leader = validator_rankings[0]; let next_slot_leader = validator_rankings[0];
if last_slot_leader == next_slot_leader { if last_slot_leader == next_slot_leader {
let slots_per_epoch = let slots_per_epoch = self.ticks_per_epoch / self.ticks_per_slot;
self.seed_rotation_interval / self.leader_rotation_interval;
if slots_per_epoch == 1 { if slots_per_epoch == 1 {
// If there is only one slot per epoch, and the same leader as the last slot // If there is only one slot per epoch, and the same leader as the last slot
// of the previous epoch was chosen, then pick the next leader in the // of the previous epoch was chosen, then pick the next leader in the
@ -326,7 +318,7 @@ impl LeaderScheduler {
trace!( trace!(
"generate_schedule: schedule for ticks ({}, {}): {:?} ", "generate_schedule: schedule for ticks ({}, {}): {:?} ",
tick_height, tick_height,
tick_height + self.seed_rotation_interval, tick_height + self.ticks_per_epoch,
self.epoch_schedule[0] self.epoch_schedule[0]
); );
} }
@ -490,23 +482,19 @@ pub mod tests {
bank.process_transaction(&new_vote_tx).unwrap(); bank.process_transaction(&new_vote_tx).unwrap();
} }
fn run_scheduler_test( fn run_scheduler_test(num_validators: usize, ticks_per_slot: u64, ticks_per_epoch: u64) {
num_validators: usize,
leader_rotation_interval: u64,
seed_rotation_interval: u64,
) {
info!( info!(
"run_scheduler_test({}, {}, {})", "run_scheduler_test({}, {}, {})",
num_validators, leader_rotation_interval, seed_rotation_interval num_validators, ticks_per_slot, ticks_per_epoch
); );
// Allow the validators to be in the active window for the entire test // Allow the validators to be in the active window for the entire test
let active_window_length = seed_rotation_interval; let active_window_tick_length = ticks_per_epoch;
// Set up the LeaderScheduler struct // Set up the LeaderScheduler struct
let leader_scheduler_config = LeaderSchedulerConfig::new( let leader_scheduler_config = LeaderSchedulerConfig::new(
leader_rotation_interval, ticks_per_slot,
seed_rotation_interval, ticks_per_epoch / ticks_per_slot,
active_window_length, active_window_tick_length,
); );
// Create the bank and validators, which are inserted in order of account balance // Create the bank and validators, which are inserted in order of account balance
@ -537,11 +525,11 @@ pub mod tests {
); );
// Vote to make the validator part of the active set for the entire test // Vote to make the validator part of the active set for the entire test
// (we made the active_window_length large enough at the beginning of the test) // (we made the active_window_tick_length large enough at the beginning of the test)
push_vote( push_vote(
&voting_keypair, &voting_keypair,
&bank, &bank,
seed_rotation_interval, ticks_per_epoch,
genesis_block.last_id(), genesis_block.last_id(),
); );
} }
@ -551,7 +539,7 @@ pub mod tests {
leader_scheduler.generate_schedule(0, &bank); leader_scheduler.generate_schedule(0, &bank);
// The leader outside of the newly generated schedule window: // The leader outside of the newly generated schedule window:
// (0, seed_rotation_interval] // (0, ticks_per_epoch]
info!("yyy"); info!("yyy");
assert_eq!( assert_eq!(
leader_scheduler.get_leader_for_slot(0), leader_scheduler.get_leader_for_slot(0),
@ -560,7 +548,7 @@ pub mod tests {
info!("xxxx"); info!("xxxx");
assert_eq!( assert_eq!(
leader_scheduler leader_scheduler
.get_leader_for_slot(leader_scheduler.tick_height_to_slot(seed_rotation_interval)), .get_leader_for_slot(leader_scheduler.tick_height_to_slot(ticks_per_epoch)),
None None
); );
@ -571,15 +559,15 @@ pub mod tests {
// Generate schedule for third epoch to ensure the bootstrap leader will not be added to // Generate schedule for third epoch to ensure the bootstrap leader will not be added to
// the schedule, as the bootstrap leader did not vote in the second epoch but all other // the schedule, as the bootstrap leader did not vote in the second epoch but all other
// validators did // validators did
leader_scheduler.generate_schedule(seed_rotation_interval + 1, &bank); leader_scheduler.generate_schedule(ticks_per_epoch + 1, &bank);
// For the next seed_rotation_interval entries, call get_leader_for_slot every // For the next ticks_per_epoch entries, call get_leader_for_slot every
// leader_rotation_interval entries, and the next leader should be the next validator // ticks_per_slot entries, and the next leader should be the next validator
// in order of stake // in order of stake
let num_slots = seed_rotation_interval / leader_rotation_interval; let num_slots = ticks_per_epoch / ticks_per_slot;
let mut start_leader_index = None; let mut start_leader_index = None;
for i in 0..num_slots { for i in 0..num_slots {
let tick_height = 2 * seed_rotation_interval + i * leader_rotation_interval; let tick_height = 2 * ticks_per_epoch + i * ticks_per_slot;
info!("iteration {}: tick_height={}", i, tick_height); info!("iteration {}: tick_height={}", i, tick_height);
let slot = leader_scheduler.tick_height_to_slot(tick_height); let slot = leader_scheduler.tick_height_to_slot(tick_height);
let current_leader = leader_scheduler let current_leader = leader_scheduler
@ -605,11 +593,11 @@ pub mod tests {
assert_eq!(current_leader, expected_leader); assert_eq!(current_leader, expected_leader);
assert_eq!( assert_eq!(
slot, slot,
leader_scheduler.tick_height_to_slot(2 * seed_rotation_interval) + i leader_scheduler.tick_height_to_slot(2 * ticks_per_epoch) + i
); );
assert_eq!( assert_eq!(
slot, slot,
leader_scheduler.tick_height_to_slot(tick_height + leader_rotation_interval - 1) leader_scheduler.tick_height_to_slot(tick_height + ticks_per_slot - 1)
); );
assert_eq!( assert_eq!(
leader_scheduler.get_leader_for_slot(slot), leader_scheduler.get_leader_for_slot(slot),
@ -620,7 +608,7 @@ pub mod tests {
#[test] #[test]
fn test_num_ticks_left_in_slot() { fn test_num_ticks_left_in_slot() {
let leader_scheduler = LeaderScheduler::new(&LeaderSchedulerConfig::new(10, 20, 0)); let leader_scheduler = LeaderScheduler::new(&LeaderSchedulerConfig::new(10, 2, 1));
assert_eq!(leader_scheduler.num_ticks_left_in_slot(0), 9); assert_eq!(leader_scheduler.num_ticks_left_in_slot(0), 9);
assert_eq!(leader_scheduler.num_ticks_left_in_slot(1), 8); assert_eq!(leader_scheduler.num_ticks_left_in_slot(1), 8);
@ -638,8 +626,8 @@ pub mod tests {
solana_logger::setup(); solana_logger::setup();
let leader_id = Keypair::new().pubkey(); let leader_id = Keypair::new().pubkey();
let active_window_length = 1000; let active_window_tick_length = 1000;
let leader_scheduler_config = LeaderSchedulerConfig::new(100, 100, active_window_length); let leader_scheduler_config = LeaderSchedulerConfig::new(100, 1, active_window_tick_length);
let (genesis_block, mint_keypair) = GenesisBlock::new_with_leader(10000, leader_id, 500); let (genesis_block, mint_keypair) = GenesisBlock::new_with_leader(10000, leader_id, 500);
let bank = Bank::new_with_leader_scheduler_config(&genesis_block, &leader_scheduler_config); let bank = Bank::new_with_leader_scheduler_config(&genesis_block, &leader_scheduler_config);
@ -677,7 +665,7 @@ pub mod tests {
); );
} }
// Insert a bunch of votes at height "start_height + active_window_length" // Insert a bunch of votes at height "start_height + active_window_tick_length"
let num_new_ids = 10; let num_new_ids = 10;
let mut new_ids = HashSet::new(); let mut new_ids = HashSet::new();
for _ in 0..num_new_ids { for _ in 0..num_new_ids {
@ -701,7 +689,7 @@ pub mod tests {
push_vote( push_vote(
&voting_keypair, &voting_keypair,
&bank, &bank,
start_height + active_window_length + 1, start_height + active_window_tick_length + 1,
genesis_block.last_id(), genesis_block.last_id(),
); );
} }
@ -716,26 +704,27 @@ pub mod tests {
assert_eq!(result, bootstrap_ids); assert_eq!(result, bootstrap_ids);
let result = let result =
leader_scheduler.get_active_set(active_window_length + start_height - 1, &bank); leader_scheduler.get_active_set(active_window_tick_length + start_height - 1, &bank);
assert_eq!(result, old_ids);
let result = leader_scheduler.get_active_set(active_window_length + start_height, &bank);
assert_eq!(result, old_ids); assert_eq!(result, old_ids);
let result = let result =
leader_scheduler.get_active_set(active_window_length + start_height + 1, &bank); leader_scheduler.get_active_set(active_window_tick_length + start_height, &bank);
assert_eq!(result, old_ids);
let result =
leader_scheduler.get_active_set(active_window_tick_length + start_height + 1, &bank);
assert_eq!(result, new_ids); assert_eq!(result, new_ids);
let result = let result =
leader_scheduler.get_active_set(2 * active_window_length + start_height, &bank); leader_scheduler.get_active_set(2 * active_window_tick_length + start_height, &bank);
assert_eq!(result, new_ids); assert_eq!(result, new_ids);
let result = let result = leader_scheduler
leader_scheduler.get_active_set(2 * active_window_length + start_height + 1, &bank); .get_active_set(2 * active_window_tick_length + start_height + 1, &bank);
assert_eq!(result, new_ids); assert_eq!(result, new_ids);
let result = let result = leader_scheduler
leader_scheduler.get_active_set(2 * active_window_length + start_height + 2, &bank); .get_active_set(2 * active_window_tick_length + start_height + 2, &bank);
assert!(result.is_empty()); assert!(result.is_empty());
} }
@ -885,52 +874,36 @@ pub mod tests {
fn test_scheduler_basic() { fn test_scheduler_basic() {
solana_logger::setup(); solana_logger::setup();
// Test when the number of validators equals // Test when the number of validators equals
// seed_rotation_interval / leader_rotation_interval, so each validator // ticks_per_epoch / ticks_per_slot, so each validator
// is selected once // is selected once
let mut num_validators = 100; let mut num_validators = 100;
let mut leader_rotation_interval = 100; let mut ticks_per_slot = 100;
let mut seed_rotation_interval = leader_rotation_interval * num_validators as u64; let mut ticks_per_epoch = ticks_per_slot * num_validators as u64;
run_scheduler_test( run_scheduler_test(num_validators, ticks_per_slot, ticks_per_epoch);
num_validators,
leader_rotation_interval,
seed_rotation_interval,
);
// Test when there are fewer validators than // Test when there are fewer validators than
// seed_rotation_interval / leader_rotation_interval, so each validator // ticks_per_epoch / ticks_per_slot, so each validator
// is selected multiple times // is selected multiple times
num_validators = 3; num_validators = 3;
leader_rotation_interval = 100; ticks_per_slot = 100;
seed_rotation_interval = 1000; ticks_per_epoch = 1000;
run_scheduler_test( run_scheduler_test(num_validators, ticks_per_slot, ticks_per_epoch);
num_validators,
leader_rotation_interval,
seed_rotation_interval,
);
// Test when there are fewer number of validators than // Test when there are fewer number of validators than
// seed_rotation_interval / leader_rotation_interval, so each validator // ticks_per_epoch / ticks_per_slot, so each validator
// may not be selected // may not be selected
num_validators = 10; num_validators = 10;
leader_rotation_interval = 100; ticks_per_slot = 100;
seed_rotation_interval = 200; ticks_per_epoch = 200;
run_scheduler_test( run_scheduler_test(num_validators, ticks_per_slot, ticks_per_epoch);
num_validators,
leader_rotation_interval,
seed_rotation_interval,
);
// Test when seed_rotation_interval == leader_rotation_interval, // Test when ticks_per_epoch == ticks_per_slot,
// only one validator should be selected // only one validator should be selected
num_validators = 10; num_validators = 10;
leader_rotation_interval = 2; ticks_per_slot = 2;
seed_rotation_interval = 2; ticks_per_epoch = 2;
run_scheduler_test( run_scheduler_test(num_validators, ticks_per_slot, ticks_per_epoch);
num_validators,
leader_rotation_interval,
seed_rotation_interval,
);
} }
#[test] #[test]
@ -940,18 +913,15 @@ pub mod tests {
let num_validators = 10; let num_validators = 10;
let num_vote_account_tokens = 1; let num_vote_account_tokens = 1;
// Make sure seed_rotation_interval is big enough so we select all the // Make sure ticks_per_epoch is big enough so we select all the
// validators as part of the schedule each time (we need to check the active window // validators as part of the schedule each time (we need to check the active window
// is the cause of validators being truncated later) // is the cause of validators being truncated later)
let leader_rotation_interval = 100; let ticks_per_slot = 100;
let seed_rotation_interval = leader_rotation_interval * num_validators; let slots_per_epoch = num_validators;
let active_window_length = seed_rotation_interval; let active_window_tick_length = ticks_per_slot * slots_per_epoch;
let leader_scheduler_config = LeaderSchedulerConfig::new( let leader_scheduler_config =
leader_rotation_interval, LeaderSchedulerConfig::new(ticks_per_slot, slots_per_epoch, active_window_tick_length);
seed_rotation_interval,
active_window_length,
);
// Create the bank and validators // Create the bank and validators
let (genesis_block, mint_keypair) = GenesisBlock::new( let (genesis_block, mint_keypair) = GenesisBlock::new(
@ -988,12 +958,12 @@ pub mod tests {
push_vote( push_vote(
&voting_keypair, &voting_keypair,
&bank, &bank,
(i + 2) * active_window_length - 1, (i + 2) * active_window_tick_length - 1,
genesis_block.last_id(), genesis_block.last_id(),
); );
} }
// Generate schedule every active_window_length entries and check that // Generate schedule every active_window_tick_length entries and check that
// validators are falling out of the rotation as they fall out of the // validators are falling out of the rotation as they fall out of the
// active set // active set
let mut leader_scheduler = bank.leader_scheduler.write().unwrap(); let mut leader_scheduler = bank.leader_scheduler.write().unwrap();
@ -1006,7 +976,7 @@ pub mod tests {
assert_eq!(leader_scheduler.current_epoch, 1); assert_eq!(leader_scheduler.current_epoch, 1);
for i in 0..=num_validators { for i in 0..=num_validators {
info!("i === {}", i); info!("i === {}", i);
leader_scheduler.generate_schedule((i + 1) * active_window_length, &bank); leader_scheduler.generate_schedule((i + 1) * active_window_tick_length, &bank);
assert_eq!(leader_scheduler.current_epoch, i + 2); assert_eq!(leader_scheduler.current_epoch, i + 2);
if i == 0 { if i == 0 {
assert_eq!( assert_eq!(
@ -1026,9 +996,9 @@ pub mod tests {
fn test_multiple_vote() { fn test_multiple_vote() {
let leader_keypair = Arc::new(Keypair::new()); let leader_keypair = Arc::new(Keypair::new());
let leader_id = leader_keypair.pubkey(); let leader_id = leader_keypair.pubkey();
let active_window_length = 1000; let active_window_tick_length = 1000;
let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(10000, leader_id, 500); let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(10000, leader_id, 500);
let leader_scheduler_config = LeaderSchedulerConfig::new(100, 100, active_window_length); let leader_scheduler_config = LeaderSchedulerConfig::new(100, 1, active_window_tick_length);
let bank = Bank::new_with_leader_scheduler_config(&genesis_block, &leader_scheduler_config); let bank = Bank::new_with_leader_scheduler_config(&genesis_block, &leader_scheduler_config);
// Bootstrap leader should be in the active set even without explicit votes // Bootstrap leader should be in the active set even without explicit votes
@ -1037,10 +1007,10 @@ pub mod tests {
let result = leader_scheduler.get_active_set(0, &bank); let result = leader_scheduler.get_active_set(0, &bank);
assert_eq!(result, to_hashset_owned(&vec![leader_id])); assert_eq!(result, to_hashset_owned(&vec![leader_id]));
let result = leader_scheduler.get_active_set(active_window_length, &bank); let result = leader_scheduler.get_active_set(active_window_tick_length, &bank);
assert_eq!(result, to_hashset_owned(&vec![leader_id])); assert_eq!(result, to_hashset_owned(&vec![leader_id]));
let result = leader_scheduler.get_active_set(active_window_length + 1, &bank); let result = leader_scheduler.get_active_set(active_window_tick_length + 1, &bank);
assert!(result.is_empty()); assert!(result.is_empty());
} }
@ -1062,10 +1032,10 @@ pub mod tests {
{ {
let mut leader_scheduler = bank.leader_scheduler.write().unwrap(); let mut leader_scheduler = bank.leader_scheduler.write().unwrap();
let result = leader_scheduler.get_active_set(active_window_length + 1, &bank); let result = leader_scheduler.get_active_set(active_window_tick_length + 1, &bank);
assert_eq!(result, to_hashset_owned(&vec![leader_id])); assert_eq!(result, to_hashset_owned(&vec![leader_id]));
let result = leader_scheduler.get_active_set(active_window_length + 2, &bank); let result = leader_scheduler.get_active_set(active_window_tick_length + 2, &bank);
assert!(result.is_empty()); assert!(result.is_empty());
} }
@ -1074,10 +1044,10 @@ pub mod tests {
{ {
let mut leader_scheduler = bank.leader_scheduler.write().unwrap(); let mut leader_scheduler = bank.leader_scheduler.write().unwrap();
let result = leader_scheduler.get_active_set(active_window_length + 2, &bank); let result = leader_scheduler.get_active_set(active_window_tick_length + 2, &bank);
assert_eq!(result, to_hashset_owned(&vec![leader_id])); assert_eq!(result, to_hashset_owned(&vec![leader_id]));
let result = leader_scheduler.get_active_set(active_window_length + 3, &bank); let result = leader_scheduler.get_active_set(active_window_tick_length + 3, &bank);
assert!(result.is_empty()); assert!(result.is_empty());
} }
} }
@ -1086,15 +1056,13 @@ pub mod tests {
fn test_update_tick_height() { fn test_update_tick_height() {
solana_logger::setup(); solana_logger::setup();
let leader_rotation_interval = 100; let ticks_per_slot = 100;
let seed_rotation_interval = 2 * leader_rotation_interval; let slots_per_epoch = 2;
let active_window_length = 1; let ticks_per_epoch = ticks_per_slot * slots_per_epoch;
let active_window_tick_length = 1;
let leader_scheduler_config = LeaderSchedulerConfig::new( let leader_scheduler_config =
leader_rotation_interval, LeaderSchedulerConfig::new(ticks_per_slot, slots_per_epoch, active_window_tick_length);
seed_rotation_interval,
active_window_length,
);
// Check that the generate_schedule() function is being called by the // Check that the generate_schedule() function is being called by the
// update_tick_height() function at the correct entry heights. // update_tick_height() function at the correct entry heights.
@ -1129,10 +1097,10 @@ pub mod tests {
// //
for tick_height in &[ for tick_height in &[
1, 1,
leader_rotation_interval, ticks_per_slot,
leader_rotation_interval + 1, ticks_per_slot + 1,
seed_rotation_interval - 1, ticks_per_epoch - 1,
seed_rotation_interval, ticks_per_epoch,
] { ] {
info!("Checking tick_height {}", *tick_height); info!("Checking tick_height {}", *tick_height);
leader_scheduler.update_tick_height(*tick_height, &bank); leader_scheduler.update_tick_height(*tick_height, &bank);
@ -1163,11 +1131,11 @@ pub mod tests {
// Check various tick heights in epoch 1, and tick 0 of epoch 2 // Check various tick heights in epoch 1, and tick 0 of epoch 2
// //
for tick_height in &[ for tick_height in &[
seed_rotation_interval + 1, ticks_per_epoch + 1,
seed_rotation_interval + leader_rotation_interval, ticks_per_epoch + ticks_per_slot,
seed_rotation_interval + leader_rotation_interval + 1, ticks_per_epoch + ticks_per_slot + 1,
seed_rotation_interval + seed_rotation_interval - 1, ticks_per_epoch + ticks_per_epoch - 1,
seed_rotation_interval + seed_rotation_interval, ticks_per_epoch + ticks_per_epoch,
] { ] {
info!("Checking tick_height {}", *tick_height); info!("Checking tick_height {}", *tick_height);
leader_scheduler.update_tick_height(*tick_height, &bank); leader_scheduler.update_tick_height(*tick_height, &bank);
@ -1205,50 +1173,37 @@ pub mod tests {
let leader_scheduler = LeaderScheduler::new(&leader_scheduler_config); let leader_scheduler = LeaderScheduler::new(&leader_scheduler_config);
assert_eq!(leader_scheduler.ticks_per_slot, DEFAULT_TICKS_PER_SLOT);
assert_eq!( assert_eq!(
leader_scheduler.leader_rotation_interval, leader_scheduler.ticks_per_epoch,
DEFAULT_TICKS_PER_SLOT DEFAULT_TICKS_PER_SLOT * DEFAULT_SLOTS_PER_EPOCH
);
assert_eq!(
leader_scheduler.seed_rotation_interval,
DEFAULT_SEED_ROTATION_INTERVAL
); );
// Check actual arguments for LeaderScheduler // Check actual arguments for LeaderScheduler
let leader_rotation_interval = 100; let ticks_per_slot = 100;
let seed_rotation_interval = 200; let slots_per_epoch = 2;
let active_window_length = 1; let active_window_tick_length = 1;
let leader_scheduler_config = LeaderSchedulerConfig::new( let leader_scheduler_config =
leader_rotation_interval, LeaderSchedulerConfig::new(ticks_per_slot, slots_per_epoch, active_window_tick_length);
seed_rotation_interval,
active_window_length,
);
let leader_scheduler = LeaderScheduler::new(&leader_scheduler_config); let leader_scheduler = LeaderScheduler::new(&leader_scheduler_config);
assert_eq!(leader_scheduler.ticks_per_slot, ticks_per_slot);
assert_eq!( assert_eq!(
leader_scheduler.leader_rotation_interval, leader_scheduler.ticks_per_epoch,
leader_rotation_interval ticks_per_slot * slots_per_epoch
);
assert_eq!(
leader_scheduler.seed_rotation_interval,
seed_rotation_interval
); );
} }
fn run_consecutive_leader_test(num_slots_per_epoch: u64, add_validator: bool) { fn run_consecutive_leader_test(slots_per_epoch: u64, add_validator: bool) {
let bootstrap_leader_keypair = Arc::new(Keypair::new()); let bootstrap_leader_keypair = Arc::new(Keypair::new());
let bootstrap_leader_id = bootstrap_leader_keypair.pubkey(); let bootstrap_leader_id = bootstrap_leader_keypair.pubkey();
let leader_rotation_interval = 100; let ticks_per_slot = 100;
let seed_rotation_interval = num_slots_per_epoch * leader_rotation_interval; let active_window_tick_length = slots_per_epoch * ticks_per_slot;
let active_window_length = seed_rotation_interval;
let leader_scheduler_config = LeaderSchedulerConfig::new( let leader_scheduler_config =
leader_rotation_interval, LeaderSchedulerConfig::new(ticks_per_slot, slots_per_epoch, active_window_tick_length);
seed_rotation_interval,
active_window_length,
);
// Create mint and bank // Create mint and bank
let (genesis_block, mint_keypair) = let (genesis_block, mint_keypair) =

View File

@ -100,11 +100,7 @@ impl ReplayStage {
inc_new_counter_info!("replicate-stage_bank-tick", bank.tick_height() as usize); inc_new_counter_info!("replicate-stage_bank-tick", bank.tick_height() as usize);
if entry.is_tick() { if entry.is_tick() {
if num_ticks_to_next_vote == 0 { if num_ticks_to_next_vote == 0 {
num_ticks_to_next_vote = bank num_ticks_to_next_vote = bank.leader_scheduler.read().unwrap().ticks_per_slot;
.leader_scheduler
.read()
.unwrap()
.leader_rotation_interval;
} }
num_ticks_to_next_vote -= 1; num_ticks_to_next_vote -= 1;
} }
@ -205,8 +201,7 @@ impl ReplayStage {
let tick_height = bank.tick_height(); let tick_height = bank.tick_height();
let leader_scheduler = bank.leader_scheduler.read().unwrap(); let leader_scheduler = bank.leader_scheduler.read().unwrap();
let current_slot = leader_scheduler.tick_height_to_slot(tick_height + 1); let current_slot = leader_scheduler.tick_height_to_slot(tick_height + 1);
let first_tick_in_current_slot = let first_tick_in_current_slot = current_slot * leader_scheduler.ticks_per_slot;
current_slot * leader_scheduler.leader_rotation_interval;
( (
current_slot, current_slot,
first_tick_in_current_slot first_tick_in_current_slot
@ -274,11 +269,8 @@ impl ReplayStage {
} }
current_slot += 1; current_slot += 1;
max_tick_height_for_slot += bank max_tick_height_for_slot +=
.leader_scheduler bank.leader_scheduler.read().unwrap().ticks_per_slot;
.read()
.unwrap()
.leader_rotation_interval;
last_leader_id = leader_id; last_leader_id = leader_id;
} }
} }
@ -383,19 +375,15 @@ mod test {
info!("old_leader_id: {:?}", old_leader_id); info!("old_leader_id: {:?}", old_leader_id);
// Set up the LeaderScheduler so that my_id becomes the leader for epoch 1 // Set up the LeaderScheduler so that my_id becomes the leader for epoch 1
let leader_rotation_interval = 16; let ticks_per_slot = 16;
let leader_scheduler_config = LeaderSchedulerConfig::new( let leader_scheduler_config = LeaderSchedulerConfig::new(ticks_per_slot, 1, ticks_per_slot);
leader_rotation_interval,
leader_rotation_interval,
leader_rotation_interval,
);
let my_keypair = Arc::new(my_keypair); let my_keypair = Arc::new(my_keypair);
let (active_set_entries, voting_keypair) = make_active_set_entries( let (active_set_entries, voting_keypair) = make_active_set_entries(
&my_keypair, &my_keypair,
&mint_keypair, &mint_keypair,
100, 100,
leader_rotation_interval, // add a vote for tick_height = leader_rotation_interval ticks_per_slot, // add a vote for tick_height = ticks_per_slot
&last_id, &last_id,
&last_id, &last_id,
0, 0,
@ -438,7 +426,7 @@ mod test {
l_receiver, l_receiver,
); );
let total_entries_to_send = 2 * leader_rotation_interval as usize - 2; let total_entries_to_send = 2 * ticks_per_slot as usize - 2;
let mut entries_to_send = vec![]; let mut entries_to_send = vec![];
while entries_to_send.len() < total_entries_to_send { while entries_to_send.len() < total_entries_to_send {
let entry = Entry::new(&mut last_id, 0, 1, vec![]); let entry = Entry::new(&mut last_id, 0, 1, vec![]);
@ -457,7 +445,7 @@ mod test {
info!("Wait for replay_stage to exit and check return value is correct"); info!("Wait for replay_stage to exit and check return value is correct");
assert_eq!( assert_eq!(
Some(TvuReturnType::LeaderRotation( Some(TvuReturnType::LeaderRotation(
2 * leader_rotation_interval - 1, 2 * ticks_per_slot - 1,
expected_entry_height, expected_entry_height,
expected_last_id, expected_last_id,
)), )),
@ -607,18 +595,11 @@ mod test {
.unwrap(); .unwrap();
} }
// Set up the LeaderScheduler so that this this node becomes the leader at let ticks_per_slot = 10;
// bootstrap_height = num_bootstrap_slots * leader_rotation_interval let slots_per_epoch = 2;
// Set up the LeaderScheduler so that this this node becomes the leader at let active_window_tick_length = ticks_per_slot * slots_per_epoch;
// bootstrap_height = num_bootstrap_slots * leader_rotation_interval let leader_scheduler_config =
let leader_rotation_interval = 10; LeaderSchedulerConfig::new(ticks_per_slot, slots_per_epoch, active_window_tick_length);
let num_bootstrap_slots = 2;
let bootstrap_height = num_bootstrap_slots * leader_rotation_interval;
let leader_scheduler_config = LeaderSchedulerConfig::new(
leader_rotation_interval,
leader_rotation_interval * 2,
bootstrap_height,
);
// Set up the cluster info // Set up the cluster info
let cluster_info_me = Arc::new(RwLock::new(ClusterInfo::new(my_node.info.clone()))); let cluster_info_me = Arc::new(RwLock::new(ClusterInfo::new(my_node.info.clone())));
@ -658,14 +639,15 @@ mod test {
cluster_info_me.write().unwrap().push_vote(vote); cluster_info_me.write().unwrap().push_vote(vote);
// Send enough ticks to trigger leader rotation // Send enough ticks to trigger leader rotation
let total_entries_to_send = (bootstrap_height - initial_tick_height) as usize; let total_entries_to_send = (active_window_tick_length - initial_tick_height) as usize;
let num_hashes = 1; let num_hashes = 1;
// Add on the only entries that weren't ticks to the bootstrap height to get the // Add on the only entries that weren't ticks to the bootstrap height to get the
// total expected entry length // total expected entry length
let expected_entry_height = let expected_entry_height =
bootstrap_height + initial_non_tick_height + active_set_entries_len; active_window_tick_length + initial_non_tick_height + active_set_entries_len;
let leader_rotation_index = (bootstrap_height - initial_tick_height - 1) as usize; let leader_rotation_index =
(active_window_tick_length - initial_tick_height - 1) as usize;
let mut expected_last_id = Hash::default(); let mut expected_last_id = Hash::default();
for i in 0..total_entries_to_send { for i in 0..total_entries_to_send {
let entry = Entry::new(&mut last_id, 0, num_hashes, vec![]); let entry = Entry::new(&mut last_id, 0, num_hashes, vec![]);
@ -691,7 +673,7 @@ mod test {
// Wait for replay_stage to exit and check return value is correct // Wait for replay_stage to exit and check return value is correct
assert_eq!( assert_eq!(
Some(TvuReturnType::LeaderRotation( Some(TvuReturnType::LeaderRotation(
bootstrap_height, active_window_tick_length,
expected_entry_height, expected_entry_height,
expected_last_id, expected_last_id,
)), )),

View File

@ -140,7 +140,7 @@ impl WindowUtil for Window {
// 1) The replay stage hasn't caught up to the "consumed" entries we sent, // 1) The replay stage hasn't caught up to the "consumed" entries we sent,
// in which case it will eventually catch up // in which case it will eventually catch up
// //
// 2) We are on the border between seed_rotation_intervals, so the // 2) We are on the border between ticks_per_epochs, so the
// schedule won't be known until the entry on that cusp is received // schedule won't be known until the entry on that cusp is received
// by the replay stage (which comes after this stage). Hence, the next // by the replay stage (which comes after this stage). Hence, the next
// leader at the beginning of that next epoch will not know they are the // leader at the beginning of that next epoch will not know they are the

View File

@ -130,10 +130,8 @@ fn test_multi_node_ledger_window() -> result::Result<()> {
let fullnode_config = FullnodeConfig::default(); let fullnode_config = FullnodeConfig::default();
info!( info!(
"leader_rotation_interval: {}", "ticks_per_slot: {}",
fullnode_config fullnode_config.leader_scheduler_config.ticks_per_slot
.leader_scheduler_config
.leader_rotation_interval
); );
// Write some into leader's ledger, this should populate the leader's window // Write some into leader's ledger, this should populate the leader's window
@ -143,11 +141,7 @@ fn test_multi_node_ledger_window() -> result::Result<()> {
let db_ledger = DbLedger::open(&leader_ledger_path).unwrap(); let db_ledger = DbLedger::open(&leader_ledger_path).unwrap();
let entries = solana::entry::create_ticks( let entries = solana::entry::create_ticks(
fullnode_config fullnode_config.leader_scheduler_config.ticks_per_slot - last_entry_height - 2,
.leader_scheduler_config
.leader_rotation_interval
- last_entry_height
- 2,
last_entry_id, last_entry_id,
); );
db_ledger db_ledger
@ -931,14 +925,14 @@ fn test_leader_to_validator_transition() {
let leader_info = leader_node.info.clone(); let leader_info = leader_node.info.clone();
let mut fullnode_config = FullnodeConfig::default(); let mut fullnode_config = FullnodeConfig::default();
let leader_rotation_interval = 5; let ticks_per_slot = 5;
fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new( fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new(
leader_rotation_interval, ticks_per_slot,
leader_rotation_interval, 1,
// Setup window length to exclude the genesis bootstrap leader vote at tick height 0, so // Setup window length to exclude the genesis bootstrap leader vote at tick height 0, so
// that when the leader schedule is recomputed for epoch 1 only the validator vote at tick // that when the leader schedule is recomputed for epoch 1 only the validator vote at tick
// height 1 will be considered. // height 1 will be considered.
leader_rotation_interval, ticks_per_slot,
); );
// Initialize the leader ledger. Make a mint and a genesis entry // Initialize the leader ledger. Make a mint and a genesis entry
@ -958,7 +952,7 @@ fn test_leader_to_validator_transition() {
&validator_keypair, &validator_keypair,
&mint_keypair, &mint_keypair,
100, 100,
leader_rotation_interval, ticks_per_slot,
&last_id, &last_id,
&last_id, &last_id,
0, 0,
@ -993,13 +987,10 @@ fn test_leader_to_validator_transition() {
// slot 0 -> slot 1: bootstrap leader remains the leader // slot 0 -> slot 1: bootstrap leader remains the leader
// slot 1 -> slot 2: bootstrap leader to the validator // slot 1 -> slot 2: bootstrap leader to the validator
let expected_rotations = vec![ let expected_rotations = vec![
( (FullnodeReturnType::LeaderToLeaderRotation, ticks_per_slot),
FullnodeReturnType::LeaderToLeaderRotation,
leader_rotation_interval,
),
( (
FullnodeReturnType::LeaderToValidatorRotation, FullnodeReturnType::LeaderToValidatorRotation,
2 * leader_rotation_interval, 2 * ticks_per_slot,
), ),
]; ];
@ -1020,10 +1011,7 @@ fn test_leader_to_validator_transition() {
assert_eq!( assert_eq!(
bank.tick_height(), bank.tick_height(),
2 * fullnode_config 2 * fullnode_config.leader_scheduler_config.ticks_per_slot - 1
.leader_scheduler_config
.leader_rotation_interval
- 1
); );
remove_dir_all(leader_ledger_path).unwrap(); remove_dir_all(leader_ledger_path).unwrap();
} }
@ -1083,11 +1071,11 @@ fn test_leader_validator_basic() {
// Create the leader scheduler config // Create the leader scheduler config
let mut fullnode_config = FullnodeConfig::default(); let mut fullnode_config = FullnodeConfig::default();
let leader_rotation_interval = 5; let ticks_per_slot = 5;
fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new( fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new(
leader_rotation_interval, ticks_per_slot,
leader_rotation_interval, // 1 slot per epoch 1, // 1 slot per epoch
leader_rotation_interval, ticks_per_slot,
); );
// Start the validator node // Start the validator node
@ -1121,10 +1109,7 @@ fn test_leader_validator_basic() {
info!("Waiting for slot 0 -> slot 1: bootstrap leader will remain the leader"); info!("Waiting for slot 0 -> slot 1: bootstrap leader will remain the leader");
assert_eq!( assert_eq!(
leader_rotation_receiver.recv().unwrap(), leader_rotation_receiver.recv().unwrap(),
( (FullnodeReturnType::LeaderToLeaderRotation, ticks_per_slot,)
FullnodeReturnType::LeaderToLeaderRotation,
leader_rotation_interval,
)
); );
info!("Waiting for slot 1 -> slot 2: bootstrap leader becomes a validator"); info!("Waiting for slot 1 -> slot 2: bootstrap leader becomes a validator");
@ -1132,7 +1117,7 @@ fn test_leader_validator_basic() {
leader_rotation_receiver.recv().unwrap(), leader_rotation_receiver.recv().unwrap(),
( (
FullnodeReturnType::LeaderToValidatorRotation, FullnodeReturnType::LeaderToValidatorRotation,
leader_rotation_interval * 2, ticks_per_slot * 2,
) )
); );
@ -1141,7 +1126,7 @@ fn test_leader_validator_basic() {
validator_rotation_receiver.recv().unwrap(), validator_rotation_receiver.recv().unwrap(),
( (
FullnodeReturnType::ValidatorToLeaderRotation, FullnodeReturnType::ValidatorToLeaderRotation,
leader_rotation_interval * 2, ticks_per_slot * 2,
) )
); );
@ -1155,7 +1140,7 @@ fn test_leader_validator_basic() {
let validator_entries: Vec<Entry> = read_ledger(&validator_ledger_path); let validator_entries: Vec<Entry> = read_ledger(&validator_ledger_path);
let leader_entries = read_ledger(&leader_ledger_path); let leader_entries = read_ledger(&leader_ledger_path);
assert!(leader_entries.len() as u64 >= leader_rotation_interval); assert!(leader_entries.len() as u64 >= ticks_per_slot);
for (v, l) in validator_entries.iter().zip(leader_entries) { for (v, l) in validator_entries.iter().zip(leader_entries) {
assert_eq!(*v, l); assert_eq!(*v, l);
@ -1182,15 +1167,12 @@ fn test_dropped_handoff_recovery() {
let bootstrap_leader_info = bootstrap_leader_node.info.clone(); let bootstrap_leader_info = bootstrap_leader_node.info.clone();
// Create the common leader scheduling configuration // Create the common leader scheduling configuration
let num_slots_per_epoch = (N + 1) as u64; let slots_per_epoch = (N + 1) as u64;
let leader_rotation_interval = 5; let ticks_per_slot = 5;
let seed_rotation_interval = num_slots_per_epoch * leader_rotation_interval; let ticks_per_epoch = slots_per_epoch * ticks_per_slot;
let mut fullnode_config = FullnodeConfig::default(); let mut fullnode_config = FullnodeConfig::default();
fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new( fullnode_config.leader_scheduler_config =
leader_rotation_interval, LeaderSchedulerConfig::new(ticks_per_slot, slots_per_epoch, ticks_per_epoch);
seed_rotation_interval,
seed_rotation_interval,
);
// Make a common mint and a genesis entry for both leader + validator's ledgers // Make a common mint and a genesis entry for both leader + validator's ledgers
let num_ending_ticks = 1; let num_ending_ticks = 1;
@ -1218,7 +1200,7 @@ fn test_dropped_handoff_recovery() {
&next_leader_keypair, &next_leader_keypair,
&mint_keypair, &mint_keypair,
100, 100,
leader_rotation_interval, ticks_per_slot,
&last_id, &last_id,
&last_id, &last_id,
0, 0,
@ -1399,15 +1381,12 @@ fn test_full_leader_validator_network() {
} }
// Create the common leader scheduling configuration // Create the common leader scheduling configuration
let num_slots_per_epoch = (N + 1) as u64; let slots_per_epoch = (N + 1) as u64;
let leader_rotation_interval = 5; let ticks_per_slot = 5;
let seed_rotation_interval = num_slots_per_epoch * leader_rotation_interval; let ticks_per_epoch = slots_per_epoch * ticks_per_slot;
let mut fullnode_config = FullnodeConfig::default(); let mut fullnode_config = FullnodeConfig::default();
fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new( fullnode_config.leader_scheduler_config =
leader_rotation_interval, LeaderSchedulerConfig::new(ticks_per_slot, slots_per_epoch, ticks_per_epoch);
seed_rotation_interval,
seed_rotation_interval,
);
let mut nodes = vec![]; let mut nodes = vec![];
@ -1538,13 +1517,7 @@ fn test_full_leader_validator_network() {
} }
let shortest = shortest.unwrap(); let shortest = shortest.unwrap();
assert!( assert!(shortest >= fullnode_config.leader_scheduler_config.ticks_per_slot * 3,);
shortest
>= fullnode_config
.leader_scheduler_config
.leader_rotation_interval
* 3,
);
for path in ledger_paths { for path in ledger_paths {
DbLedger::destroy(&path).expect("Expected successful database destruction"); DbLedger::destroy(&path).expect("Expected successful database destruction");
@ -1598,18 +1571,16 @@ fn test_broadcast_last_tick() {
}) })
.collect(); .collect();
let leader_rotation_interval = 40; let ticks_per_slot = 40;
let seed_rotation_interval = 2 * leader_rotation_interval; let slots_per_epoch = 2;
let ticks_per_epoch = slots_per_epoch * ticks_per_slot;
// Start up the bootstrap leader fullnode // Start up the bootstrap leader fullnode
let bootstrap_leader_keypair = Arc::new(bootstrap_leader_keypair); let bootstrap_leader_keypair = Arc::new(bootstrap_leader_keypair);
let voting_keypair = VotingKeypair::new_local(&bootstrap_leader_keypair); let voting_keypair = VotingKeypair::new_local(&bootstrap_leader_keypair);
let mut fullnode_config = FullnodeConfig::default(); let mut fullnode_config = FullnodeConfig::default();
fullnode_config.leader_scheduler_config = LeaderSchedulerConfig::new( fullnode_config.leader_scheduler_config =
leader_rotation_interval, LeaderSchedulerConfig::new(ticks_per_slot, slots_per_epoch, ticks_per_epoch);
seed_rotation_interval,
seed_rotation_interval,
);
let bootstrap_leader = Fullnode::new( let bootstrap_leader = Fullnode::new(
bootstrap_leader_node, bootstrap_leader_node,
&bootstrap_leader_keypair, &bootstrap_leader_keypair,
@ -1638,8 +1609,8 @@ fn test_broadcast_last_tick() {
info!("Shutting down the leader..."); info!("Shutting down the leader...");
bootstrap_leader_exit(); bootstrap_leader_exit();
// Index of the last tick must be at least leader_rotation_interval - 1 // Index of the last tick must be at least ticks_per_slot - 1
let last_tick_entry_index = leader_rotation_interval as usize - 2; let last_tick_entry_index = ticks_per_slot as usize - 2;
let entries = read_ledger(&bootstrap_leader_ledger_path); let entries = read_ledger(&bootstrap_leader_ledger_path);
assert!(entries.len() >= last_tick_entry_index + 1); assert!(entries.len() >= last_tick_entry_index + 1);
let expected_last_tick = &entries[last_tick_entry_index]; let expected_last_tick = &entries[last_tick_entry_index];