From 5bc93869c8533e8ee78b05d9bf3dd562240dbf79 Mon Sep 17 00:00:00 2001 From: realbigsean Date: Wed, 24 Feb 2021 04:15:13 +0000 Subject: [PATCH] Update ValidatorStatus to match the v1 API (#2149) ## Issue Addressed N/A ## Proposed Changes We are currently a bit off of the standard API spec because we have [this](https://hackmd.io/bQxMDRt1RbS1TLno8K4NPg?view) proposal implemented for validator status. Based on discussion [here](https://github.com/ethereum/eth2.0-APIs/pull/94), it looks like this won't be added to the spec until v2, so this PR implements [this](https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ) validator status logic instead ## Additional Info N/A Co-authored-by: realbigsean --- beacon_node/http_api/src/lib.rs | 20 ++-- beacon_node/http_api/tests/tests.rs | 36 +++---- common/eth2/src/types.rs | 154 ++++++++++++++++------------ 3 files changed, 113 insertions(+), 97 deletions(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 707e5fd37..03f8fb7a2 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -498,7 +498,6 @@ pub fn serve( state_id .map_state(&chain, |state| { let epoch = state.current_epoch(); - let finalized_epoch = state.finalized_checkpoint.epoch; let far_future_epoch = chain.spec.far_future_epoch; Ok(state @@ -522,17 +521,18 @@ pub fn serve( // filter by status(es) if provided and map the result .filter_map(|(index, (validator, balance))| { let status = api_types::ValidatorStatus::from_validator( - Some(validator), + validator, epoch, - finalized_epoch, far_future_epoch, ); - if query - .status - .as_ref() - .map_or(true, |statuses| statuses.0.contains(&status)) - { + let status_matches = + query.status.as_ref().map_or(true, |statuses| { + statuses.0.contains(&status) + || statuses.0.contains(&status.superstatus()) + }); + + if status_matches { Some(api_types::ValidatorData { index: index as u64, balance: *balance, @@ -577,16 +577,14 @@ pub fn serve( let validator = state.validators.get(index)?; let balance = *state.balances.get(index)?; let epoch = state.current_epoch(); - let finalized_epoch = state.finalized_checkpoint.epoch; let far_future_epoch = chain.spec.far_future_epoch; Some(api_types::ValidatorData { index: index as u64, balance, status: api_types::ValidatorStatus::from_validator( - Some(validator), + validator, epoch, - finalized_epoch, far_future_epoch, ), validator: validator.clone(), diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 1e3bb9651..65441b3e8 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -638,7 +638,6 @@ impl ApiTester { let expected = state_opt.map(|state| { let epoch = state.current_epoch(); - let finalized_epoch = state.finalized_checkpoint.epoch; let far_future_epoch = self.chain.spec.far_future_epoch; let mut validators = Vec::with_capacity(validator_indices.len()); @@ -649,12 +648,14 @@ impl ApiTester { } let validator = state.validators[i as usize].clone(); let status = ValidatorStatus::from_validator( - Some(&validator), + &validator, epoch, - finalized_epoch, far_future_epoch, ); - if statuses.contains(&status) || statuses.is_empty() { + if statuses.contains(&status) + || statuses.is_empty() + || statuses.contains(&status.superstatus()) + { validators.push(ValidatorData { index: i as u64, balance: state.balances[i as usize], @@ -706,16 +707,14 @@ impl ApiTester { let expected = { let epoch = state.current_epoch(); - let finalized_epoch = state.finalized_checkpoint.epoch; let far_future_epoch = self.chain.spec.far_future_epoch; ValidatorData { index: i as u64, balance: state.balances[i], status: ValidatorStatus::from_validator( - Some(&validator), + &validator, epoch, - finalized_epoch, far_future_epoch, ), validator: validator.clone(), @@ -1444,18 +1443,19 @@ impl ApiTester { vec![], vec![ValidatorStatus::Active], vec![ - ValidatorStatus::Unknown, - ValidatorStatus::WaitingForEligibility, - ValidatorStatus::WaitingForFinality, - ValidatorStatus::WaitingInQueue, - ValidatorStatus::StandbyForActive, - ValidatorStatus::Active, - ValidatorStatus::ActiveAwaitingVoluntaryExit, - ValidatorStatus::ActiveAwaitingSlashedExit, - ValidatorStatus::ExitedVoluntarily, + ValidatorStatus::PendingInitialized, + ValidatorStatus::PendingQueued, + ValidatorStatus::ActiveOngoing, + ValidatorStatus::ActiveExiting, + ValidatorStatus::ActiveSlashed, + ValidatorStatus::ExitedUnslashed, ValidatorStatus::ExitedSlashed, - ValidatorStatus::Withdrawable, - ValidatorStatus::Withdrawn, + ValidatorStatus::WithdrawalPossible, + ValidatorStatus::WithdrawalDone, + ValidatorStatus::Active, + ValidatorStatus::Pending, + ValidatorStatus::Exited, + ValidatorStatus::Withdrawal, ], ]; interesting diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index f905025be..a00d1e769 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -246,66 +246,84 @@ pub struct ValidatorBalanceData { pub balance: u64, } -// TODO: This does not currently match the spec, but I'm going to try and change the spec using +// Implemented according to what is described here: +// +// https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ +// +// We expect this to be updated in v2 of the standard api to // this proposal: // // https://hackmd.io/bQxMDRt1RbS1TLno8K4NPg?view #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ValidatorStatus { - Unknown, - WaitingForEligibility, - WaitingForFinality, - WaitingInQueue, - StandbyForActive, - Active, - ActiveAwaitingVoluntaryExit, - ActiveAwaitingSlashedExit, - ExitedVoluntarily, + PendingInitialized, + PendingQueued, + ActiveOngoing, + ActiveExiting, + ActiveSlashed, + ExitedUnslashed, ExitedSlashed, - Withdrawable, - Withdrawn, + WithdrawalPossible, + WithdrawalDone, + Active, + Pending, + Exited, + Withdrawal, } impl ValidatorStatus { - pub fn from_validator( - validator_opt: Option<&Validator>, - epoch: Epoch, - finalized_epoch: Epoch, - far_future_epoch: Epoch, - ) -> Self { - if let Some(validator) = validator_opt { - if validator.is_withdrawable_at(epoch) { - ValidatorStatus::Withdrawable - } else if validator.is_exited_at(epoch) { + pub fn from_validator(validator: &Validator, epoch: Epoch, far_future_epoch: Epoch) -> Self { + if validator.is_withdrawable_at(epoch) { + if validator.effective_balance == 0 { + ValidatorStatus::WithdrawalDone + } else { + ValidatorStatus::WithdrawalPossible + } + } else if validator.is_exited_at(epoch) && epoch < validator.withdrawable_epoch { + if validator.slashed { + ValidatorStatus::ExitedSlashed + } else { + ValidatorStatus::ExitedUnslashed + } + } else if validator.is_active_at(epoch) { + if validator.exit_epoch < far_future_epoch { if validator.slashed { - ValidatorStatus::ExitedSlashed + ValidatorStatus::ActiveSlashed } else { - ValidatorStatus::ExitedVoluntarily - } - } else if validator.is_active_at(epoch) { - if validator.exit_epoch < far_future_epoch { - if validator.slashed { - ValidatorStatus::ActiveAwaitingSlashedExit - } else { - ValidatorStatus::ActiveAwaitingVoluntaryExit - } - } else { - ValidatorStatus::Active - } - } else if validator.activation_epoch < far_future_epoch { - ValidatorStatus::StandbyForActive - } else if validator.activation_eligibility_epoch < far_future_epoch { - if finalized_epoch < validator.activation_eligibility_epoch { - ValidatorStatus::WaitingForFinality - } else { - ValidatorStatus::WaitingInQueue + ValidatorStatus::ActiveExiting } } else { - ValidatorStatus::WaitingForEligibility + ValidatorStatus::ActiveOngoing } + // `pending` statuses are specified as validators where `validator.activation_epoch > current_epoch`. + // If this code is reached, this criteria must have been met because `validator.is_active_at(epoch)`, + // `validator.is_exited_at(epoch)`, and `validator.is_withdrawable_at(epoch)` all returned false. + } else if validator.activation_eligibility_epoch == far_future_epoch { + ValidatorStatus::PendingInitialized } else { - ValidatorStatus::Unknown + ValidatorStatus::PendingQueued + } + } + + pub fn superstatus(&self) -> Self { + match self { + ValidatorStatus::PendingInitialized | ValidatorStatus::PendingQueued => { + ValidatorStatus::Pending + } + ValidatorStatus::ActiveOngoing + | ValidatorStatus::ActiveExiting + | ValidatorStatus::ActiveSlashed => ValidatorStatus::Active, + ValidatorStatus::ExitedUnslashed | ValidatorStatus::ExitedSlashed => { + ValidatorStatus::Exited + } + ValidatorStatus::WithdrawalPossible | ValidatorStatus::WithdrawalDone => { + ValidatorStatus::Withdrawal + } + ValidatorStatus::Active + | ValidatorStatus::Pending + | ValidatorStatus::Exited + | ValidatorStatus::Withdrawal => *self, } } } @@ -315,18 +333,19 @@ impl FromStr for ValidatorStatus { fn from_str(s: &str) -> Result { match s { - "unknown" => Ok(ValidatorStatus::Unknown), - "waiting_for_eligibility" => Ok(ValidatorStatus::WaitingForEligibility), - "waiting_for_finality" => Ok(ValidatorStatus::WaitingForFinality), - "waiting_in_queue" => Ok(ValidatorStatus::WaitingInQueue), - "standby_for_active" => Ok(ValidatorStatus::StandbyForActive), - "active" => Ok(ValidatorStatus::Active), - "active_awaiting_voluntary_exit" => Ok(ValidatorStatus::ActiveAwaitingVoluntaryExit), - "active_awaiting_slashed_exit" => Ok(ValidatorStatus::ActiveAwaitingSlashedExit), - "exited_voluntarily" => Ok(ValidatorStatus::ExitedVoluntarily), + "pending_initialized" => Ok(ValidatorStatus::PendingInitialized), + "pending_queued" => Ok(ValidatorStatus::PendingQueued), + "active_ongoing" => Ok(ValidatorStatus::ActiveOngoing), + "active_exiting" => Ok(ValidatorStatus::ActiveExiting), + "active_slashed" => Ok(ValidatorStatus::ActiveSlashed), + "exited_unslashed" => Ok(ValidatorStatus::ExitedUnslashed), "exited_slashed" => Ok(ValidatorStatus::ExitedSlashed), - "withdrawable" => Ok(ValidatorStatus::Withdrawable), - "withdrawn" => Ok(ValidatorStatus::Withdrawn), + "withdrawal_possible" => Ok(ValidatorStatus::WithdrawalPossible), + "withdrawal_done" => Ok(ValidatorStatus::WithdrawalDone), + "active" => Ok(ValidatorStatus::Active), + "pending" => Ok(ValidatorStatus::Pending), + "exited" => Ok(ValidatorStatus::Exited), + "withdrawal" => Ok(ValidatorStatus::Withdrawal), _ => Err(format!("{} cannot be parsed as a validator status.", s)), } } @@ -335,20 +354,19 @@ impl FromStr for ValidatorStatus { impl fmt::Display for ValidatorStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ValidatorStatus::Unknown => write!(f, "unknown"), - ValidatorStatus::WaitingForEligibility => write!(f, "waiting_for_eligibility"), - ValidatorStatus::WaitingForFinality => write!(f, "waiting_for_finality"), - ValidatorStatus::WaitingInQueue => write!(f, "waiting_in_queue"), - ValidatorStatus::StandbyForActive => write!(f, "standby_for_active"), - ValidatorStatus::Active => write!(f, "active"), - ValidatorStatus::ActiveAwaitingVoluntaryExit => { - write!(f, "active_awaiting_voluntary_exit") - } - ValidatorStatus::ActiveAwaitingSlashedExit => write!(f, "active_awaiting_slashed_exit"), - ValidatorStatus::ExitedVoluntarily => write!(f, "exited_voluntarily"), + ValidatorStatus::PendingInitialized => write!(f, "pending_initialized"), + ValidatorStatus::PendingQueued => write!(f, "pending_queued"), + ValidatorStatus::ActiveOngoing => write!(f, "active_ongoing"), + ValidatorStatus::ActiveExiting => write!(f, "active_exiting"), + ValidatorStatus::ActiveSlashed => write!(f, "active_slashed"), + ValidatorStatus::ExitedUnslashed => write!(f, "exited_unslashed"), ValidatorStatus::ExitedSlashed => write!(f, "exited_slashed"), - ValidatorStatus::Withdrawable => write!(f, "withdrawable"), - ValidatorStatus::Withdrawn => write!(f, "withdrawn"), + ValidatorStatus::WithdrawalPossible => write!(f, "withdrawal_possible"), + ValidatorStatus::WithdrawalDone => write!(f, "withdrawal_done"), + ValidatorStatus::Active => write!(f, "active"), + ValidatorStatus::Pending => write!(f, "pending"), + ValidatorStatus::Exited => write!(f, "exited"), + ValidatorStatus::Withdrawal => write!(f, "withdrawal"), } } }