def test_initiate_validator_exit(genesis_state, is_already_exited, config): state = genesis_state index = random.choice(range(len(state.validators))) validator = state.validators[index] assert not validator.slashed assert validator.activation_epoch == config.GENESIS_EPOCH assert validator.activation_eligibility_epoch == config.GENESIS_EPOCH assert validator.exit_epoch == FAR_FUTURE_EPOCH assert validator.withdrawable_epoch == FAR_FUTURE_EPOCH if is_already_exited: churn_limit = get_validator_churn_limit(state, config) exit_queue_epoch = _compute_exit_queue_epoch(state, churn_limit, config) validator = validator.copy( exit_epoch=exit_queue_epoch, withdrawable_epoch=exit_queue_epoch + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY, ) exited_validator = initiate_exit_for_validator(validator, state, config) if is_already_exited: assert exited_validator == validator else: churn_limit = get_validator_churn_limit(state, config) exit_queue_epoch = _compute_exit_queue_epoch(state, churn_limit, config) assert exited_validator.exit_epoch == exit_queue_epoch assert exited_validator.withdrawable_epoch == ( exit_queue_epoch + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
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)