pub struct StakesHandle { /* private fields */ }Expand description
Handle for builtin programs to access stake cache data and freeze stakes.
This handle provides:
- Read/write access to the pending (next epoch) stake cache data
- Layered lookup across baseline, frozen, and pending
- The ability to freeze the pending stakes into frozen via
freeze_stakes() - Callback to check if EpochRewards exists for a given epoch
§Architecture: Baseline + Deltas
The stake cache uses a layered architecture:
- baseline: Complete historical state (empty at genesis, populated during EpochRewards activation)
- frozen: VecDeque of per-epoch deltas awaiting reward distribution (FIFO order)
- pending: Current epoch’s changes being accumulated
Lookups search: pending → frozen (newest to oldest) → baseline
§Epoch Semantics
Important: The pending field contains data for the NEXT epoch (i.e., changes being
accumulated that will take effect after FreezeStakes). To get the CURRENT epoch’s frozen
data for lookups, use frozen.back() instead.
The handle is cached at block level for performance. Since the handle uses shared
Arc<RwLock<...>> references, mutations to pending are immediately visible without
needing to recreate the handle.
§Thread Safety
StakeCache and StakeHistory wrap their data in Arc<RwLock<...>> internally,
allowing safe concurrent access from builtin programs during transaction execution.
Mutations to pending are immediately visible to the owning Bank since they share
the same Arc.
§Field Access
The baseline, pending, and frozen fields are private to enforce proper layered lookups.
Use the provided methods for queries:
get_stake_account()- layered lookup for a single stake accountget_validator_account()- layered lookup for a single validator accountget_all_validator_accounts()- merged view of all validatorsfreeze_stakes()- freeze pending stakesepoch_rewards_exists()- check if EpochRewards account exists for an epoch
Direct field access is only available via #[cfg(test)] accessors for unit tests.
Implementations§
Source§impl StakesHandle
impl StakesHandle
Create a new stakes handle with shared references.
This shares the same Arc<RwLock<...>> with the Bank, so mutations
to pending by builtin programs are immediately visible to the Bank.
The signaling Arcs (epoch_rewards_init, epoch_stakes_frozen)
are created internally with default values. This simplifies the API since callers
don’t need to manage these internal signaling mechanisms.
§Arguments
baseline- The baseline stake cachepending- The pending stake cache for the next epochfrozen- The frozen stake historyepoch_rewards_exists_fn- Callback to check if an EpochRewards account exists
Sourcepub fn epoch_rewards_exists(&self, epoch: u64) -> bool
pub fn epoch_rewards_exists(&self, epoch: u64) -> bool
Check if an EpochRewards account exists for the given epoch.
Uses the callback provided at construction time to query the StateStore. This allows DistributeRewards to find the first completed frozen epoch that doesn’t yet have an EpochRewards account.
Sourcepub fn set_epoch_stakes_frozen(&self)
pub fn set_epoch_stakes_frozen(&self)
Signal that epoch stakes have been frozen (FreezeStakes was called).
This sets the epoch_stakes_frozen flag to true to signal that
apply_pending_validator_changes_if_needed() should be called by the Bank.
Sourcepub fn take_epoch_stakes_frozen(&self) -> bool
pub fn take_epoch_stakes_frozen(&self) -> bool
Atomically take the epoch_stakes_frozen signal.
This atomically reads and clears the flag, returning true if it was set.
Used by finalize_impl() to consume the signal and perform the deferred
pending → frozen swap.
Returns true if FreezeStakes was called and the signal hadn’t been consumed yet.
Sourcepub fn is_epoch_stakes_frozen(&self) -> bool
pub fn is_epoch_stakes_frozen(&self) -> bool
Check if FreezeStakes was signaled this block, without consuming the flag.
Used by apply_pending_validator_changes_if_needed() to detect the epoch
boundary while leaving the flag set for finalize_impl() to consume.
Sourcepub fn get_stake_account_from_pending(
&self,
pubkey: &Pubkey,
) -> Option<StakeAccount>
pub fn get_stake_account_from_pending( &self, pubkey: &Pubkey, ) -> Option<StakeAccount>
Get a stake account starting from pending (next epoch state).
Searches: pending → frozen (newest to oldest) → baseline
Returns Some(account) if found, None if the account doesn’t exist
(either never created or was deleted via tombstone).
Sourcepub fn get_validator_account_from_pending(
&self,
pubkey: &Pubkey,
) -> Option<ValidatorAccount>
pub fn get_validator_account_from_pending( &self, pubkey: &Pubkey, ) -> Option<ValidatorAccount>
Get a validator account starting from pending (next epoch state).
Searches: pending → frozen (newest to oldest) → baseline
Returns Some(account) if found, None if the account doesn’t exist
(either never created or was deleted via tombstone).
Sourcepub fn get_all_validator_accounts_from_pending(
&self,
) -> Vec<(Pubkey, ValidatorAccount)>
pub fn get_all_validator_accounts_from_pending( &self, ) -> Vec<(Pubkey, ValidatorAccount)>
Get all validator accounts starting from pending (next epoch state).
Returns a vector of (pubkey, account) pairs for all validators, sorted by pubkey.
Includes pending changes (next epoch).
Note: This is O(baseline_size + total_deltas).
Sourcepub fn get_stake_account_from_last_frozen(
&self,
pubkey: &Pubkey,
) -> Option<StakeAccount>
pub fn get_stake_account_from_last_frozen( &self, pubkey: &Pubkey, ) -> Option<StakeAccount>
Get a stake account starting from the last frozen epoch (current epoch state).
Searches: frozen (newest to oldest) → baseline Skips pending (next epoch changes).
Returns Some(account) if found, None if the account doesn’t exist
(either never created or was deleted via tombstone).
Sourcepub fn get_validator_account_from_last_frozen(
&self,
pubkey: &Pubkey,
) -> Option<ValidatorAccount>
pub fn get_validator_account_from_last_frozen( &self, pubkey: &Pubkey, ) -> Option<ValidatorAccount>
Get a validator account starting from the last frozen epoch (current epoch state).
Searches: frozen (newest to oldest) → baseline Skips pending (next epoch changes).
Returns Some(account) if found, None if the account doesn’t exist
(either never created or was deleted via tombstone).
Sourcepub fn get_all_validator_accounts_from_last_frozen(
&self,
) -> Vec<(Pubkey, ValidatorAccount)>
pub fn get_all_validator_accounts_from_last_frozen( &self, ) -> Vec<(Pubkey, ValidatorAccount)>
Get all validator accounts from the last frozen epoch (current epoch state).
Returns a vector of (pubkey, account) pairs for all validators, sorted by pubkey.
Skips pending (next epoch changes).
Note: This is O(baseline_size + total_frozen_deltas).
Sourcepub fn get_stake_account_from_first_frozen(
&self,
pubkey: &Pubkey,
) -> Option<StakeAccount>
pub fn get_stake_account_from_first_frozen( &self, pubkey: &Pubkey, ) -> Option<StakeAccount>
Get a stake account starting from the first frozen epoch (oldest pending rewards).
Searches: frozen.front() → baseline only Skips all newer frozen epochs and pending.
Returns Some(account) if found, None if the account doesn’t exist
(either never created or was deleted via tombstone).
Sourcepub fn get_all_stake_accounts_from_pending(&self) -> Vec<(Pubkey, StakeAccount)>
pub fn get_all_stake_accounts_from_pending(&self) -> Vec<(Pubkey, StakeAccount)>
Get all stake accounts starting from pending (next epoch state).
Returns a vector of (pubkey, account) pairs for all stake accounts, sorted by pubkey.
Includes pending changes (next epoch).
Note: This is O(baseline_size + total_deltas).
This method is used for operations that need to check all stake accounts including the most recent changes (e.g., checking validator references during Withdraw).
Sourcepub fn get_all_stake_accounts_from_first_frozen(
&self,
) -> Vec<(Pubkey, StakeAccount)>
pub fn get_all_stake_accounts_from_first_frozen( &self, ) -> Vec<(Pubkey, StakeAccount)>
Get all stake accounts from the first frozen epoch (oldest pending rewards).
Returns a vector of (pubkey, account) pairs for all stake accounts, sorted by pubkey.
Skips all newer frozen epochs and pending.
This method is used by reward calculation to iterate over all stake accounts that were active at the time rewards were frozen (baseline + first frozen delta).
Sourcepub fn get_all_stake_accounts_from_frozen_epoch(
&self,
target_epoch: Epoch,
) -> Vec<(Pubkey, StakeAccount)>
pub fn get_all_stake_accounts_from_frozen_epoch( &self, target_epoch: Epoch, ) -> Vec<(Pubkey, StakeAccount)>
Get all stake accounts from baseline + frozen deltas up to (and including) the specified epoch.
Lookups: baseline + frozen deltas where delta.epoch <= target_epoch
Sourcepub fn get_all_stake_accounts_from_baseline(
&self,
) -> Vec<(Pubkey, StakeAccount)>
pub fn get_all_stake_accounts_from_baseline( &self, ) -> Vec<(Pubkey, StakeAccount)>
Get all stake accounts from the baseline only.
Returns a vector of (pubkey, account) pairs for all stake accounts in baseline,
sorted by pubkey. Does NOT include frozen or pending data.
This is used after the frozen.front() has been merged into baseline, for baseline-based reward calculation. The baseline contains the complete state of the epoch being rewarded after merge.
Sourcepub fn get_all_validator_accounts_from_baseline(
&self,
) -> Vec<(Pubkey, ValidatorAccount)>
pub fn get_all_validator_accounts_from_baseline( &self, ) -> Vec<(Pubkey, ValidatorAccount)>
Get all validator accounts from the baseline only.
Returns a vector of (pubkey, account) pairs for all validators in baseline,
sorted by pubkey. Does NOT include frozen or pending data.
This is used after the frozen.front() has been merged into baseline, for baseline-based reward calculation.
Sourcepub fn freeze_stakes(&self)
pub fn freeze_stakes(&self)
Freeze the pending stake cache data.
This performs an O(1) swap of the pending stake cache data using std::mem::take()
and pushes it to the back of the frozen queue. This is typically called by the
ValidatorRegistry program’s FreezeStakes instruction to capture the validator set
at a specific point.
Note: Since the handle uses shared Arc<RwLock<...>> references, the frozen
data and updated pending epoch are immediately visible to all handle instances.
To access the frozen validator data after calling this method, use
get_all_validator_accounts_from_last_frozen().
Sourcepub fn pending_epoch(&self) -> Epoch
pub fn pending_epoch(&self) -> Epoch
Get the epoch of the pending (next) stake cache.
Sourcepub fn set_pending_epoch(&self, epoch: Epoch)
pub fn set_pending_epoch(&self, epoch: Epoch)
Set the epoch of the pending stake cache.
Sourcepub fn set_pending_timestamp(&self, timestamp: u64)
pub fn set_pending_timestamp(&self, timestamp: u64)
Set the timestamp of the pending stake cache.
Sourcepub fn last_frozen_timestamp(&self) -> Option<u64>
pub fn last_frozen_timestamp(&self) -> Option<u64>
Get the timestamp of the last frozen epoch (current epoch’s effective state).
Returns None if no frozen snapshots exist yet.
Sourcepub fn push_frozen(&self, data: StakeCacheData)
pub fn push_frozen(&self, data: StakeCacheData)
Push a new frozen snapshot to the history.
Sourcepub fn frozen_len(&self) -> usize
pub fn frozen_len(&self) -> usize
Get the number of frozen snapshots in the history.
Sourcepub fn front_frozen_epoch(&self) -> Option<Epoch>
pub fn front_frozen_epoch(&self) -> Option<Epoch>
Get the epoch of the oldest frozen snapshot (front of the queue).
Returns None if no frozen snapshots exist.
Sourcepub fn request_epoch_rewards_init(&self, epoch: Epoch, total_rewards: u64)
pub fn request_epoch_rewards_init(&self, epoch: Epoch, total_rewards: u64)
Request epoch rewards initialization.
This is called by the DistributeRewards instruction to signal that the Bank
should create an EpochRewards account. The Bank checks for the request after
transaction execution via take_epoch_rewards_init_request().
§Arguments
epoch- The epoch for which rewards are being distributedtotal_rewards- The total rewards to distribute (hardcoded for MVP)
Sourcepub fn take_epoch_rewards_init_request(&self) -> Option<EpochRewardsInitRequest>
pub fn take_epoch_rewards_init_request(&self) -> Option<EpochRewardsInitRequest>
Take the epoch rewards initialization request, clearing it.
This is called by the Bank after transaction execution to check if epoch rewards init was requested. The Bank uses the returned data to create the EpochRewards account.
Returns Some(request) if a request was pending, None otherwise.
After this call, epoch_rewards_init will be None.
Sourcepub fn is_epoch_rewards_init_pending(&self) -> bool
pub fn is_epoch_rewards_init_pending(&self) -> bool
Check if an epoch rewards initialization request is pending.
This is used by DistributeRewards to fail if a signal is already set for the current block (prevents multiple DistributeRewards in same block).
Returns true if a request is pending, false otherwise.
Does NOT consume the request (unlike take_epoch_rewards_init_request).
Sourcepub fn completed_frozen_epochs(&self) -> Vec<Epoch> ⓘ
pub fn completed_frozen_epochs(&self) -> Vec<Epoch> ⓘ
Get completed frozen epochs (excludes the last/current epoch).
Returns epoch numbers for all frozen entries except the last one, which represents the currently ongoing epoch. These are epochs that have completed and are eligible for reward distribution.
Returns empty if frozen has 0 or 1 entries (need at least 2 to have completed epochs).
Sourcepub fn is_validator_referenced(
&self,
validator: &Pubkey,
validator_info: &ValidatorInfo,
last_freeze_timestamp: u64,
current_timestamp: u64,
) -> bool
pub fn is_validator_referenced( &self, validator: &Pubkey, validator_info: &ValidatorInfo, last_freeze_timestamp: u64, current_timestamp: u64, ) -> bool
Check if any stake account references the given validator pubkey whose unbonding period is NOT yet complete.
This performs an O(n) search over all stake accounts starting from pending → frozen → baseline. Uses Rayon’s parallel iterator for better performance on multi-core systems.
A stake account is considered to “reference” the validator if:
- It has
validator == Some(target_validator), AND - Either:
- It is active (no
deactivation_requested), OR - It is still unbonding (unbonding conditions not yet met)
- It is active (no
Stake accounts whose unbonding is complete are NOT considered as referencing the validator, since they can be fully withdrawn or reactivated to another validator.
§Unbonding Completion Conditions
Unbonding is complete when BOTH conditions are met:
- State transition:
deactivation_timestamp < last_freeze_timestamp(at least one FreezeStakes has occurred since deactivation) - Duration enforcement:
deactivation_timestamp + unbonding_period < current_timestamp(the unbonding period has actually elapsed)
§Arguments
validator- The validator pubkey to checkvalidator_info- The validator’s info (used to compute unbonding end viaend_of_unbonding)last_freeze_timestamp- When FreezeStakes was last called (epoch boundary)current_timestamp- Current block timestamp from Clock sysvar (in milliseconds)
§Returns
true if at least one stake account references the validator and is either
active or still unbonding, false otherwise.
§Performance
This is an expensive O(n) operation that should only be called when needed (e.g., during Withdraw when checking if a validator can be fully drained).
Sourcepub fn has_locked_stakers(
&self,
validator: &Pubkey,
lockup_period: u64,
current_timestamp: u64,
) -> bool
pub fn has_locked_stakers( &self, validator: &Pubkey, lockup_period: u64, current_timestamp: u64, ) -> bool
Check if any stake account delegated to the given validator is still within its lockup period.
This performs an O(n) search over all stake accounts starting from pending → frozen → baseline. Uses Rayon’s parallel iterator for better performance on multi-core systems.
A staker is considered “locked” if ALL of the following are true:
- It has
validator == Some(target_validator)(delegated to this validator) - It has
activation_requested == Some(timestamp)(was activated) activation_requested + lockup_period > current_timestamp(lockup hasn’t expired)
Self-bonds are excluded from lockup checks to prevent the validator from being unable to change commission rates or shut down when only the self-bond exists.
§Arguments
validator- The validator pubkey to checklockup_period- The validator’s lockup period in millisecondscurrent_timestamp- Current block timestamp from Clock sysvar (in milliseconds)
§Returns
true if at least one stake account is delegated to the validator and still
within its lockup period, false otherwise.
Sourcepub fn is_validator_referenced_excluding_self_bond(
&self,
validator_pubkey: &Pubkey,
validator_info: &ValidatorInfo,
last_freeze_timestamp: u64,
current_timestamp: u64,
) -> bool
pub fn is_validator_referenced_excluding_self_bond( &self, validator_pubkey: &Pubkey, validator_info: &ValidatorInfo, last_freeze_timestamp: u64, current_timestamp: u64, ) -> bool
Check if a validator is referenced by any stake accounts (excluding the self-bond).
This variant excludes the self-bond PDA from the check to prevent circular logic where the self-bond cannot be deactivated because its existence always makes is_validator_referenced() return true.
§Arguments
validator_pubkey- The validator pubkey to checkvalidator_info- The validator’s info (used to compute unbonding end viaend_of_unbonding)last_freeze_timestamp- When FreezeStakes was last called (epoch boundary)current_timestamp- Current block timestamp from Clock sysvar (in milliseconds)
§Returns
true if at least one non-self-bond stake account references the validator
Sourcepub fn insert_stake_account(&self, pubkey: Pubkey, account: StakeAccount)
pub fn insert_stake_account(&self, pubkey: Pubkey, account: StakeAccount)
Insert a stake account into the pending cache.
Sourcepub fn insert_validator_account(
&self,
pubkey: Pubkey,
account: ValidatorAccount,
)
pub fn insert_validator_account( &self, pubkey: Pubkey, account: ValidatorAccount, )
Insert a validator account into the pending cache.
Trait Implementations§
Source§impl Clone for StakesHandle
impl Clone for StakesHandle
Source§impl Debug for StakesHandle
impl Debug for StakesHandle
Auto Trait Implementations§
impl Freeze for StakesHandle
impl !RefUnwindSafe for StakesHandle
impl Send for StakesHandle
impl Sync for StakesHandle
impl Unpin for StakesHandle
impl UnsafeUnpin for StakesHandle
impl !UnwindSafe for StakesHandle
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more