# Increase `CHURN_LIMIT_QUOTIENT` ## Rationale Current `CHURN_LIMIT_QUOTIENT` in Gnosis is low such that the network's validator set can change significantly in short periods of time posing unnecessary risks to the network. The original value was chosen without a great deal of care to facilitate staker onboarding UX. Even that argument will no longer apply with [`eip6110`](https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip6110/beacon-chain.md) (in-protocol deposits). `CHURN_LIMIT_QUOTIENT` dictates the `validator_churn_limit` which is constant through an epoch variable only to active validator count. ```python def get_validator_churn_limit(state: BeaconState) -> uint64: """ Return the validator churn limit for the current epoch. """ active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT) ``` ([specs/phase0/beacon-chain.md](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#get_validator_churn_limit)) ## Inflow Higher `CHURN_LIMIT_QUOTIENT` results in less validators dequeued from that epoch's `activation_epoch`. ```python def process_registry_updates(state: BeaconState) -> None: ... # Dequeued validators for activation up to churn limit for index in activation_queue[:get_validator_churn_limit(state)]: validator = state.validators[index] validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) ``` ## Outflow The result of computing the validator churn limit is persisted in the state for outflows. Below `initiate_validator_exit` - Compute the highest registered `exit_epoch`, and count how many validators will exit at the epoch - If that count exceeds churn limit, schedule to exit validator at max epoch + 1. By definition that will not have any validators scheduled to exit on it ```python def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: ... # Compute exit queue epoch exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH] exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_validator_churn_limit(state): exit_queue_epoch += Epoch(1) # Set validator exit epoch and withdrawable epoch validator.exit_epoch = exit_queue_epoch validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY) ``` Irrespective of the current status of the exit churn, the chrun decrease is well handled with the spec as is. ![](https://i.imgur.com/ngbiw2n.png) Implementations must make sure to invalidate any exit churn cache and recompute that fork boundary. ## New `CHURN_LIMIT_QUOTIENT` TBD pending research. Current tentative value Ethereum's `65536` ## Max churn simulation Consensus layer config differences to Ethereum | Config value | Ethereum | Gnosis | | -------- | -------- | -------- | | `CHURN_LIMIT_QUOTIENT` | 65536 | 4096 | - `CHURN_LIMIT_QUOTIENT`: The churn to enter and exit the validator set is greater. For 100_000 validators, _100000 / 4096 = 24_ validators can activate and exit per epoch. Churn out | Churn in :-------------------------:|:-------------------------: ![](https://i.imgur.com/WY5AkuY.png) | ![](https://i.imgur.com/MJmnIFR.png) ![](https://i.imgur.com/pOzFM09.png) | ![](https://i.imgur.com/6EFUzTk.png)