This EIP roughly doubles the consolidation churn, as well as quadrupling the exit churn and restoring its proportionality to total stake (though maintaining the existing cap on activations). Moreover, it routes exits through the consolidation queue when it is shorter than the exit queue (but not viceversa!), increasing the maximum exit throughput by another 75%. The choice of parameters balances maintaining a sufficiently long weak subjectivity period (~7 days, roughly halving the current period) with achieving two goals: allowing for faster consolidation of the validator set, in turn accelerating the timeline to faster finality, and relieving exit queue congestion, improving staking liquidity.
Motivation
EIP-7514 introduced an activation cap of 8 validators per epoch (now 256 ETH per epoch) to prevent overly rapid validator set growth. EIP-7251 extended this cap to exits, to make room for the newly introduced consolidation operations, without increasing the weak subjectivity period. However, the fixed cap prevents the exit churn limit from being proportional to total stake, which, all else being equal, leads to longer queues as stake grows. Moreover, the
CHURN_LIMIT_QUOTIENT is set quite conservatively in the first place, requiring more than 3 months for 1/3 of the validator set to exit, a time which is now be more than doubled due to the cap. Recent episodes have stressed for more headroom to improve liquidity and staking user experience, as the exit queue has stretched beyond forty days as a consequence of the mass exit of Kiln validators. Long queues degrade user experience, and slow operator response to market or operational events. They also reduce the network’s ability to reconfigure stake more quickly after adverse events, for example to regain finality by having a large amount of stake exit (a double-edged sword, as preventing double finality is the reason these queues exist in the first place, as discussed in the security section). Finally, solo stakers are arguably the participants that suffer the most from a lack of liquidity today, as they do not have the ability to issue a liquid staking token or maintain liquidity reserves.
The conservative setting of the CHURN_LIMIT_QUOTIENT affects consolidations as well. Increasing the consolidation churn limit can shorten the path to a smaller validator set and, in turn, the path to a protocol with much faster finality. Under today’s parameters, even the best-case timelines are long: using only activations and exits, fully saturated around the clock, shrinking the roughly 32M ETH of 0x01 stake would take on the order of ~1.5 years; a fully saturated consolidation queue would still take ~1.3 years. Moreover, deposit capacity is also needed for new stake and is thus far from guaranteed to be available for consolidations, as evidenced by the recent inflow of > 1M ETH, fully occupying the queue for ~20 days.
The EIP proposes to address these issues by:
removing the cap on exits, restoring the proportionality of exit churn limit to total stake
increasing the churn limit on exits
introducing a separate consolidation churn limit, since there is no more “excess churn” from having an exit cap, and increase it compared to the current (implicitly defined) one. This also lets us more independently adjust it in the future, for example to increase it if we want to allow for faster consolidation of the validator set or decrease it when this process has already run its course.
allowing exits to use the consolidation queue whenever earliest_consolidation_epoch < earliest_exit_epoch, so that the consolidation churn is not wasted even when consolidations are not much in use. This lets us use the allocated churn more efficiently, greatly increasing the maximum exit throughput, while not harming the cause of shrinking the validator set size, since exits do that too. Moreover, it is already possible to route exits through the consolidation queue, due to a a loophole in the consolidation logic, which EIP-8071 proposes to fix. Democratizing this feature is useful and arguably even simpler than EIP-8071.
The EIP does not propose to lift the cap on the deposit churn, as part of the motivation for introducing it in EIP-7514 still holds today: though active validator set growth is mostly solved by EIP-7251, the validator array in the BeaconState (including inactive validators) is very large and still growing, and too rapid stake growth continues to be a concern as well.
Specification
The CHURN_LIMIT_QUOTIENT is halved, from 2**16 to 2**15, and fully dedicated to activations and exits. However, the cap on activations introduced in EIP-7514 is maintained via MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, while exits are freed from this cap. Consolidations now have their own dedicated churn, determined by CONSOLIDATION_CHURN_LIMIT_QUOTIENT. This is set here to 2**16, so that consolidations are allocated half as much churn as activations and exits combined, and a third of the total churn. Finally, compute_weak_subjectivity_period is adjusted to account for the asymmetry between exit and activation churn: a unit of exit churn has twice the weak subjectivity effect ($\frac{4}{3}$) of a unit of activation churn $\frac{2}{3}$, and a unit of consolidation churn has an effect equivalent to the sum of the other two ($2$).
Configuration
MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT replaces the existing MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT, also 256, as the cap now only applies to activations. We introduce the new CONSOLIDATION_CHURN_LIMIT_QUOTIENT and update the CHURN_LIMIT_QUOTIENT.
Name
Value
CHURN_LIMIT_QUOTIENT_GLOAS
uint64(2**15) (= 32,768)
CONSOLIDATION_CHURN_LIMIT_QUOTIENT
uint64(2**16) (= 65,536)
MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
uint64(2**8) (=256)
Churn computations
Due to the intended asymmetry in activation and exit churn, we replace get_activation_exit_churn_limit with get_activation_churn_limit, capped at MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, and get_exit_churn_limit, uncapped. get_consolidation_churn_limit uses the new CONSOLIDATION_CHURN_LIMIT_QUOTIENT, without either a minimum or a maximum value.
defget_activation_churn_limit(state:BeaconState)->Gwei:"""
Per-epoch churn limit for activations, rounded to EFFECTIVE_BALANCE_INCREMENT.
"""churn=max(MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA,get_total_active_balance(state)//CHURN_LIMIT_QUOTIENT_GLOAS)churn=churn-churn%EFFECTIVE_BALANCE_INCREMENTreturnmin(MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT,churn)defget_exit_churn_limit(state:BeaconState)->Gwei:"""
Per-epoch churn limit for activations, rounded to EFFECTIVE_BALANCE_INCREMENT.
"""churn=max(MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA,get_total_active_balance(state)//CHURN_LIMIT_QUOTIENT_GLOAS)returnchurn-churn%EFFECTIVE_BALANCE_INCREMENTdefget_consolidation_churn_limit(state:BeaconState)->Gwei:"""
Per-epoch churn limit for consolidations (EIP-7521), rounded to
EFFECTIVE_BALANCE_INCREMENT. Can also be used by exits.
"""churn=get_total_active_balance(state)//CONSOLIDATION_CHURN_LIMIT_QUOTIENTreturnchurn-churn%EFFECTIVE_BALANCE_INCREMENT
In compute_weak_subjectivity_period we only replace delta = get_balance_churn_limit(state) with an explicit calculation taking into account the different weak subjectivity effect of each type of operation.
defcompute_weak_subjectivity_period(state:BeaconState)->uint64:"""
Returns the weak subjectivity period for the current ``state``.
This computation takes into account the effect of validator set churn,
bounded by the three churn_limit functions used below.
"""t=get_total_active_balance(state)delta=(2*get_activation_exit_churn_limit(state)//3# effect of exit churn
+get_activation_churn_limit(state)//3# effect of activation churn
+get_consolidation_churn_limit(state)# effect of consolidation churn
)epochs_for_validator_set_churn=SAFETY_DECAY*t//(2*delta*100)returnMIN_VALIDATOR_WITHDRAWABILITY_DELAY+epochs_for_validator_set_churn
Deposit processing
The only modification is replacing get_activation_exit_churn_limit with get_activation_churn_limit, maintaing the current cap on deposit churn respects, while removing the cap from exits. This reverts to the pre-Electra status quo, when activations were capped but exits were not.
defprocess_pending_deposits(state:BeaconState)->None:next_epoch=Epoch(get_current_epoch(state)+1)# [Modified in Gloas:EIP-8061]
available_for_processing=state.deposit_balance_to_consume+get_activation_churn_limit(state)processed_amount=0next_deposit_index=0deposits_to_postpone=[]is_churn_limit_reached=Falsefinalized_slot=compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)fordepositinstate.pending_deposits:# Do not process deposit requests if Eth1 bridge deposits are not yet applied.
if(# Is deposit request
deposit.slot>GENESIS_SLOTand# There are pending Eth1 bridge deposits
state.eth1_deposit_index<state.deposit_requests_start_index):break# Check if deposit has been finalized, otherwise, stop processing.
ifdeposit.slot>finalized_slot:break# Check if number of processed deposits has not reached the limit, otherwise, stop processing.
ifnext_deposit_index>=MAX_PENDING_DEPOSITS_PER_EPOCH:break# Read validator state
is_validator_exited=Falseis_validator_withdrawn=Falsevalidator_pubkeys=[v.pubkeyforvinstate.validators]ifdeposit.pubkeyinvalidator_pubkeys:validator=state.validators[ValidatorIndex(validator_pubkeys.index(deposit.pubkey))]is_validator_exited=validator.exit_epoch<FAR_FUTURE_EPOCHis_validator_withdrawn=validator.withdrawable_epoch<next_epochifis_validator_withdrawn:# Deposited balance will never become active. Increase balance but do not consume churn
apply_pending_deposit(state,deposit)elifis_validator_exited:# Validator is exiting, postpone the deposit until after withdrawable epoch
deposits_to_postpone.append(deposit)else:# Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch.
is_churn_limit_reached=processed_amount+deposit.amount>available_for_processingifis_churn_limit_reached:break# Consume churn and apply deposit.
processed_amount+=deposit.amountapply_pending_deposit(state,deposit)# Regardless of how the deposit was handled, we move on in the queue.
next_deposit_index+=1state.pending_deposits=state.pending_deposits[next_deposit_index:]+deposits_to_postpone# Accumulate churn only if the churn limit has been hit.
ifis_churn_limit_reached:state.deposit_balance_to_consume=available_for_processing-processed_amountelse:state.deposit_balance_to_consume=Gwei(0)
Exit processing
We replace get_activation_exit_churn_limit with get_exit_churn_limit. Moreover, we route exits through the consolidation queue whenever state.earliest_exit_epoch > state.earliest_consolidation_epoch, by using compute_consolidation_epoch_and_update_churn. Note that the exit_balance passed to the latter is 2 * exit_balance // 3, because each unit of exit churn corresponds to 2/3 units of consolidation churn from a weak subjectivity perspective. This way, we keep the weak subjectivity impact of the consolidation queue the same regardless of whether it is used by consolidations or by exits.
defcompute_exit_epoch_and_update_churn(state:BeaconState,exit_balance:Gwei)->Epoch:ifstate.earliest_exit_epoch>state.earliest_consolidation_epoch:returncompute_consolidation_epoch_and_update_churn(state,2*exit_balance//3)earliest_exit_epoch=max(state.earliest_exit_epoch,compute_activation_exit_epoch(get_current_epoch(state)))per_epoch_churn=get_exit_churn_limit(state)# New epoch for exits.
ifstate.earliest_exit_epoch<earliest_exit_epoch:exit_balance_to_consume=per_epoch_churnelse:exit_balance_to_consume=state.exit_balance_to_consume# Exit doesn't fit in the current earliest epoch.
ifexit_balance>exit_balance_to_consume:balance_to_process=exit_balance-exit_balance_to_consumeadditional_epochs=(balance_to_process-1)//per_epoch_churn+1earliest_exit_epoch+=additional_epochsexit_balance_to_consume+=additional_epochs*per_epoch_churn# Consume the balance and update state variables.
state.exit_balance_to_consume=exit_balance_to_consume-exit_balancestate.earliest_exit_epoch=earliest_exit_epochreturnstate.earliest_exit_epoch
Rationale
Independent parameters: a separate churn consolidation is easy to independently tune, in particular adjusted upward if we want to speed up the consolidation process further, and later downward, when the primary wave of validator set consolidation has already happened and the consolidation operation becomes less crucial.
Scaling with stake: the exit churn regains proportionality to the total stake, and the consolidation churn maintains it.
Preserving the activation cap: the activation churn limit maintains the cap from EIP-7514
Efficient consolidation churn utilization: allowing exits to use the consolidation queue when it is shorter than the exit queue ensures that consolidation churn is not wasted when consolidations are not in high demand. With the current parameters, this increases the maximum exit throughput by 75% beyond the base increase, while not impeding the goal of shrinking the validator set size. The conversion factor of $2/3$ preserves the weak subjectivity impact regardless of whether the consolidation queue is used by consolidations or exits.
Balancing security and functionality: the exit churn limit is increased by ~4x compared to the current cap, and exactly 2x compared to previous uncapped version, while the consolidation churn limit is increased by ~2x. Moreover, the maximum exit throughput increases by a further 75% when we include routing through the consolidation queue, for a total increase of ~7x. Activations remain capped at the existing limit. As we see later in more detail, accounting for the asymmetric impact of exits and activations on safety degradation, this results in a decrease of the weak subjectivity period from ~15.7 days to ~7 days (see detailed calculations below), or ~6 days if we were to later remove the activation cap, in either case still on the order of a week. Moreover, were consolidations to be removed later, the weak subjectivity period would go up to ~11 days, or ~8.4 days without the activation cap.
Backwards Compatibility
This EIP introduces a backwards-incompatible change to the consensus rules and MUST be activated as part of a scheduled network upgrade.
Test Cases
TODO
Security Considerations
Weak subjectivity period
Let MIN_VALIDATOR_WITHDRAWABILITY_DELAY = 256 epochs, and let $S$ denote the total stake, $E$ the exit churn per epoch, $A$ the activation churn per epoch, and $C$ the consolidation churn per epoch. Each unit of exit churn contributes to a safety degradation (measured in ETH, as the churn) of $\frac{4}{3}$, whereas the safety degradation from activations is only $\frac{2}{3}$ per unit. Finally, it is $2$ per unit for consolidations, as a consolidation is the equivalent of an exit and an activation.
The formula to compute the weak subjectivity period (in epochs) for a 10% safety decay target (exactly corresponding to compute_weak_subjectivity_period) then is:
$\text{WS}_{\text{epochs}} = 256 +
\frac{0.1 \cdot S}{\frac{4}{3} \cdot E + \frac{2}{3} \cdot A + 2 \cdot C}$
The churn limits of the current protocol, assuming a total stake $S = 36M$ ETH (approximately the stake at the time of writing) are:
Consolidations disabled and activation cap removed: $\textbf{WS} \approx \mathbf{8.4\ \text{days}}$ ($\text{WS}_{\text{epochs}} \approx 1894$)
Note that removing the activation cap only reduces the weak subjectivity period by one day, and leaves it on the order of a week. The current parameters might then also be considered acceptable in a future where it is decided to remove the activation cap.