owned this note
owned this note
Published
Linked with GitHub
# Validator Status
This document contains an alterative proposal for the `status` field of the `/eth/v1/beacon/states/{state_id}/validator` Eth2.0 API endpoint. The original proposal is sufficient, however I believe this new proposal has the following advantages:
- More detail during the intial phases. E.g., instead of just `pending_queued` we have `waiting_for_finality -> waiting_in_queue -> standby_for_active`.
- Specifically, the `waiting_for_finality` state will be useful for users if the network experiences a long period without finality.
- The `next_state_epoch: Epoch` values allows the user to know when they can expect the next state to change. Applications can translate these epochs into times and display countdowns to users.
- A simple example implementation shows how to convert a `Validator` object into a status.
### References
- Eth2.0 API spec: https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateValidators- Eth2.0 API spec: https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateValidators
- `status` field spec: https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ)
- Works by Jim McDonald: https://www.attestant.io/posts/understanding-the-validator-lifecycle/
### Notes
- This doc is quite long, but you only really need to read the summary to get the jist of it. The rest of the doc contains resources for docs/implementation.
## Summary (TL;DR)
There are twelve states, which form a [simple DAG](#State-Diagram):
1. `Unknown`
- The validator does not exist in the validator registry.
1. `WaitingForEligibility`
- The validator does not have enough funds.
1. `WaitingForFinality`
- Once the chain finalizes, the validator will become eligible.
1. `WaitingInQueue`
- The validator is eligible to be activated, however it must wait for other validators to activate first.
- *Note: I think it's possible to add a `next_state_epoch` value here, but it's not trivial so I've skipped it.*
1. `StandbyForActive(next_state_epoch)`
- The validator has reached the front of the activation queue and has been assigned an activation epoch (`next_state_epoch`).
1. `Active`
- The validator is active, unslashed and has not had an exit included in the chain. The validator is expected to perform duties until they exit voluntarily or are slashed.
1. `ActiveAwaitingVoluntaryExit(next_state_epoch)`
- The validator is active, but a `SignedVoluntaryExit` has been included on-chain. The validator is expected to perform duties until `next_state_epoch`.
1. `ActiveAwaitingSlashedExit(next_state_epoch)`
- The validator is active, but it has been slashed. The validator is expected to perform duties until `next_state_epoch`.
1. `ExitedVoluntarily(next_state_epoch)`
- The validator exited voluntarily and is no longer required to perform duties. They will become withdrawable at `next_state_epoch`.
1. `ExitedSlashed(next_state_epoch)`
- The validator was slashed and exited by the protocol. They will become withdrawable at `next_state_epoch`.
1. `Withdrawable`
- The validator is eligible to withdraw thier balance.
1. `Withdrawn`
- The validator has withdrawn their ETH. This is not possible in phase 0.
## JSON Representation Examples
An example JSON representation is shown for each state:
### Unknown
```json
{
"state": "unknown",
"next_state_epoch": null
}
```
### WaitingForEligibility
```json
{
"state": "waiting_for_eligibility",
"next_state_epoch": null
}
```
### WaitingForFinality
```json
{
"state": "waiting_for_finality",
"next_state_epoch": null
}
```
### WaitingInQueue
```json
{
"state": "waiting_in_queue",
"next_state_epoch": null
}
```
### StandbyForActive
```json
{
"state": "standby_for_active",
"next_state_epoch": "42"
}
```
### Active
```json
{
"state": "active",
"next_state_epoch": null
}
```
### ActiveAwaitingVoluntaryExit
```json
{
"state": "active",
"next_state_epoch": "42"
}
```
### ActiveAwaitingSlashedExit
```json
{
"state": "active_awaiting_slashed_exit",
"next_state_epoch": "42"
}
```
### ExitedVoluntarily
```json
{
"state": "active_awaiting_voluntary_exit",
"next_state_epoch": "42"
}
```
### ExitedSlashed
```json
{
"state": "active_awaiting_slashed_exit",
"next_state_epoch": "42"
}
```
### Withdrawable
```json
{
"state": "withdrawable",
"next_state_epoch": null
}
```
### Withdrawn
```json
{
"state": "withdrawable",
"next_state_epoch": null
}
```
## State Diagram
Each of the states is represented below:
```mermaid
stateDiagram
[*] --> validator_unknown
validator_unknown --> waiting_for_eligibility : Eth1 deposit
waiting_for_eligibility --> waiting_for_finality : 32 ETH balance
waiting_for_finality --> waiting_in_queue : 32 ETH balance is finalized
waiting_in_queue --> standby_for_active : Queue position reached
standby_for_active --> active : Activation epoch reached
active --> active_awaiting_voluntary_exit : Voluntary exit
active --> active_awaiting_slashed_exit : Slashing
active_awaiting_slashed_exit --> exited_slashed : Exit epoch reached
active_awaiting_voluntary_exit --> exited_voluntarily : Exit epoch reached
exited_slashed --> withdrawable: Withdrawable epoch reached
exited_voluntarily --> withdrawable: Withdrawable epoch reached
withdrawable --> withdrawn: Withdraw (after phase 0)
withdrawn --> [*]
```
## Example Implementation
Here is an example implementation in Rust, hopefully it's easy to ready without Rust knowledge. The `Validator` struct is as it appears in the eth2 spec.
*This implementation has not yet been reviewed or tested.*
```rust
enum ValidatorStatus {
Unknown,
WaitingForEligibility,
WaitingForFinality,
WaitingInQueue,
StandbyForActive(Epoch),
Active,
ActiveAwaitingVoluntaryExit(Epoch),
ActiveAwaitingSlashedExit(Epoch),
ExitedVoluntarily(Epoch),
ExitedSlashed(Epoch),
Withdrawable,
Withdrawn,
}
impl ValidatorStatus {
pub fn from_validator(
validator_opt: Option<&Validator>,
epoch: Epoch, // Generally the current 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) {
if validator.slashed {
ValidatorStatus::ExitedSlashed(validator.withdrawable_epoch)
} else {
ValidatorStatus::ExitedVoluntarily(validator.withdrawable_epoch)
}
} else if validator.is_active_at(epoch) {
if validator.exit_epoch < far_future_epoch {
if validator.slashed {
ValidatorStatus::ActiveAwaitingSlashedExit(validator.exit_epoch)
} else {
ValidatorStatus::ActiveAwaitingVoluntaryExit(validator.exit_epoch)
}
} else {
ValidatorStatus::Active
}
} else {
if validator.activation_epoch < far_future_epoch {
ValidatorStatus::StandbyForActive(validator.activation_epoch)
} else if validator.activation_eligibility_epoch < far_future_epoch {
if finalized_epoch < validator.activation_eligibility_epoch {
ValidatorStatus::WaitingForFinality
} else {
ValidatorStatus::WaitingInQueue
}
} else {
ValidatorStatus::WaitingForEligibility
}
}
} else {
ValidatorStatus::Unknown
}
}
}
```
## Excruciating Detail
This section contains some more UX detail about each state.
### 1. `validator_unknown`
The Beacon Chain is completely unaware of the validator. This might be because
the validator:
- has never deposited.
- has submitted a deposit transaction, but it has not yet been included in the
beacon chain.
#### Progression
The validator can progress past this phase once the Beacon Chain has processed
the `Deposit` message from the Eth1 deposit contract.
#### User input required for progression
User input is required to submit the deposit and have it included in the eth1 chain. After that, it is up to the beacon chain.
#### Time-frame for progression
After the deposit is included in an Eth1 block, it generally takes 4 to 7.4
hours for the beacon chain to become aware of that Eth1 block. This is because
the beacon chain follows Eth1 at a distance of 1,024 blocks (approximately 4
hours) and validators vote on eth1 blocks in a 32 epoch period (~3.4 hours).
See [FAQs: Waiting for the beacon chain to detect the Eth1
deposit](./faq.md#1-waiting-for-the-beacon-chain-to-detect-the-eth1-deposit)
for more detail.
After the beacon chain becomes aware of the Eth1 block containing the validator
deposit, it can only include 16 validators per block. If there is a high rate
of deposits then this 16-validator limit may cause further delays.
### 2. `waiting_for_eligibility`
A `Deposit` is included in a `BeaconBlock`, causing the Beacon Chain to become
aware of the validator.
#### Progression
The validator can progress past this phase when:
- They have deposited at least 32 ETH.
- The beacon chain has transitioned into the next epoch after the `Deposit`
which afforded the validator adequate balance was included in a block. (No more
than 6.4 minutes).
#### User input required for progression
User input is required to submit 32 ETH (or more) in deposits and have those
included in the Eth1 chain. After that, it is up to the existing beacon chain
block producers to vote in new Eth1 blocks.
#### Time-frame for progression
This state will remain indefinitely until the validator submits adequate
deposits. After the user has submitted their deposits, it will take no more
than one epoch (6.4 minutes) for the beacon chain to detect that the validator
has sufficient balance and transition to the next state.
### 3. `waiting_for_finality`
The Beacon Chain has observed that the validator *could* become eligible and it
is waiting for those conditions to become finalized.
#### Progression
The validator can progress past this phase when the Beacon Chain finalizes the
epoch *following* the one where they became eligible for activation. E.g., if
the validator became eligible in epoch 2, we must wait for epoch 3 to
finalize.
#### User input required for progression
No.
#### Time-frame for progression
If the beacon chain is finalizing as usual, this should be approximately 3
epochs (~20 minutes). However, this will be delayed indefinitely if the chain
is not finalizing.
### 4. `waiting_in_queue`
The conditions which allow the validator to become active are finalized. The
validator is now waiting in the activation queue, awaiting its turn to be
activated.
#### Progression
The validator can progress past this phase once all prior validators in the
queue have been activated.
#### User input required for progression
No.
#### Time-frame for progression
Up until there are approximately 330,000 validators, only 4 validators can be
activated per epoch (6.4 minutes). This activation rate increases very slowly
as more validators are added to the chain.
### 5. `standby_for_active`
The conditions which allow the validator to become active are finalized. The
validator is now waiting in the activation queue, awaiting its turn to be
activated.
#### Progression
The validator must wait for the `activation_epoch` that was determined at the
beginning of this period.
#### User input required for progression
No.
#### Time-frame for progression
The validator will remain in this state for 5 epochs (32 minutes) before
progressing to the next state.
### 6. `active`
The validator is now active and, for the first time, is rewarded for including
attestations and blocks in the beacon chain. It will be penalized if it fails
to have attestations included in the chain.
#### Progression
The validator can progress past this phase for two reasons:
- The validator submits a signed `VoluntaryExit` message (i.e., they choose to
exit).
- The validator is slashed.
#### User input required for progression
Yes, the user may choose to submit a `VoluntaryExit` to start the exit. Or, the
validator must sign two conflicting messages and then have those messages
submitted in a `ProposerSlashing` or `AttesterSlashing`.
#### Time-frame for progression
The user remains in this state indefinitely until they exit or are slashed.
### 7a. `active_awaiting_slashed_exit`
The validator has produced conflicting messages and they have been included in
the chain via a `ProposerSlashing` or `AttesterSlashing`. It will not be
rewarded for producing attestations but may be required to (and rewarded for)
producing blocks.
#### Progression
The validator must wait until the exit epoch arrives. This exit epoch was
determined by the protocol when the exit was initiated.
#### User input required for progression
No.
#### Time-frame for progression
The validator will remain in this state for 5 epochs (32 minutes) before
progressing to the next state.
### 7b. `active_awaiting_voluntary_exit`
The validator has voluntarily started exiting. As with the active state, the
validator will be rewarded for including attestations and blocks in the chain
and penalized for failing to include attestations.
#### Progression
The validator must wait until the exit epoch arrives. This exit epoch was
decided by the protocol when the exit was initiated.
#### User input required for progression
No.
#### Time-frame for progression
The validator will remain in this state for 5 epochs (32 minutes) before
progressing to the next state.
### 8. `exited_voluntarily` & `exited_slashed`
The validator has exited and is no longer permitted to include attestations or
blocks in the beacon chain.
#### Progression
The validator must wait until the withdrawable epoch arrives. This withdrawable epoch was
decided by the protocol when the exit was initiated.
#### User input required for progression
No.
#### Time-frame for progression
The validator will remain in this state for 256 epochs (~27 hours) before
progressing to the next state.
### 9. `withdrawable`
The validator has exited and waited long enough to be withdrawable.
From the perspective of the Beacon Chain, this is the end of the road for a
validator. In phase 0 it is unable to withdraw and must await later phases.
#### Progression
N/A.
#### User input required for progression
N/A.
#### Time-frame for progression
N/A.