Skip to content

Commit

Permalink
Fix for removed validator past boundary (#84)
Browse files Browse the repository at this point in the history
Problem: A bug was found where validators who are added to the pool
outside of the boundary of the Steward state, then removed before they
begin getting tracked (validators added during cycle N are tracked in
steward state in cycle N+1) were not getting properly removed. This
caused the state machine to get stuck because the invariant used to keep
the state aligned was failing (`num_pool_validators + validators_added -
validators_to_remove == validator_list.len()`)

Resolution: Make sure we're shifting values for steward state fields
that can be relevant past `num_pool_validators` (which is just the
validators_to_remove and validators_for_immediate_removal). We also
don't want to clear those values at `num_pool_validators` because that
might be a relevant value.
  • Loading branch information
ebatsell authored Sep 16, 2024
1 parent 3f53496 commit 59176f8
Show file tree
Hide file tree
Showing 4 changed files with 12 additions and 14 deletions.
2 changes: 1 addition & 1 deletion keepers/stakenet-keeper/src/entries/crank_steward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ async fn _handle_instant_removal_validators(

while validators_to_remove.count() != 0 {
let mut validator_index_to_remove = None;
for i in 0..num_validators {
for i in 0..all_steward_accounts.validator_list_account.validators.len() as u64 {
if validators_to_remove.get(i as usize).map_err(|e| {
JitoTransactionError::Custom(format!(
"Error fetching bitmask index for immediate removed validator: {}/{} - {}",
Expand Down
2 changes: 1 addition & 1 deletion programs/steward/src/instructions/epoch_maintenance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub fn handler(
if let Some(validator_index_to_remove) = validator_index_to_remove {
state_account
.state
.remove_validator(validator_index_to_remove)?;
.remove_validator(validator_index_to_remove, validators_in_list)?;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub fn handler(

state_account
.state
.remove_validator(validator_index_to_remove)?;
.remove_validator(validator_index_to_remove, validators_in_list)?;

Ok(())
}
20 changes: 9 additions & 11 deletions programs/steward/src/state/steward_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ impl StewardState {
}

/// Update internal state when a validator is removed from the pool
pub fn remove_validator(&mut self, index: usize) -> Result<()> {
pub fn remove_validator(&mut self, index: usize, validator_list_len: usize) -> Result<()> {
let marked_for_regular_removal = self.validators_to_remove.get(index)?;
let marked_for_immediate_removal = self.validators_for_immediate_removal.get(index)?;

Expand Down Expand Up @@ -494,6 +494,11 @@ impl StewardState {
self.instant_unstake
.set(i, self.instant_unstake.get(next_i)?)?;
self.progress.set(i, self.progress.get(next_i)?)?;
}

// For state that can be valid past num_pool_validators, we still need to shift the values
for i in index..validator_list_len {
let next_i = i.checked_add(1).ok_or(StewardError::ArithmeticError)?;
self.validators_to_remove
.set(i, self.validators_to_remove.get(next_i)?)?;
self.validators_for_immediate_removal
Expand Down Expand Up @@ -538,24 +543,17 @@ impl StewardState {
}

// Clear values on empty last index
self.validator_lamport_balances[num_pool_validators] = 0;
self.validator_lamport_balances[num_pool_validators] = LAMPORT_BALANCE_DEFAULT;
self.scores[num_pool_validators] = 0;
self.yield_scores[num_pool_validators] = 0;
self.sorted_score_indices[num_pool_validators] = SORTED_INDEX_DEFAULT;
self.sorted_yield_score_indices[num_pool_validators] = SORTED_INDEX_DEFAULT;
self.delegations[num_pool_validators] = Delegation::default();
self.instant_unstake.set(num_pool_validators, false)?;
self.progress.set(num_pool_validators, false)?;
self.validators_to_remove.set(num_pool_validators, false)?;
self.validators_to_remove.set(validator_list_len, false)?;
self.validators_for_immediate_removal
.set(num_pool_validators, false)?;

if marked_for_regular_removal {
self.validators_to_remove.set(num_pool_validators, false)?;
} else {
self.validators_for_immediate_removal
.set(num_pool_validators, false)?;
}
.set(validator_list_len, false)?;

Ok(())
}
Expand Down

0 comments on commit 59176f8

Please sign in to comment.