Clarify pre-activation validator statuses and add deposit_inqueue for the post-Electra/Fulu deposit pipeline#618
Conversation
Rename pending_initialized -> pending_minBalance and pending_queued -> pending_lookahead in the ValidatorStatus enum, correcting their descriptions for the post-Electra (EIP-6110/EIP-7251) deposit pipeline: pending_minBalance is the pre-eligibility state (effective balance < MIN_ACTIVATION_BALANCE) and pending_lookahead is the fixed 1 + MAX_SEED_LOOKAHEAD delay (no longer a churn-limited queue). Document deposit_inqueue, the deposit-pipeline state for deposits in state.pending_deposits that are not yet validators, observable via getPendingDeposits.
| enum: ["pending_initialized", "pending_queued", "active_ongoing", "active_exiting", "active_slashed", "exited_unslashed", "exited_slashed", "withdrawal_possible", "withdrawal_done"] | ||
| enum: ["pending_minBalance", "pending_lookahead", "active_ongoing", "active_exiting", "active_slashed", "exited_unslashed", "exited_slashed", "withdrawal_possible", "withdrawal_done"] |
There was a problem hiding this comment.
I think this would be breaking in this format.
The 2 ideas that may be better would be either
- v2, seems a simple option but can be annoying for something fairly trivial
- rather than rename, add 2 new entries to the enum, and document the fact that the flow changes (eg pending_minBalance (post electra) pending_queued(pre-electra)) with an extra statement like your note to cover the flow pre / post electra.
Maybe the extra doc with new enums is adequate in this case - thoughts @nflaig ?
There was a problem hiding this comment.
Thanks @rolfyone, agreed. Renaming the existing values outright would break anything matching on the strings (and the ?status= filter); that's the breaking trade-off I called out under Backward compatibility in the description (option A vs B).
Going additive is basically option B there: add pending_minBalance and pending_lookahead as the post-Electra values, keep pending_initialized / pending_queued in the enum as deprecated aliases, and document the pre/post-Electra flow in the description. Nothing breaks for current consumers and we get a clean deprecation window, with the old names removable in a future major once everyone has moved over.
Does that work for you and @nflaig? If you're both good with it I'll update the PR accordingly.
There was a problem hiding this comment.
renaming existing values seems definitely like it requires a v2, adding new value might be debatable, but if we want to allow filtering by these new values then we would also need a v2
this api is heavily used by many consumers outside of clients so it worries me to do any such change without a v2
I also haven't looked into the actual new values in detail, maybe depending on how they work this could be different, a tldr of what this PR does would be helpful and why we need it, the PR description is way to verbose and seems AI generated to me
There was a problem hiding this comment.
Maybe the extra doc with new enums is adequate in this case - thoughts @nflaig ?
could be warranted, or we add the details to the spec itself, it's not great to rely on an external source (like a hackmd) for more details
|
This seems hairy to me- I think this is a big enough departure from an API that simply _GET_s a list of validators from the state that it should be a new endpoint, personally, if accepted at all. E.g. /eth/v1/beacon/states/{state_id}/validator_deposits could return a list of containers that have an optional validator_info for validators that have already had deposits applied, as well as a list of pending deposits for any deposit in pending_deposits with a matching pubkey (valid or not). You would still need to admonish people against assuming the deposit will be successfully processed, or add some sort of |
This PR keeps deposit_inqueue as documentation only (not a ValidatorStatus, no new endpoint), just pointing at the existing getPendingDeposits. I'll add an explicit caveat to that note that pending deposits are unvalidated until applied (presence != guaranteed activation), which I think covers the concern for now. |
| Possible statuses: | ||
| - **pending_initialized** - When the first deposit is processed, but not enough funds are available (or not yet the end of the first epoch) to get validator into the activation queue. | ||
| - **pending_queued** - When validator is waiting to get activated, and have enough funds etc. while in the queue, validator activation epoch keeps changing until it gets to the front and make it through (finalization is a requirement here too). | ||
| - **pending_minBalance** - The validator exists in `state.validators` but its effective balance has not reached `MIN_ACTIVATION_BALANCE` (32 ETH), so it is not yet eligible for activation. Condition: `activation_eligibility_epoch == FAR_FUTURE_EPOCH`. (Formerly `pending_initialized`.) |
There was a problem hiding this comment.
what status do we return in the case that the validator has deposited 32 ETH but was not activated yet (before next epoch boundary)? There will be one epoch where no valid status exists now. Returning that the validator has not reached the min balance is wrong.
as a side note, we probably wanna follow snake_case naming, ie. rename this to pending_min_balance
| description: | | ||
| Possible statuses: | ||
| - **pending_initialized** - When the first deposit is processed, but not enough funds are available (or not yet the end of the first epoch) to get validator into the activation queue. | ||
| - **pending_queued** - When validator is waiting to get activated, and have enough funds etc. while in the queue, validator activation epoch keeps changing until it gets to the front and make it through (finalization is a requirement here too). |
There was a problem hiding this comment.
what status do we use now if activation_epoch == FAR_FUTURE (eligible but not finalized), below for pending_lookahead it notes "this is a fixed 1 + MAX_SEED_LOOKAHEAD delay" but during non-finality this doesn't seem true?
I will probably need @rolfyone to chime in here he knows the Electra changes much better than I do
There was a problem hiding this comment.
pending_queued im not sure is strictly accurate, i don't think there's an activation epoch computed until there are sufficient funds to activate, and until theres sufficient funds it's FAR_FUTURE_EPOCH
The function in question is basically is_eligible_for_activation_queue which requires that your activation balance is sufficient and that you have a activation_epoch of FAR_FUTURE_EPOCH
Summary
The Beacon API
ValidatorStatusenum (types/api.yaml) describes the two pre-activation states with Phase 0 semantics that no longer hold after Electra (EIP-6110 + EIP-7251) and Fulu.This proposal:
pending_initialized→pending_minBalanceand clarifies its definition.pending_queued→pending_lookaheadand corrects its (now inaccurate) description.deposit_inqueue, for deposits that are instate.pending_depositsbut not yet instate.validators.Motivation
The current
pending_queueddescription intypes/api.yamlreads:This describes the Phase 0 activation mechanism, where
process_registry_updateswas the rate-limiting gate: validators competed for a churn-limited number of activation slots per epoch and theiractivation_epochshifted as the queue drained.That is no longer how activation works:
state.pending_deposits, a per-epoch, churn-limited queue drained byprocess_pending_deposits.process_registry_updatesto activate every eligible validator in a single pass (no churn, no per-epoch cap), and moved the churn gate upstream intoprocess_pending_deposits(balance-based, ~256 ETH/epoch).As a result, the two pre-activation states now mean:
pending_initializedis really "the validator exists but its effective balance has not reachedMIN_ACTIVATION_BALANCE(32 ETH), so it is not yet eligible." Its name conveys none of this.pending_queuedis not a queue. Onceactivation_eligibility_epochis finalized,activation_epochis set to a fixedcurrent_epoch + 1 + MAX_SEED_LOOKAHEADand does not change regardless of how many other validators are activating. It is a fixed-duration lookahead, not a queue position.The real churn-limited queue — the one that can hold a deposit for days or weeks — is
state.pending_deposits, and it has no representation in the validator status enum at all, because the deposit is not yet a validator. This is the state operators actually ask about ("where is my deposit / how long until activation"), and the API cannot currently express it.Proposed changes
1. New:
deposit_inqueue(deposit-level, not aValidatorStatus)A deposit sits in
state.pending_depositsand the pubkey is not yet instate.validators.This is where the real churn-limited wait happens.
state.pending_deposits, absent fromstate.validators./eth/v1/beacon/states/{state_id}/validators. It is observable via the existinggetPendingDepositsendpoint (/eth/v1/beacon/states/{state_id}/pending_deposits).We recommend documenting
deposit_inqueueas a deposit-pipeline state rather than adding it to theValidatorStatusenum (which is, by construction, derived from registered-validator fields).2.
pending_initialized→pending_minBalanceValidator exists in
state.validatorsbut its effective balance has not reachedMIN_ACTIVATION_BALANCE(32 ETH), so it is not yet eligible for activation.validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH3.
pending_queued→pending_lookaheadValidator is eligible (
activation_eligibility_epochset and finalized) andactivation_epochis assigned but in the future. This is a fixed1 + MAX_SEED_LOOKAHEADdelay, not a queue.(validator.activation_eligibility_epoch < FAR_FUTURE_EPOCH) and (validator.activation_epoch > current_epoch)Status reference (pre-activation)
deposit_inqueuestate.pending_deposits, not instate.validatorspending_initializedpending_minBalanceactivation_eligibility_epoch == FAR_FUTURE_EPOCHpending_queuedpending_lookaheadactivation_eligibility_epoch < FAR_FUTURE_EPOCHandactivation_epoch > current_epoch1 + MAX_SEED_LOOKAHEADdelay (not a queue)All
active_*,exited_*, andwithdrawal_*statuses are unchanged.Backward compatibility
deposit_inqueueis additive: documenting a deposit-pipeline state and pointing consumers at the existinggetPendingDepositsendpoint breaks nothing.The renames (
pending_initialized→pending_minBalance,pending_queued→pending_lookahead) are breaking for any consumer that matches on the string values (clients, explorers, dashboards, the?status=query filter on/validators). Options for discussion:pending_initialized/pending_queued, fix their descriptions, and adddeposit_inqueue. Zero breakage; lowest friction to merge.This PR is written for option B; if reviewers prefer to avoid the breaking change, it degrades cleanly to option A (the description fixes and
deposit_inqueuestand on their own).References
types/api.yaml— currentValidatorStatusenum and descriptionselectra/beacon-chain.md—process_pending_deposits,process_registry_updatesapis/beacon/states/pending_deposits.yaml— existinggetPendingDepositsendpointProposal by MigaLabs, coordinated by @leobago.