-
Notifications
You must be signed in to change notification settings - Fork 968
feat(alpenglow): introduce alpenclock primitives to bank #10331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
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 |
|---|---|---|
|
|
@@ -192,7 +192,7 @@ use { | |
| AtomicBool, AtomicI64, AtomicU64, | ||
| Ordering::{self, AcqRel, Acquire, Relaxed}, | ||
| }, | ||
| Arc, LockResult, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak, | ||
| Arc, LazyLock, LockResult, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak, | ||
| }, | ||
| time::{Duration, Instant}, | ||
| }, | ||
|
|
@@ -236,6 +236,14 @@ pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5; | |
| /// only the top 2000 validators by stake will be present in vote account structures. | ||
| pub const MAX_ALPENGLOW_VOTE_ACCOUNTS: usize = 2000; | ||
|
|
||
| /// The off-curve account where we store the Alpenglow clock. The clock sysvar has seconds | ||
| /// resolution while the Alpenglow clock has nanosecond resolution. | ||
| static NANOSECOND_CLOCK_ACCOUNT: LazyLock<Pubkey> = LazyLock::new(|| { | ||
| let (pubkey, _) = | ||
| Pubkey::find_program_address(&[b"alpenclock"], &agave_feature_set::alpenglow::id()); | ||
| pubkey | ||
| }); | ||
|
|
||
| pub type BankStatusCache = StatusCache<Result<()>>; | ||
| #[cfg_attr( | ||
| feature = "frozen-abi", | ||
|
|
@@ -2911,6 +2919,64 @@ impl Bank { | |
| self.store_account_and_update_capitalization(&GENESIS_CERTIFICATE_ACCOUNT, &cert_acct); | ||
| } | ||
|
|
||
| /// Update the clock sysvar from a block footer's nanosecond timestamp. | ||
| /// Also stores the nanosecond value for later retrieval via `get_nanosecond_clock`. | ||
| pub fn update_clock_from_footer(&self, unix_timestamp_nanos: i64) { | ||
| if !self.feature_set.is_active(&feature_set::alpenglow::id()) { | ||
| return; | ||
| } | ||
|
|
||
| // On epoch boundaries, update epoch_start_timestamp | ||
| // | ||
| // Note: the genesis block's bank is created via new_from_genesis, which calls update_clock | ||
| // unconditionally. In update_clock, we have a check for whether slot == 0, and if that's | ||
| // the case, the clock is set to self.unix_timestamp_from_genesis(). | ||
| // | ||
| // As a result, we don't actually need the (0, _) case below, since it's never invoked. | ||
| // However, include this for completeness in the match statement. | ||
| let unix_timestamp_s = unix_timestamp_nanos / 1_000_000_000; | ||
| let epoch_start_timestamp = match (self.slot, self.parent()) { | ||
| (0, _) => self.unix_timestamp_from_genesis(), | ||
|
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. This line is basically just for tests. 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. is this function actually called for tests for the genesis block? Just want to make sure I completely understand the corner cases before we upstream
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.
It's not; the genesis block's bank is created via You're correct in that we can probably get rid of this first match statement, though I'd rather keep this here for completeness. I'll add in a comment for clarity, though.
Writing this here for completeness - the rest of the banks are initialized via if new.get_alpenglow_genesis_certificate().is_none() {
new.update_clock(Some(parent.epoch()));
}During migration, this is the line that runs; concretely, on the zero'th slot (i.e., the zero'th Alpenglow slot), the genesis certificate should be set to Later in the block, we then observe the footer, which updates the clock for the second time in the zero'th Alpenglow slot. So the clock is actually updated twice in the zero'th Alpenglow slot, when migrating from TowerBFT. For Alpenglow slots 1+, the |
||
| (_, Some(parent)) if parent.epoch() != self.epoch() => unix_timestamp_s, | ||
| _ => self.clock().epoch_start_timestamp, | ||
| }; | ||
|
|
||
| // Update clock sysvar | ||
| // NOTE: block footer UNIX timestamps are in nanoseconds, but clock sysvar stores timestamps | ||
| // in seconds | ||
| let clock = sysvar::clock::Clock { | ||
| slot: self.slot, | ||
| epoch_start_timestamp, | ||
| epoch: self.epoch_schedule().get_epoch(self.slot), | ||
| leader_schedule_epoch: self.epoch_schedule().get_leader_schedule_epoch(self.slot), | ||
| unix_timestamp: unix_timestamp_s, | ||
| }; | ||
|
|
||
| self.update_sysvar_account(&sysvar::clock::id(), |account| { | ||
| create_account( | ||
| &clock, | ||
| self.inherit_specially_retained_account_fields(account), | ||
| ) | ||
| }); | ||
|
|
||
| // Update Alpenglow clock | ||
| let data = wincode::serialize(&unix_timestamp_nanos).unwrap(); | ||
| let lamports = Rent::default().minimum_balance(data.len()); | ||
| let mut alpenclock_acct = AccountSharedData::new(lamports, data.len(), &system_program::ID); | ||
| alpenclock_acct.set_data_from_slice(&data); | ||
|
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. no action for this PR, but since this
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. sg - third time's the charm |
||
|
|
||
| self.store_account_and_update_capitalization(&NANOSECOND_CLOCK_ACCOUNT, &alpenclock_acct); | ||
| } | ||
|
|
||
| /// Get the nanosecond clock value. Returns `None` if the nanosecond clock has not been | ||
| /// populated (i.e., before Alpenglow migration completes). | ||
| pub fn get_nanosecond_clock(&self) -> Option<i64> { | ||
| self.get_account(&NANOSECOND_CLOCK_ACCOUNT).map(|acct| { | ||
| wincode::deserialize(acct.data()) | ||
| .expect("Couldn't deserialize nanosecond resolution clock") | ||
| }) | ||
| } | ||
|
|
||
| pub fn confirmed_last_blockhash(&self) -> Hash { | ||
| const NUM_BLOCKHASH_CONFIRMATIONS: usize = 3; | ||
|
|
||
|
|
||
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.
Added these lines in to prevent accidental invocation.