Skip to content
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

REFACTOR: Added status flags #55

Merged
merged 7 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 7 additions & 33 deletions programs/steward/idl/steward.json
Original file line number Diff line number Diff line change
Expand Up @@ -2741,44 +2741,18 @@
"type": "u64"
},
{
"name": "validators_added",
"docs": [
"Number of validators added to the pool in the current cycle"
],
"type": "u16"
},
{
"name": "compute_delegations_completed",
"docs": [
"Tracks whether delegation computation has been completed"
],
"type": {
"defined": {
"name": "U8Bool"
}
}
},
{
"name": "rebalance_completed",
"name": "status_flags",
"docs": [
"Tracks whether unstake and delegate steps have completed"
"Various flags to track state progress - See"
],
"type": {
"defined": {
"name": "U8Bool"
}
}
"type": "u32"
},
{
"name": "checked_validators_removed_from_list",
"name": "validators_added",
"docs": [
"So we only have to check the validator list once for `ReadyToRemove`"
"Number of validators added to the pool in the current cycle"
],
"type": {
"defined": {
"name": "U8Bool"
}
}
"type": "u16"
},
{
"name": "_padding0",
Expand All @@ -2788,7 +2762,7 @@
"type": {
"array": [
"u8",
40003
40002
]
}
}
Expand Down
34 changes: 27 additions & 7 deletions programs/steward/src/instructions/epoch_maintenance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::{
check_validator_list_has_stake_status_other_than, deserialize_stake_pool,
get_stake_pool_address, get_validator_list_length,
},
Config, StewardStateAccount,
Config, StewardStateAccount, CHECKED_VALIDATORS_REMOVED_FROM_LIST, COMPUTE_INSTANT_UNSTAKES,
EPOCH_MAINTENANCE, POST_LOOP_IDLE, PRE_LOOP_IDLE, REBALANCE, RESET_TO_IDLE,
};
use anchor_lang::prelude::*;
use spl_stake_pool::state::StakeStatus;
Expand Down Expand Up @@ -49,7 +50,14 @@ pub fn handler(
StewardError::StakePoolNotUpdated
);

