-
Notifications
You must be signed in to change notification settings - Fork 5.7k
[Obsoleted] [wip] Retain N epochs worth of epoch_stakes #7098
Changes from all commits
91baaea
7df99cb
aabc0a3
eff19e6
c60c1fd
358046f
677818b
25ecb99
2380be3
ba07af6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| use crate::{blocktree::Blocktree, leader_schedule::LeaderSchedule, leader_schedule_utils}; | ||
| use log::*; | ||
| use solana_runtime::bank::Bank; | ||
| use solana_runtime::bank::{Bank, MAX_LEADER_SCHEDULE_STAKES}; | ||
| use solana_sdk::{ | ||
| clock::{Epoch, Slot}, | ||
| epoch_schedule::EpochSchedule, | ||
|
|
@@ -48,7 +48,9 @@ impl LeaderScheduleCache { | |
|
|
||
| // Calculate the schedule for all epochs between 0 and leader_schedule_epoch(root) | ||
| let leader_schedule_epoch = epoch_schedule.get_leader_schedule_epoch(root_bank.slot()); | ||
| for epoch in 0..leader_schedule_epoch { | ||
| for epoch in (leader_schedule_epoch.max(MAX_LEADER_SCHEDULE_STAKES) | ||
| - MAX_LEADER_SCHEDULE_STAKES)..leader_schedule_epoch | ||
| { | ||
| let first_slot_in_epoch = epoch_schedule.get_first_slot_in_epoch(epoch); | ||
| cache.slot_leader_at(first_slot_in_epoch, Some(root_bank)); | ||
| } | ||
|
|
@@ -84,6 +86,22 @@ impl LeaderScheduleCache { | |
| } | ||
|
|
||
| pub fn slot_leader_at(&self, slot: Slot, bank: Option<&Bank>) -> Option<Pubkey> { | ||
| let slot_epoch = self.epoch_schedule.get_epoch_and_slot_index(slot).0; | ||
| if slot_epoch | ||
| < self | ||
| .max_epoch | ||
| .read() | ||
| .unwrap() | ||
| .max(MAX_LEADER_SCHEDULE_STAKES) | ||
| - MAX_LEADER_SCHEDULE_STAKES | ||
| { | ||
| panic!( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure its best to crash here. What if someone sends our validator a Shred that's really old, it'll cause the validator to panic. Since the function returns an Option, why not just return None and log an error?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, that will be what I'd write as the final form of this PR! I'm doing
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha! Thanks for adding the |
||
| "Requested too old epoch!: {} < {}", | ||
| slot_epoch, | ||
| *self.max_epoch.read().unwrap() | ||
| ); | ||
| } | ||
|
|
||
| if let Some(bank) = bank { | ||
| self.slot_leader_at_else_compute(slot, bank) | ||
| } else if self.epoch_schedule.slots_per_epoch == 0 { | ||
|
|
@@ -113,6 +131,16 @@ impl LeaderScheduleCache { | |
| ); | ||
| return None; | ||
| } | ||
| if self.get_epoch_schedule_else_compute(epoch, bank).is_none() | ||
| /* && epoch < bank.epoch()*/ | ||
| { | ||
| panic!( | ||
| "getting failed! epoch: {}, slot: {}, bank epoch: {}", | ||
| epoch, | ||
| current_slot, | ||
| bank.epoch() | ||
| ); | ||
| } | ||
| while let Some(leader_schedule) = self.get_epoch_schedule_else_compute(epoch, bank) { | ||
| // clippy thinks I should do this: | ||
| // for (i, <item>) in leader_schedule | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,6 +54,8 @@ use std::{ | |
|
|
||
| pub const SECONDS_PER_YEAR: f64 = (365.25 * 24.0 * 60.0 * 60.0); | ||
|
|
||
| pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 3; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is small for testing? I thought we were gonna use 16 or 32
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Oh, sorry for the lack of comment. I'm intentionally reducing this to cause CI fragile. :D @rob-solana At my current understanding with a day of code reading/testing, the assumptions that leader_schedule_stakes for past epochs are always present doesn't cause hard errors for validators (no |
||
|
|
||
| type BankStatusCache = StatusCache<Result<()>>; | ||
|
|
||
| #[derive(Default)] | ||
|
|
@@ -386,14 +388,7 @@ impl Bank { | |
| } | ||
| } | ||
|
|
||
| // update epoch_stakes cache | ||
| // if my parent didn't populate for this staker's epoch, we've | ||
| // crossed a boundary | ||
| if new.epoch_stakes.get(&leader_schedule_epoch).is_none() { | ||
| new.epoch_stakes | ||
| .insert(leader_schedule_epoch, new.stakes.read().unwrap().clone()); | ||
| } | ||
|
|
||
| new.update_epoch_stakes(leader_schedule_epoch); | ||
| new.ancestors.insert(new.slot(), 0); | ||
| new.parents().iter().enumerate().for_each(|(i, p)| { | ||
| new.ancestors.insert(p.slot(), i + 1); | ||
|
|
@@ -486,6 +481,25 @@ impl Bank { | |
| self.store_account(&sysvar::slot_hashes::id(), &account); | ||
| } | ||
|
|
||
| fn update_epoch_stakes(&mut self, leader_schedule_epoch: Epoch) { | ||
| // update epoch_stakes cache | ||
| // if my parent didn't populate for this staker's epoch, we've | ||
| // crossed a boundary | ||
| if self.epoch_stakes.get(&leader_schedule_epoch).is_none() { | ||
| self.epoch_stakes | ||
| .insert(leader_schedule_epoch, self.stakes.read().unwrap().clone()); | ||
|
|
||
| if leader_schedule_epoch >= MAX_LEADER_SCHEDULE_STAKES { | ||
| self.epoch_stakes | ||
| .remove(&(leader_schedule_epoch - MAX_LEADER_SCHEDULE_STAKES)); | ||
| } | ||
| error!( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why error?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the log level should be lower than Just to stand out for testing purpose as said above. Sorry for misleading you! |
||
| "new epoch stakes: {:#?}", | ||
| self.epoch_stakes.keys().collect::<Vec<_>>() | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| fn update_fees(&self) { | ||
| self.store_account( | ||
| &sysvar::fees::id(), | ||
|
|
@@ -1743,6 +1757,47 @@ mod tests { | |
| assert_eq!(bank1.block_height(), 1); | ||
| } | ||
|
|
||
| impl Bank { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A few more comments here would be awesome :)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the feedback! Piece of cake once I fix the snapshot issue. :) |
||
| fn epoch_stake_keys(&self) -> Vec<Epoch> { | ||
| let mut keys: Vec<Epoch> = self.epoch_stakes.keys().map(|k| *k).collect(); | ||
| keys.sort(); | ||
| keys | ||
| } | ||
|
|
||
| fn epoch_stake_key_info(&self) -> (Epoch, Epoch, usize) { | ||
| let mut keys: Vec<Epoch> = self.epoch_stakes.keys().map(|k| *k).collect(); | ||
| keys.sort(); | ||
| (*keys.first().unwrap(), *keys.last().unwrap(), keys.len()) | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_bank_update_epoch_stakes() { | ||
| let (genesis_config, _mint_keypair) = create_genesis_config(100_000); | ||
| let mut bank = Bank::new(&genesis_config); | ||
|
|
||
| let initial_epochs = bank.epoch_stake_keys(); | ||
| assert_eq!(initial_epochs, vec![0, 1]); | ||
|
|
||
| for existing_epoch in &initial_epochs { | ||
| bank.update_epoch_stakes(*existing_epoch); | ||
| assert_eq!(bank.epoch_stake_keys(), initial_epochs); | ||
| } | ||
|
|
||
| for epoch in (initial_epochs.len() as Epoch)..MAX_LEADER_SCHEDULE_STAKES { | ||
| bank.update_epoch_stakes(epoch); | ||
| assert_eq!(bank.epoch_stakes.len() as Epoch, epoch + 1); | ||
| } | ||
|
|
||
| assert_eq!(bank.epoch_stake_key_info(), (0, 2, 3)); | ||
|
|
||
| bank.update_epoch_stakes(MAX_LEADER_SCHEDULE_STAKES); | ||
| assert_eq!(bank.epoch_stake_key_info(), (1, 3, 3)); | ||
|
|
||
| bank.update_epoch_stakes(MAX_LEADER_SCHEDULE_STAKES + 1); | ||
| assert_eq!(bank.epoch_stake_key_info(), (2, 4, 3)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_bank_capitalization() { | ||
| let bank = Arc::new(Bank::new(&GenesisConfig { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this might come out prettier if the bank gave you the epochs for which it had leader_schedule_stakes...