solana_runtime/
leader_schedule_utils.rs1use {
2 crate::bank::Bank,
3 solana_clock::{Epoch, Slot},
4 solana_epoch_schedule::EpochSchedule,
5 solana_leader_schedule::{LeaderSchedule, NUM_CONSECUTIVE_LEADER_SLOTS},
6 solana_pubkey::Pubkey,
7 solana_vote::vote_account::VoteAccountsHashMap,
8 std::collections::HashMap,
9};
10
11pub fn leader_schedule(epoch: Epoch, bank: &Bank) -> Option<LeaderSchedule> {
13 leader_schedule_from_vote_accounts(
14 epoch,
15 bank.epoch_schedule(),
16 bank.epoch_vote_accounts(epoch)?,
17 )
18}
19
20pub fn leader_schedule_from_vote_accounts(
24 epoch: Epoch,
25 epoch_schedule: &EpochSchedule,
26 epoch_vote_accounts: &VoteAccountsHashMap,
27) -> Option<LeaderSchedule> {
28 Some(LeaderSchedule::new(
29 epoch_vote_accounts,
30 epoch,
31 epoch_schedule
32 .get_slots_in_epoch(epoch)
33 .try_into()
34 .expect("number of slots in epoch must fit in usize"),
35 NUM_CONSECUTIVE_LEADER_SLOTS,
36 ))
37}
38
39pub type LeaderScheduleByIdentity = HashMap<String, Vec<usize>>;
41
42pub fn leader_schedule_by_identity<'a>(
43 upcoming_leaders: impl Iterator<Item = (usize, &'a Pubkey)>,
44) -> LeaderScheduleByIdentity {
45 let mut leader_schedule_by_identity = HashMap::new();
46
47 for (slot_index, identity_pubkey) in upcoming_leaders {
48 leader_schedule_by_identity
49 .entry(identity_pubkey)
50 .or_insert_with(Vec::new)
51 .push(slot_index);
52 }
53
54 leader_schedule_by_identity
55 .into_iter()
56 .map(|(identity_pubkey, slot_indices)| (identity_pubkey.to_string(), slot_indices))
57 .collect()
58}
59
60pub fn slot_leader_at(slot: Slot, bank: &Bank) -> Option<Pubkey> {
62 let (epoch, slot_index) = bank.get_epoch_and_slot_index(slot);
63
64 leader_schedule(epoch, bank).map(|leader_schedule| leader_schedule[slot_index].id)
65}
66
67pub fn num_ticks_left_in_slot(bank: &Bank, tick_height: u64) -> u64 {
70 bank.ticks_per_slot() - tick_height % bank.ticks_per_slot()
71}
72
73pub fn first_of_consecutive_leader_slots(slot: Slot) -> Slot {
74 let num_consecutive_leader_slots = NUM_CONSECUTIVE_LEADER_SLOTS.get() as u64;
75 (slot / num_consecutive_leader_slots) * num_consecutive_leader_slots
76}
77
78#[inline]
80pub fn last_of_consecutive_leader_slots(slot: Slot) -> Slot {
81 first_of_consecutive_leader_slots(slot) + NUM_CONSECUTIVE_LEADER_SLOTS.get() as u64 - 1
82}
83
84#[inline]
86pub fn leader_slot_index(slot: Slot) -> usize {
87 slot as usize % NUM_CONSECUTIVE_LEADER_SLOTS
88}
89
90#[inline]
93pub fn remaining_slots_in_window(slot: Slot) -> usize {
94 NUM_CONSECUTIVE_LEADER_SLOTS
95 .get()
96 .checked_sub(leader_slot_index(slot))
97 .unwrap()
98}
99
100#[cfg(test)]
101mod tests {
102 use {
103 super::*,
104 crate::genesis_utils::{
105 bootstrap_validator_stake_lamports, create_genesis_config_with_leader,
106 },
107 };
108
109 #[test]
110 fn test_leader_schedule_via_bank() {
111 let pubkey = solana_pubkey::new_rand();
112 let genesis_config =
113 create_genesis_config_with_leader(0, &pubkey, bootstrap_validator_stake_lamports())
114 .genesis_config;
115
116 let bank = Bank::new_for_tests(&genesis_config);
117 let leader_schedule = leader_schedule(0, &bank).unwrap();
118
119 assert_eq!(leader_schedule[0].id, pubkey);
120 assert_eq!(leader_schedule[1].id, pubkey);
121 assert_eq!(leader_schedule[2].id, pubkey);
122 }
123
124 #[test]
125 fn test_leader_scheduler1_basic() {
126 let pubkey = solana_pubkey::new_rand();
127 let genesis_config =
128 create_genesis_config_with_leader(42, &pubkey, bootstrap_validator_stake_lamports())
129 .genesis_config;
130 let bank = Bank::new_for_tests(&genesis_config);
131 assert_eq!(slot_leader_at(bank.slot(), &bank).unwrap(), pubkey);
132 }
133
134 #[test]
135 fn test_leader_span_math() {
136 assert_eq!(NUM_CONSECUTIVE_LEADER_SLOTS.get(), 4);
139
140 assert_eq!(first_of_consecutive_leader_slots(0), 0);
141 assert_eq!(first_of_consecutive_leader_slots(1), 0);
142 assert_eq!(first_of_consecutive_leader_slots(2), 0);
143 assert_eq!(first_of_consecutive_leader_slots(3), 0);
144 assert_eq!(first_of_consecutive_leader_slots(4), 4);
145
146 assert_eq!(last_of_consecutive_leader_slots(0), 3);
147 assert_eq!(last_of_consecutive_leader_slots(1), 3);
148 assert_eq!(last_of_consecutive_leader_slots(2), 3);
149 assert_eq!(last_of_consecutive_leader_slots(3), 3);
150 assert_eq!(last_of_consecutive_leader_slots(4), 7);
151
152 assert_eq!(leader_slot_index(0), 0);
153 assert_eq!(leader_slot_index(1), 1);
154 assert_eq!(leader_slot_index(2), 2);
155 assert_eq!(leader_slot_index(3), 3);
156 assert_eq!(leader_slot_index(4), 0);
157 assert_eq!(leader_slot_index(5), 1);
158 assert_eq!(leader_slot_index(6), 2);
159 assert_eq!(leader_slot_index(7), 3);
160
161 assert_eq!(remaining_slots_in_window(0), 4);
162 assert_eq!(remaining_slots_in_window(1), 3);
163 assert_eq!(remaining_slots_in_window(2), 2);
164 assert_eq!(remaining_slots_in_window(3), 1);
165 assert_eq!(remaining_slots_in_window(4), 4);
166 }
167}