def process_registry_updates(state: BeaconState, config: Eth2Config) -> BeaconState: new_validators = tuple( _process_activation_eligibility_or_ejections(state, validator, config) for validator in state.validators ) activation_exit_epoch = compute_activation_exit_epoch( state.finalized_checkpoint.epoch, config.ACTIVATION_EXIT_DELAY ) activation_queue = sorted( ( index for index, validator in enumerate(new_validators) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_epoch >= activation_exit_epoch ), key=lambda index: new_validators[index].activation_eligibility_epoch, ) for index in activation_queue[: get_validator_churn_limit(state, config)]: new_validators = update_tuple_item_with_fn( new_validators, index, _update_validator_activation_epoch(state, config) ) return state.copy(validators=new_validators)
def _update_validator_activation_epoch(state: BeaconState, config: Eth2Config, validator: Validator) -> Validator: return validator.set( "activation_epoch", compute_activation_exit_epoch( state.current_epoch(config.SLOTS_PER_EPOCH), config.MAX_SEED_LOOKAHEAD), )
def _update_validator_activation_epoch(state: BeaconState, config: Eth2Config, validator: Validator) -> Validator: if validator.activation_epoch == FAR_FUTURE_EPOCH: return validator.copy(activation_epoch=compute_activation_exit_epoch( state.current_epoch(config.SLOTS_PER_EPOCH), config.ACTIVATION_EXIT_DELAY, )) else: return validator
def _update_validator_activation_epoch( state: BeaconState, config: Eth2Config, validator: Validator ) -> Validator: if validator.activation_epoch == FAR_FUTURE_EPOCH: return validator.copy( activation_epoch=compute_activation_exit_epoch( state.current_epoch(config.SLOTS_PER_EPOCH), config.MAX_SEED_LOOKAHEAD ) ) else: return validator
def _compute_exit_queue_epoch(state: BeaconState, churn_limit: int, config: Eth2Config) -> Epoch: slots_per_epoch = config.SLOTS_PER_EPOCH exit_epochs = tuple(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( state.current_epoch(slots_per_epoch), config.MAX_SEED_LOOKAHEAD), )) exit_queue_churn = len( tuple(v for v in state.validators if v.exit_epoch == exit_queue_epoch)) if exit_queue_churn >= churn_limit: exit_queue_epoch += 1 return Epoch(exit_queue_epoch)
def process_registry_updates(state: BeaconState, config: Eth2Config) -> BeaconState: new_validators = state.validators for index, validator in enumerate(state.validators): new_validator = _process_activation_eligibility_or_ejections( state, validator, config) new_validators = new_validators.set(index, new_validator) activation_exit_epoch = compute_activation_exit_epoch( state.finalized_checkpoint.epoch, config.MAX_SEED_LOOKAHEAD) activation_queue = sorted( (index for index, validator in enumerate(new_validators) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_epoch >= activation_exit_epoch), key=lambda index: new_validators[index].activation_eligibility_epoch, ) for index in activation_queue[:get_validator_churn_limit(state, config)]: new_validators = new_validators.transform( (index, ), _update_validator_activation_epoch(state, config)) return state.set("validators", new_validators)
def test_compute_exit_queue_epoch( genesis_state, is_delayed_exit_epoch_the_maximum_exit_queue_epoch, is_churn_limit_met, config, ): state = genesis_state for index in random.sample(range(len(state.validators)), len(state.validators) // 4): some_future_epoch = config.GENESIS_EPOCH + random.randrange(1, 2**32) state = state.update_validator_with_fn( index, lambda validator, *_: validator.copy(exit_epoch=some_future_epoch)) if is_delayed_exit_epoch_the_maximum_exit_queue_epoch: expected_candidate_exit_queue_epoch = compute_activation_exit_epoch( state.current_epoch(config.SLOTS_PER_EPOCH), config.MAX_SEED_LOOKAHEAD) for index, validator in enumerate(state.validators): if validator.exit_epoch == FAR_FUTURE_EPOCH: continue some_prior_epoch = random.randrange( config.GENESIS_EPOCH, expected_candidate_exit_queue_epoch) state = state.update_validator_with_fn( index, lambda validator, *_: validator.copy(exit_epoch= some_prior_epoch)) validator = state.validators[index] assert expected_candidate_exit_queue_epoch >= validator.exit_epoch else: expected_candidate_exit_queue_epoch = -1 for validator in state.validators: if validator.exit_epoch == FAR_FUTURE_EPOCH: continue if validator.exit_epoch > expected_candidate_exit_queue_epoch: expected_candidate_exit_queue_epoch = validator.exit_epoch assert expected_candidate_exit_queue_epoch >= config.GENESIS_EPOCH if is_churn_limit_met: churn_limit = 0 expected_exit_queue_epoch = expected_candidate_exit_queue_epoch + 1 else: # add more validators to the queued epoch to make the test less trivial # with the setup so far, it is likely that the queue in the target epoch is size 1. queued_validators = { index: validator for index, validator in state.validators if validator.exit_epoch == expected_candidate_exit_queue_epoch } additional_queued_validator_count = random.randrange( len(queued_validators), len(state.validators)) unqueued_validators = tuple(v for v in state.validators if v.exit_epoch == FAR_FUTURE_EPOCH) for index in random.sample(range(len(unqueued_validators)), additional_queued_validator_count): state = state.update_validator_with_fn( index, lambda validator, *_: validator.copy( exit_epoch=expected_candidate_exit_queue_epoch), ) all_queued_validators = tuple( v for v in state.validators if v.exit_epoch == expected_candidate_exit_queue_epoch) churn_limit = len(all_queued_validators) + 1 expected_exit_queue_epoch = expected_candidate_exit_queue_epoch assert (_compute_exit_queue_epoch(state, churn_limit, config) == expected_exit_queue_epoch)
def test_compute_activation_exit_epoch(max_seed_lookahead): epoch = random.randrange(0, FAR_FUTURE_EPOCH) entry_exit_effect_epoch = compute_activation_exit_epoch( epoch, max_seed_lookahead) assert entry_exit_effect_epoch == (epoch + 1 + max_seed_lookahead)
def test_compute_activation_exit_epoch(activation_exit_delay): epoch = random.randrange(0, FAR_FUTURE_EPOCH) entry_exit_effect_epoch = compute_activation_exit_epoch( epoch, activation_exit_delay) assert entry_exit_effect_epoch == (epoch + 1 + activation_exit_delay)