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:
pending_queued
we have waiting_for_finality -> waiting_in_queue -> standby_for_active
.
waiting_for_finality
state will be useful for users if the network experiences a long period without finality.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.Validator
object into a status.status
field spec: https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ)There are twelve states, which form a simple DAG:
Unknown
WaitingForEligibility
WaitingForFinality
WaitingInQueue
next_state_epoch
value here, but it's not trivial so I've skipped it.StandbyForActive(next_state_epoch)
next_state_epoch
).Active
ActiveAwaitingVoluntaryExit(next_state_epoch)
SignedVoluntaryExit
has been included on-chain. The validator is expected to perform duties until next_state_epoch
.ActiveAwaitingSlashedExit(next_state_epoch)
next_state_epoch
.ExitedVoluntarily(next_state_epoch)
next_state_epoch
.ExitedSlashed(next_state_epoch)
next_state_epoch
.Withdrawable
Withdrawn
An example JSON representation is shown for each state:
{
"state": "unknown",
"next_state_epoch": null
}
{
"state": "waiting_for_eligibility",
"next_state_epoch": null
}
{
"state": "waiting_for_finality",
"next_state_epoch": null
}
{
"state": "waiting_in_queue",
"next_state_epoch": null
}
{
"state": "standby_for_active",
"next_state_epoch": "42"
}
{
"state": "active",
"next_state_epoch": null
}
{
"state": "active",
"next_state_epoch": "42"
}
{
"state": "active_awaiting_slashed_exit",
"next_state_epoch": "42"
}
{
"state": "active_awaiting_voluntary_exit",
"next_state_epoch": "42"
}
{
"state": "active_awaiting_slashed_exit",
"next_state_epoch": "42"
}
{
"state": "withdrawable",
"next_state_epoch": null
}
{
"state": "withdrawable",
"next_state_epoch": null
}
Each of the states is represented below:
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 --> [*]
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.
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
}
}
}
This section contains some more UX detail about each state.
validator_unknown
The Beacon Chain is completely unaware of the validator. This might be because
the validator:
The validator can progress past this phase once the Beacon Chain has processed
the Deposit
message from the Eth1 deposit contract.
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.
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
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.
waiting_for_eligibility
A Deposit
is included in a BeaconBlock
, causing the Beacon Chain to become
aware of the validator.
The validator can progress past this phase when:
Deposit
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.
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.
waiting_for_finality
The Beacon Chain has observed that the validator could become eligible and it
is waiting for those conditions to become finalized.
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.
No.
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.
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.
The validator can progress past this phase once all prior validators in the
queue have been activated.
No.
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.
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.
The validator must wait for the activation_epoch
that was determined at the
beginning of this period.
No.
The validator will remain in this state for 5 epochs (32 minutes) before
progressing to the next state.
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.
The validator can progress past this phase for two reasons:
VoluntaryExit
message (i.e., they choose toYes, 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
.
The user remains in this state indefinitely until they exit or are slashed.
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.
The validator must wait until the exit epoch arrives. This exit epoch was
determined by the protocol when the exit was initiated.
No.
The validator will remain in this state for 5 epochs (32 minutes) before
progressing to the next state.
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.
The validator must wait until the exit epoch arrives. This exit epoch was
decided by the protocol when the exit was initiated.
No.
The validator will remain in this state for 5 epochs (32 minutes) before
progressing to the next state.
exited_voluntarily
& exited_slashed
The validator has exited and is no longer permitted to include attestations or
blocks in the beacon chain.
The validator must wait until the withdrawable epoch arrives. This withdrawable epoch was
decided by the protocol when the exit was initiated.
No.
The validator will remain in this state for 256 epochs (~27 hours) before
progressing to the next state.
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.
N/A.
N/A.
N/A.