From 55437c2642d6c7ddf409733853642629d574fd28 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 16:54:47 +0800 Subject: [PATCH 1/2] Add `process_ejections` --- .../forks/serenity/epoch_processing.py | 27 ++++++++++++ .../forks/serenity/state_transitions.py | 3 +- .../forks/test_serenity_epoch_processing.py | 43 ++++++++++++++++--- 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/eth2/beacon/state_machines/forks/serenity/epoch_processing.py b/eth2/beacon/state_machines/forks/serenity/epoch_processing.py index 17ab1fe03b..ccdc02362b 100644 --- a/eth2/beacon/state_machines/forks/serenity/epoch_processing.py +++ b/eth2/beacon/state_machines/forks/serenity/epoch_processing.py @@ -56,6 +56,9 @@ get_randao_mix, slot_to_epoch, ) +from eth2.beacon.validator_status_helpers import ( + exit_validator, +) from eth2.beacon._utils.hash import ( hash_eth2, ) @@ -784,6 +787,30 @@ def process_rewards_and_penalties(state: BeaconState, config: BeaconConfig) -> B return state +# +# Ejections +# +def process_ejections(state: BeaconState, + config: BeaconConfig) -> BeaconState: + """ + Iterate through the validator registry and eject active validators + with balance below ``EJECTION_BALANCE``. + """ + active_validator_indices = get_active_validator_indices( + state.validator_registry, + state.current_epoch(config.SLOTS_PER_EPOCH), + ) + for index in set(active_validator_indices): + if state.validator_balances[index] < config.EJECTION_BALANCE: + state = exit_validator( + state, + index, + slots_per_epoch=config.SLOTS_PER_EPOCH, + activation_exit_delay=config.ACTIVATION_EXIT_DELAY, + ) + return state + + # # Validator registry and shuffling seed data # diff --git a/eth2/beacon/state_machines/forks/serenity/state_transitions.py b/eth2/beacon/state_machines/forks/serenity/state_transitions.py index 26f50b6bfb..982e7a38c1 100644 --- a/eth2/beacon/state_machines/forks/serenity/state_transitions.py +++ b/eth2/beacon/state_machines/forks/serenity/state_transitions.py @@ -20,6 +20,7 @@ from .epoch_processing import ( process_justification, process_crosslinks, + process_ejections, process_final_updates, process_rewards_and_penalties, process_validator_registry, @@ -90,7 +91,7 @@ def per_epoch_transition(self, state: BeaconState, block: BaseBeaconBlock) -> Be state = process_justification(state, self.config) state = process_crosslinks(state, self.config) state = process_rewards_and_penalties(state, self.config) - # TODO: state = process_ejections(state, self.config) + state = process_ejections(state, self.config) state = process_validator_registry(state, self.config) state = process_final_updates(state, self.config) diff --git a/tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py b/tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py index e22d61fb1d..5ef545ee39 100644 --- a/tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py +++ b/tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py @@ -33,6 +33,7 @@ get_active_validator_indices, get_block_root, get_epoch_start_slot, + get_delayed_activation_exit_epoch, get_randao_mix, slot_to_epoch, ) @@ -50,16 +51,17 @@ from eth2.beacon.types.pending_attestation_records import PendingAttestationRecord from eth2.beacon.state_machines.forks.serenity.epoch_processing import ( _check_if_update_validator_registry, - _update_latest_active_index_roots, - process_crosslinks, - process_final_updates, + _current_previous_epochs_justifiable, + _get_finalized_epoch, _process_rewards_and_penalties_for_attestation_inclusion, _process_rewards_and_penalties_for_crosslinks, _process_rewards_and_penalties_for_finality, - process_validator_registry, - _current_previous_epochs_justifiable, - _get_finalized_epoch, + _update_latest_active_index_roots, + process_crosslinks, + process_ejections, + process_final_updates, process_justification, + process_validator_registry, ) from eth2.beacon.types.states import BeaconState @@ -838,6 +840,35 @@ def test_process_rewards_and_penalties_for_crosslinks( assert rewards_received[index] == expected_rewards_received[index] +# +# Ejections +# +def test_process_ejections(genesis_state, config, activation_exit_delay): + current_epoch = 8 + state = genesis_state.copy( + slot=get_epoch_start_slot(current_epoch, config.SLOTS_PER_EPOCH), + ) + delayed_activation_exit_epoch = get_delayed_activation_exit_epoch( + current_epoch, + activation_exit_delay, + ) + + ejecting_validator_index = 0 + validator = state.validator_registry[ejecting_validator_index] + assert validator.is_active(current_epoch) + assert validator.exit_epoch > delayed_activation_exit_epoch + + state = state.update_validator_balance( + validator_index=ejecting_validator_index, + balance=config.EJECTION_BALANCE - 1, + ) + result_state = process_ejections(state, config) + result_validator = result_state.validator_registry[ejecting_validator_index] + assert result_validator.is_active(current_epoch) + assert result_validator.exit_epoch == delayed_activation_exit_epoch + assert not result_validator.is_active(result_validator.exit_epoch) + + # # Validator registry and shuffling seed data # From c9b587603383cc5fcbd533dd94a5f26dd42ae416 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Mar 2019 17:09:27 +0800 Subject: [PATCH 2/2] Add comment --- .../forks/test_serenity_epoch_processing.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py b/tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py index 5ef545ee39..e4d8eecb2f 100644 --- a/tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py +++ b/tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py @@ -29,6 +29,9 @@ from eth2.beacon.configs import ( CommitteeConfig, ) +from eth2.beacon.constants import ( + FAR_FUTURE_EPOCH, +) from eth2.beacon.helpers import ( get_active_validator_indices, get_block_root, @@ -866,7 +869,13 @@ def test_process_ejections(genesis_state, config, activation_exit_delay): result_validator = result_state.validator_registry[ejecting_validator_index] assert result_validator.is_active(current_epoch) assert result_validator.exit_epoch == delayed_activation_exit_epoch + # The ejecting validator will be inactive at the exit_epoch assert not result_validator.is_active(result_validator.exit_epoch) + # Other validators are not ejected + assert ( + result_state.validator_registry[ejecting_validator_index + 1].exit_epoch == + FAR_FUTURE_EPOCH + ) #