if (!state_account.state.checked_validators_removed_from_list).into() {
// Keep this unset until we have completed all maintenance tasks
state_account.state.unset_flag(EPOCH_MAINTENANCE);

// We only need to check this once per maintenance cycle
if !state_account
.state
.has_flag(CHECKED_VALIDATORS_REMOVED_FROM_LIST)
{
// Ensure there are no validators in the list that have not been removed, that should be
require!(
!check_validator_list_has_stake_status_other_than(
Expand All @@ -59,7 +67,9 @@ pub fn handler(
StewardError::ValidatorsHaveNotBeenRemoved
);

state_account.state.checked_validators_removed_from_list = true.into();
state_account
.state
.set_flag(CHECKED_VALIDATORS_REMOVED_FROM_LIST);
}

{
Expand Down Expand Up @@ -88,12 +98,22 @@ pub fn handler(
let okay_to_update = state_account.state.validators_to_remove.is_empty()
&& state_account
.state
.checked_validators_removed_from_list
.into();
.has_flag(CHECKED_VALIDATORS_REMOVED_FROM_LIST);

if okay_to_update {
state_account.state.current_epoch = clock.epoch;
state_account.state.checked_validators_removed_from_list = false.into();
state_account.state.rebalance_completed = false.into();

// We keep Compute Scores and Compute Delegations to be unset on next epoch cycle
state_account.state.unset_flag(
CHECKED_VALIDATORS_REMOVED_FROM_LIST
| PRE_LOOP_IDLE
| COMPUTE_INSTANT_UNSTAKES
| REBALANCE
| POST_LOOP_IDLE,
);
state_account
.state
.set_flag(RESET_TO_IDLE | EPOCH_MAINTENANCE);
}

emit!(EpochMaintenanceEvent {
Expand Down
3 changes: 1 addition & 2 deletions programs/steward/src/instructions/realloc_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,11 @@ pub fn handler(ctx: Context<ReallocState>) -> Result<()> {
.checked_add(config.parameters.num_epochs_between_scoring)
.ok_or(StewardError::ArithmeticError)?;
state_account.state.delegations = [Delegation::default(); MAX_VALIDATORS];
state_account.state.rebalance_completed = false.into();
state_account.state.instant_unstake = BitMask::default();
state_account.state.start_computing_scores_slot = clock.slot;
state_account.state.validators_to_remove = BitMask::default();
state_account.state.validators_added = 0;
state_account.state.checked_validators_removed_from_list = false.into();
state_account.state.clear_flags();
state_account.state._padding0 = [0; STATE_PADDING_0_SIZE];
}

Expand Down
3 changes: 1 addition & 2 deletions programs/steward/src/instructions/reset_steward_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,11 @@ pub fn handler(ctx: Context<ResetStewardState>) -> Result<()> {
.checked_add(config.parameters.num_epochs_between_scoring)
.ok_or(StewardError::ArithmeticError)?;
state_account.state.delegations = [Delegation::default(); MAX_VALIDATORS];
state_account.state.rebalance_completed = false.into();
state_account.state.instant_unstake = BitMask::default();
state_account.state.start_computing_scores_slot = clock.slot;
state_account.state.validators_to_remove = BitMask::default();
state_account.state.validators_added = 0;
state_account.state.checked_validators_removed_from_list = false.into();
state_account.state.clear_flags();
state_account.state._padding0 = [0; STATE_PADDING_0_SIZE];
Ok(())
}
100 changes: 68 additions & 32 deletions programs/steward/src/state/steward_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
score::{
instant_unstake_validator, validator_score, InstantUnstakeComponents, ScoreComponents,
},
utils::{epoch_progress, get_target_lamports, stake_lamports_at_validator_list_index, U8Bool},
utils::{epoch_progress, get_target_lamports, stake_lamports_at_validator_list_index},
Config, Parameters,
};
use anchor_lang::idl::types::*;
Expand Down Expand Up @@ -110,24 +110,18 @@ pub struct StewardState {
/// Total lamports that have been due to stake deposits this cycle
pub stake_deposit_unstake_total: u64,

/// Various flags to track state progress - See
coachchucksol marked this conversation as resolved.
Show resolved Hide resolved
pub status_flags: u32,

/// Number of validators added to the pool in the current cycle
pub validators_added: u16,

/// Tracks whether delegation computation has been completed
pub compute_delegations_completed: U8Bool,

/// Tracks whether unstake and delegate steps have completed
pub rebalance_completed: U8Bool,

/// So we only have to check the validator list once for `ReadyToRemove`
pub checked_validators_removed_from_list: U8Bool,

/// Future state and #[repr(C)] alignment
pub _padding0: [u8; STATE_PADDING_0_SIZE],
// TODO ADD MORE PADDING
}

pub const STATE_PADDING_0_SIZE: usize = MAX_VALIDATORS * 8 + 3;
pub const STATE_PADDING_0_SIZE: usize = MAX_VALIDATORS * 8 + 2;

#[derive(Clone, Copy)]
#[repr(u64)]
Expand Down Expand Up @@ -236,7 +230,45 @@ impl IdlBuild for StewardStateEnum {
}
}

// BITS 0-7 COMPLETED PROGRESS FLAGS
// Used to mark the completion of a particular state
pub const COMPUTE_SCORE: u32 = 1 << 0;
pub const COMPUTE_DELEGATIONS: u32 = 1 << 1;
pub const EPOCH_MAINTENANCE: u32 = 1 << 2;
pub const PRE_LOOP_IDLE: u32 = 1 << 3;
pub const COMPUTE_INSTANT_UNSTAKES: u32 = 1 << 4;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: COMPUTE_INSTANT_UNSTAKE to be aligned with the instruction name

pub const REBALANCE: u32 = 1 << 5;
pub const POST_LOOP_IDLE: u32 = 1 << 6;
// BITS 8-15 RESERVED FOR FUTURE USE
// BITS 16-23 OPERATIONAL FLAGS
/// In epoch maintenance, we only need to check the validator pool
/// once for any validators that still need to be removed
/// when there are no validators to remove from the pool, the operation continues
/// and this condition is not checked again
pub const CHECKED_VALIDATORS_REMOVED_FROM_LIST: u32 = 1 << 16;
/// In epoch maintenance, when a new epoch is detected, we need a flag to tell the
/// state transition layer that it needs to be reset to the IDLE state
/// this flag is set in in epoch_maintenance and unset in the IDLE state transition
pub const RESET_TO_IDLE: u32 = 1 << 17;
coachchucksol marked this conversation as resolved.
Show resolved Hide resolved
coachchucksol marked this conversation as resolved.
Show resolved Hide resolved
// BITS 24-31 RESERVED FOR FUTURE USE

impl StewardState {
pub fn set_flag(&mut self, flag: u32) {
self.status_flags |= flag;
}

pub fn clear_flags(&mut self) {
self.status_flags = 0;
}

pub fn unset_flag(&mut self, flag: u32) {
self.status_flags &= !flag;
}

pub fn has_flag(&self, flag: u32) -> bool {
self.status_flags & flag != 0
}

/// Top level transition method. Tries to transition to a new state based on current state and epoch conditions
pub fn transition(
&mut self,
Expand Down Expand Up @@ -287,7 +319,6 @@ impl StewardState {
num_epochs_between_scoring: u64,
) -> Result<()> {
if current_epoch >= self.next_cycle_epoch {
self.state_tag = StewardStateEnum::ComputeScores;
self.reset_state_for_new_cycle(
current_epoch,
current_slot,
Expand All @@ -297,6 +328,7 @@ impl StewardState {
self.state_tag = StewardStateEnum::ComputeDelegations;
self.progress = BitMask::default();
self.delegations = [Delegation::default(); MAX_VALIDATORS];
self.set_flag(COMPUTE_SCORE);
}
Ok(())
}
Expand All @@ -309,15 +341,13 @@ impl StewardState {
num_epochs_between_scoring: u64,
) -> Result<()> {
if current_epoch >= self.next_cycle_epoch {
self.state_tag = StewardStateEnum::ComputeScores;
self.reset_state_for_new_cycle(
current_epoch,
current_slot,
num_epochs_between_scoring,
)?;
} else if self.compute_delegations_completed.into() {
} else if self.has_flag(COMPUTE_DELEGATIONS) {
self.state_tag = StewardStateEnum::Idle;
self.rebalance_completed = false.into();
}
Ok(())
}
Expand All @@ -331,22 +361,28 @@ impl StewardState {
epoch_progress: f64,
min_epoch_progress_for_instant_unstake: f64,
) -> Result<()> {
let completed_loop = self.has_flag(REBALANCE);

if current_epoch >= self.next_cycle_epoch {
self.state_tag = StewardStateEnum::ComputeScores;
self.reset_state_for_new_cycle(
current_epoch,
current_slot,
num_epochs_between_scoring,
)?;
} else if (!self.rebalance_completed).into()
&& epoch_progress >= min_epoch_progress_for_instant_unstake
{
//NOTE: rebalance_completed is cleared on epoch change in `epoch_maintenance`
} else if !completed_loop {
self.unset_flag(RESET_TO_IDLE);

self.state_tag = StewardStateEnum::ComputeInstantUnstake;
self.instant_unstake = BitMask::default();
self.progress = BitMask::default();
self.set_flag(PRE_LOOP_IDLE);

if epoch_progress >= min_epoch_progress_for_instant_unstake {
self.state_tag = StewardStateEnum::ComputeInstantUnstake;
self.instant_unstake = BitMask::default();
self.progress = BitMask::default();
}
} else if completed_loop {
self.set_flag(POST_LOOP_IDLE)
}

Ok(())
}

Expand All @@ -358,19 +394,20 @@ impl StewardState {
num_epochs_between_scoring: u64,
) -> Result<()> {
if current_epoch >= self.next_cycle_epoch {
self.state_tag = StewardStateEnum::ComputeScores;
self.reset_state_for_new_cycle(
current_epoch,
current_slot,
num_epochs_between_scoring,
)?;
} else if current_epoch > self.current_epoch {
} else if self.has_flag(RESET_TO_IDLE) {
self.state_tag = StewardStateEnum::Idle;
self.instant_unstake = BitMask::default();
self.progress = BitMask::default();
// NOTE: RESET_TO_IDLE is cleared in the Idle transition
} else if self.progress.is_complete(self.num_pool_validators)? {
self.state_tag = StewardStateEnum::Rebalance;
self.progress = BitMask::default();
self.set_flag(COMPUTE_INSTANT_UNSTAKES);
}
Ok(())
}
Expand All @@ -383,19 +420,18 @@ impl StewardState {
num_epochs_between_scoring: u64,
) -> Result<()> {
if current_epoch >= self.next_cycle_epoch {
self.state_tag = StewardStateEnum::ComputeScores;
coachchucksol marked this conversation as resolved.
Show resolved Hide resolved
self.reset_state_for_new_cycle(
current_epoch,
current_slot,
num_epochs_between_scoring,
)?;
} else if current_epoch > self.current_epoch {
} else if self.has_flag(RESET_TO_IDLE) {
self.state_tag = StewardStateEnum::Idle;
self.progress = BitMask::default();
self.rebalance_completed = false.into();
// NOTE: RESET_TO_IDLE is cleared in the Idle transition
} else if self.progress.is_complete(self.num_pool_validators)? {
self.state_tag = StewardStateEnum::Idle;
self.rebalance_completed = true.into();
self.set_flag(REBALANCE);
}
Ok(())
}
Expand All @@ -420,8 +456,8 @@ impl StewardState {
self.stake_deposit_unstake_total = 0;
self.delegations = [Delegation::default(); MAX_VALIDATORS];
self.instant_unstake = BitMask::default();
self.compute_delegations_completed = false.into();
self.rebalance_completed = false.into();
self.clear_flags();

Ok(())
}

Expand Down Expand Up @@ -675,7 +711,7 @@ impl StewardState {
};
}

self.compute_delegations_completed = true.into();
self.set_flag(COMPUTE_DELEGATIONS);

return Ok(());
}
Expand Down
4 changes: 1 addition & 3 deletions tests/src/steward_fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -934,10 +934,8 @@ impl Default for StateMachineFixtures {
stake_deposit_unstake_total: 0,
delegations: [Delegation::default(); MAX_VALIDATORS],
instant_unstake: BitMask::default(),
compute_delegations_completed: false.into(),
rebalance_completed: false.into(),
status_flags: 0,
validators_added: 0,
checked_validators_removed_from_list: false.into(),
validators_to_remove: BitMask::default(),
_padding0: [0; STATE_PADDING_0_SIZE],
};
Expand Down
Loading
Loading