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 <seananderson33@gmail.com>
This commit is contained in:
realbigsean 2021-02-24 04:15:13 +00:00
parent a764c3b247
commit 5bc93869c8
3 changed files with 113 additions and 97 deletions

View File

@ -498,7 +498,6 @@ pub fn serve<T: BeaconChainTypes>(
state_id state_id
.map_state(&chain, |state| { .map_state(&chain, |state| {
let epoch = state.current_epoch(); let epoch = state.current_epoch();
let finalized_epoch = state.finalized_checkpoint.epoch;
let far_future_epoch = chain.spec.far_future_epoch; let far_future_epoch = chain.spec.far_future_epoch;
Ok(state Ok(state
@ -522,17 +521,18 @@ pub fn serve<T: BeaconChainTypes>(
// filter by status(es) if provided and map the result // filter by status(es) if provided and map the result
.filter_map(|(index, (validator, balance))| { .filter_map(|(index, (validator, balance))| {
let status = api_types::ValidatorStatus::from_validator( let status = api_types::ValidatorStatus::from_validator(
Some(validator), validator,
epoch, epoch,
finalized_epoch,
far_future_epoch, far_future_epoch,
); );
if query let status_matches =
.status query.status.as_ref().map_or(true, |statuses| {
.as_ref() statuses.0.contains(&status)
.map_or(true, |statuses| statuses.0.contains(&status)) || statuses.0.contains(&status.superstatus())
{ });
if status_matches {
Some(api_types::ValidatorData { Some(api_types::ValidatorData {
index: index as u64, index: index as u64,
balance: *balance, balance: *balance,
@ -577,16 +577,14 @@ pub fn serve<T: BeaconChainTypes>(
let validator = state.validators.get(index)?; let validator = state.validators.get(index)?;
let balance = *state.balances.get(index)?; let balance = *state.balances.get(index)?;
let epoch = state.current_epoch(); let epoch = state.current_epoch();
let finalized_epoch = state.finalized_checkpoint.epoch;
let far_future_epoch = chain.spec.far_future_epoch; let far_future_epoch = chain.spec.far_future_epoch;
Some(api_types::ValidatorData { Some(api_types::ValidatorData {
index: index as u64, index: index as u64,
balance, balance,
status: api_types::ValidatorStatus::from_validator( status: api_types::ValidatorStatus::from_validator(
Some(validator), validator,
epoch, epoch,
finalized_epoch,
far_future_epoch, far_future_epoch,
), ),
validator: validator.clone(), validator: validator.clone(),

View File

@ -638,7 +638,6 @@ impl ApiTester {
let expected = state_opt.map(|state| { let expected = state_opt.map(|state| {
let epoch = state.current_epoch(); let epoch = state.current_epoch();
let finalized_epoch = state.finalized_checkpoint.epoch;
let far_future_epoch = self.chain.spec.far_future_epoch; let far_future_epoch = self.chain.spec.far_future_epoch;
let mut validators = Vec::with_capacity(validator_indices.len()); let mut validators = Vec::with_capacity(validator_indices.len());
@ -649,12 +648,14 @@ impl ApiTester {
} }
let validator = state.validators[i as usize].clone(); let validator = state.validators[i as usize].clone();
let status = ValidatorStatus::from_validator( let status = ValidatorStatus::from_validator(
Some(&validator), &validator,
epoch, epoch,
finalized_epoch,
far_future_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 { validators.push(ValidatorData {
index: i as u64, index: i as u64,
balance: state.balances[i as usize], balance: state.balances[i as usize],
@ -706,16 +707,14 @@ impl ApiTester {
let expected = { let expected = {
let epoch = state.current_epoch(); let epoch = state.current_epoch();
let finalized_epoch = state.finalized_checkpoint.epoch;
let far_future_epoch = self.chain.spec.far_future_epoch; let far_future_epoch = self.chain.spec.far_future_epoch;
ValidatorData { ValidatorData {
index: i as u64, index: i as u64,
balance: state.balances[i], balance: state.balances[i],
status: ValidatorStatus::from_validator( status: ValidatorStatus::from_validator(
Some(&validator), &validator,
epoch, epoch,
finalized_epoch,
far_future_epoch, far_future_epoch,
), ),
validator: validator.clone(), validator: validator.clone(),
@ -1444,18 +1443,19 @@ impl ApiTester {
vec![], vec![],
vec![ValidatorStatus::Active], vec![ValidatorStatus::Active],
vec![ vec![
ValidatorStatus::Unknown, ValidatorStatus::PendingInitialized,
ValidatorStatus::WaitingForEligibility, ValidatorStatus::PendingQueued,
ValidatorStatus::WaitingForFinality, ValidatorStatus::ActiveOngoing,
ValidatorStatus::WaitingInQueue, ValidatorStatus::ActiveExiting,
ValidatorStatus::StandbyForActive, ValidatorStatus::ActiveSlashed,
ValidatorStatus::Active, ValidatorStatus::ExitedUnslashed,
ValidatorStatus::ActiveAwaitingVoluntaryExit,
ValidatorStatus::ActiveAwaitingSlashedExit,
ValidatorStatus::ExitedVoluntarily,
ValidatorStatus::ExitedSlashed, ValidatorStatus::ExitedSlashed,
ValidatorStatus::Withdrawable, ValidatorStatus::WithdrawalPossible,
ValidatorStatus::Withdrawn, ValidatorStatus::WithdrawalDone,
ValidatorStatus::Active,
ValidatorStatus::Pending,
ValidatorStatus::Exited,
ValidatorStatus::Withdrawal,
], ],
]; ];
interesting interesting

View File

@ -246,66 +246,84 @@ pub struct ValidatorBalanceData {
pub balance: u64, 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: // this proposal:
// //
// https://hackmd.io/bQxMDRt1RbS1TLno8K4NPg?view // https://hackmd.io/bQxMDRt1RbS1TLno8K4NPg?view
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum ValidatorStatus { pub enum ValidatorStatus {
Unknown, PendingInitialized,
WaitingForEligibility, PendingQueued,
WaitingForFinality, ActiveOngoing,
WaitingInQueue, ActiveExiting,
StandbyForActive, ActiveSlashed,
Active, ExitedUnslashed,
ActiveAwaitingVoluntaryExit,
ActiveAwaitingSlashedExit,
ExitedVoluntarily,
ExitedSlashed, ExitedSlashed,
Withdrawable, WithdrawalPossible,
Withdrawn, WithdrawalDone,
Active,
Pending,
Exited,
Withdrawal,
} }
impl ValidatorStatus { impl ValidatorStatus {
pub fn from_validator( pub fn from_validator(validator: &Validator, epoch: Epoch, far_future_epoch: Epoch) -> Self {
validator_opt: Option<&Validator>, if validator.is_withdrawable_at(epoch) {
epoch: Epoch, if validator.effective_balance == 0 {
finalized_epoch: Epoch, ValidatorStatus::WithdrawalDone
far_future_epoch: Epoch, } else {
) -> Self { ValidatorStatus::WithdrawalPossible
if let Some(validator) = validator_opt { }
if validator.is_withdrawable_at(epoch) { } else if validator.is_exited_at(epoch) && epoch < validator.withdrawable_epoch {
ValidatorStatus::Withdrawable if validator.slashed {
} else if validator.is_exited_at(epoch) { ValidatorStatus::ExitedSlashed
} else {
ValidatorStatus::ExitedUnslashed
}
} else if validator.is_active_at(epoch) {
if validator.exit_epoch < far_future_epoch {
if validator.slashed { if validator.slashed {
ValidatorStatus::ExitedSlashed ValidatorStatus::ActiveSlashed
} else { } else {
ValidatorStatus::ExitedVoluntarily ValidatorStatus::ActiveExiting
}
} 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
} }
} else { } 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 { } 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<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"unknown" => Ok(ValidatorStatus::Unknown), "pending_initialized" => Ok(ValidatorStatus::PendingInitialized),
"waiting_for_eligibility" => Ok(ValidatorStatus::WaitingForEligibility), "pending_queued" => Ok(ValidatorStatus::PendingQueued),
"waiting_for_finality" => Ok(ValidatorStatus::WaitingForFinality), "active_ongoing" => Ok(ValidatorStatus::ActiveOngoing),
"waiting_in_queue" => Ok(ValidatorStatus::WaitingInQueue), "active_exiting" => Ok(ValidatorStatus::ActiveExiting),
"standby_for_active" => Ok(ValidatorStatus::StandbyForActive), "active_slashed" => Ok(ValidatorStatus::ActiveSlashed),
"active" => Ok(ValidatorStatus::Active), "exited_unslashed" => Ok(ValidatorStatus::ExitedUnslashed),
"active_awaiting_voluntary_exit" => Ok(ValidatorStatus::ActiveAwaitingVoluntaryExit),
"active_awaiting_slashed_exit" => Ok(ValidatorStatus::ActiveAwaitingSlashedExit),
"exited_voluntarily" => Ok(ValidatorStatus::ExitedVoluntarily),
"exited_slashed" => Ok(ValidatorStatus::ExitedSlashed), "exited_slashed" => Ok(ValidatorStatus::ExitedSlashed),
"withdrawable" => Ok(ValidatorStatus::Withdrawable), "withdrawal_possible" => Ok(ValidatorStatus::WithdrawalPossible),
"withdrawn" => Ok(ValidatorStatus::Withdrawn), "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)), _ => Err(format!("{} cannot be parsed as a validator status.", s)),
} }
} }
@ -335,20 +354,19 @@ impl FromStr for ValidatorStatus {
impl fmt::Display for ValidatorStatus { impl fmt::Display for ValidatorStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
ValidatorStatus::Unknown => write!(f, "unknown"), ValidatorStatus::PendingInitialized => write!(f, "pending_initialized"),
ValidatorStatus::WaitingForEligibility => write!(f, "waiting_for_eligibility"), ValidatorStatus::PendingQueued => write!(f, "pending_queued"),
ValidatorStatus::WaitingForFinality => write!(f, "waiting_for_finality"), ValidatorStatus::ActiveOngoing => write!(f, "active_ongoing"),
ValidatorStatus::WaitingInQueue => write!(f, "waiting_in_queue"), ValidatorStatus::ActiveExiting => write!(f, "active_exiting"),
ValidatorStatus::StandbyForActive => write!(f, "standby_for_active"), ValidatorStatus::ActiveSlashed => write!(f, "active_slashed"),
ValidatorStatus::Active => write!(f, "active"), ValidatorStatus::ExitedUnslashed => write!(f, "exited_unslashed"),
ValidatorStatus::ActiveAwaitingVoluntaryExit => {
write!(f, "active_awaiting_voluntary_exit")
}
ValidatorStatus::ActiveAwaitingSlashedExit => write!(f, "active_awaiting_slashed_exit"),
ValidatorStatus::ExitedVoluntarily => write!(f, "exited_voluntarily"),
ValidatorStatus::ExitedSlashed => write!(f, "exited_slashed"), ValidatorStatus::ExitedSlashed => write!(f, "exited_slashed"),
ValidatorStatus::Withdrawable => write!(f, "withdrawable"), ValidatorStatus::WithdrawalPossible => write!(f, "withdrawal_possible"),
ValidatorStatus::Withdrawn => write!(f, "withdrawn"), 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"),
} }
} }
} }