From 66700e168856079f8d79306efc90d0820587b054 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 17 Mar 2019 11:36:19 +0800 Subject: [PATCH] Fix #413 --- eth2/beacon/configs.py | 1 + .../forks/serenity/block_validation.py | 18 ++++- .../state_machines/forks/serenity/configs.py | 1 + .../forks/serenity/operation_processing.py | 2 +- tests/eth2/beacon/conftest.py | 7 ++ ...erenity_block_voluntary_exit_validation.py | 73 ++++++++++++++++++- 6 files changed, 96 insertions(+), 6 deletions(-) diff --git a/eth2/beacon/configs.py b/eth2/beacon/configs.py index 1000ce5bd7..55513f541a 100644 --- a/eth2/beacon/configs.py +++ b/eth2/beacon/configs.py @@ -52,6 +52,7 @@ ('ACTIVATION_EXIT_DELAY', int), ('EPOCHS_PER_ETH1_VOTING_PERIOD', int), ('MIN_VALIDATOR_WITHDRAWABILITY_DELAY', int), + ('PERSISTENT_COMMITTEE_PERIOD', int), # Reward and penalty quotients ('BASE_REWARD_QUOTIENT', int), ('WHISTLEBLOWER_REWARD_QUOTIENT', int), diff --git a/eth2/beacon/state_machines/forks/serenity/block_validation.py b/eth2/beacon/state_machines/forks/serenity/block_validation.py index 09041f8ea2..bc18bc5fe2 100644 --- a/eth2/beacon/state_machines/forks/serenity/block_validation.py +++ b/eth2/beacon/state_machines/forks/serenity/block_validation.py @@ -714,7 +714,7 @@ def validate_slashable_attestation(state: 'BeaconState', def validate_voluntary_exit(state: 'BeaconState', voluntary_exit: 'VoluntaryExit', slots_per_epoch: int, - activation_exit_delay: int) -> None: + persistent_committee_period: int) -> None: validator = state.validator_registry[voluntary_exit.validator_index] current_epoch = state.current_epoch(slots_per_epoch) @@ -724,6 +724,8 @@ def validate_voluntary_exit(state: 'BeaconState', validate_voluntary_exit_epoch(voluntary_exit, current_epoch) + validate_voluntary_exit_persistent(validator, current_epoch, persistent_committee_period) + validate_voluntary_exit_signature(state, voluntary_exit, validator) @@ -760,6 +762,20 @@ def validate_voluntary_exit_epoch(voluntary_exit: 'VoluntaryExit', ) +def validate_voluntary_exit_persistent(validator: 'ValidatorRecord', + current_epoch: Epoch, + persistent_committee_period: int) -> None: + """ + # Must have been in the validator set long enough + """ + if current_epoch - validator.activation_epoch < persistent_committee_period: + raise ValidationError( + "current_epoch - validator.activation_epoch " + f"({current_epoch} - {validator.activation_epoch}) should be greater than or equal to " + f"PERSISTENT_COMMITTEE_PERIOD ({persistent_committee_period})" + ) + + def validate_voluntary_exit_signature(state: 'BeaconState', voluntary_exit: 'VoluntaryExit', validator: 'ValidatorRecord') -> None: diff --git a/eth2/beacon/state_machines/forks/serenity/configs.py b/eth2/beacon/state_machines/forks/serenity/configs.py index 0a1fdeebcc..eb90787fa8 100644 --- a/eth2/beacon/state_machines/forks/serenity/configs.py +++ b/eth2/beacon/state_machines/forks/serenity/configs.py @@ -52,6 +52,7 @@ ACTIVATION_EXIT_DELAY=2**2, # (= 4) epochs EPOCHS_PER_ETH1_VOTING_PERIOD=2**4, # (= 16) epochs MIN_VALIDATOR_WITHDRAWABILITY_DELAY=2**8, # (= 256) epochs + PERSISTENT_COMMITTEE_PERIOD=2**11, # (= 2,048) epochs # Reward and penalty quotients BASE_REWARD_QUOTIENT=2**10, # (= 1,024) WHISTLEBLOWER_REWARD_QUOTIENT=2**9, # (= 512) diff --git a/eth2/beacon/state_machines/forks/serenity/operation_processing.py b/eth2/beacon/state_machines/forks/serenity/operation_processing.py index 6e4219653f..a88dcf3862 100644 --- a/eth2/beacon/state_machines/forks/serenity/operation_processing.py +++ b/eth2/beacon/state_machines/forks/serenity/operation_processing.py @@ -162,7 +162,7 @@ def process_voluntary_exits(state: BeaconState, state, voluntary_exit, config.SLOTS_PER_EPOCH, - config.ACTIVATION_EXIT_DELAY, + config.PERSISTENT_COMMITTEE_PERIOD, ) # Run the exit state = initiate_validator_exit(state, voluntary_exit.validator_index) diff --git a/tests/eth2/beacon/conftest.py b/tests/eth2/beacon/conftest.py index 9024f1c3eb..0a48eaf588 100644 --- a/tests/eth2/beacon/conftest.py +++ b/tests/eth2/beacon/conftest.py @@ -550,6 +550,11 @@ def min_validator_withdrawability_delay(): return SERENITY_CONFIG.MIN_VALIDATOR_WITHDRAWABILITY_DELAY +@pytest.fixture +def persistent_committee_period(): + return SERENITY_CONFIG.PERSISTENT_COMMITTEE_PERIOD + + @pytest.fixture def base_reward_quotient(): return SERENITY_CONFIG.BASE_REWARD_QUOTIENT @@ -718,6 +723,7 @@ def config( activation_exit_delay, epochs_per_eth1_voting_period, min_validator_withdrawability_delay, + persistent_committee_period, base_reward_quotient, whistleblower_reward_quotient, attestation_inclusion_reward_quotient, @@ -759,6 +765,7 @@ def config( ACTIVATION_EXIT_DELAY=activation_exit_delay, EPOCHS_PER_ETH1_VOTING_PERIOD=epochs_per_eth1_voting_period, MIN_VALIDATOR_WITHDRAWABILITY_DELAY=min_validator_withdrawability_delay, + PERSISTENT_COMMITTEE_PERIOD=persistent_committee_period, BASE_REWARD_QUOTIENT=base_reward_quotient, WHISTLEBLOWER_REWARD_QUOTIENT=whistleblower_reward_quotient, ATTESTATION_INCLUSION_REWARD_QUOTIENT=attestation_inclusion_reward_quotient, diff --git a/tests/eth2/beacon/state_machines/forks/test_serenity_block_voluntary_exit_validation.py b/tests/eth2/beacon/state_machines/forks/test_serenity_block_voluntary_exit_validation.py index e1754e8d77..c4f97a6fcb 100644 --- a/tests/eth2/beacon/state_machines/forks/test_serenity_block_voluntary_exit_validation.py +++ b/tests/eth2/beacon/state_machines/forks/test_serenity_block_voluntary_exit_validation.py @@ -15,6 +15,7 @@ validate_voluntary_exit, validate_voluntary_exit_epoch, validate_voluntary_exit_initiated_exit, + validate_voluntary_exit_persistent, validate_voluntary_exit_signature, validate_voluntary_exit_validator_exit_epoch, ) @@ -29,26 +30,41 @@ 'slots_per_epoch', 'target_committee_size', 'activation_exit_delay', + 'persistent_committee_period', ), [ - (40, 2, 2, 2), + (40, 2, 2, 2, 16), ] ) def test_validate_voluntary_exit( genesis_state, keymap, slots_per_epoch, - activation_exit_delay, + persistent_committee_period, config): - state = genesis_state + state = genesis_state.copy( + slot=get_epoch_start_slot( + config.GENESIS_EPOCH + persistent_committee_period, + slots_per_epoch + ), + ) validator_index = 0 + validator = state.validator_registry[validator_index].copy( + activation_epoch=config.GENESIS_EPOCH, + ) + state = state.update_validator_registry(validator_index, validator) valid_voluntary_exit = create_mock_voluntary_exit( state, config, keymap, validator_index, ) - validate_voluntary_exit(state, valid_voluntary_exit, slots_per_epoch, activation_exit_delay) + validate_voluntary_exit( + state, + valid_voluntary_exit, + slots_per_epoch, + persistent_committee_period, + ) @pytest.mark.parametrize( @@ -172,6 +188,55 @@ def test_validate_voluntary_exit_epoch( validate_voluntary_exit_epoch(voluntary_exit, state.current_epoch(slots_per_epoch)) +@pytest.mark.parametrize( + ( + 'current_epoch', + 'persistent_committee_period', + 'activation_epoch', + 'success', + ), + [ + (16, 4, 16 - 4, True), + (16, 4, 16 - 4 + 1, False), + ] +) +def test_validate_voluntary_exit_persistent( + genesis_state, + keymap, + current_epoch, + activation_epoch, + slots_per_epoch, + persistent_committee_period, + success): + state = genesis_state.copy( + slot=get_epoch_start_slot( + current_epoch, + slots_per_epoch + ), + ) + validator_index = 0 + validator = state.validator_registry[validator_index].copy( + activation_epoch=activation_epoch, + ) + state = state.update_validator_registry(validator_index, validator) + + validator_index = 0 + + if success: + validate_voluntary_exit_persistent( + validator, + state.current_epoch(slots_per_epoch), + persistent_committee_period, + ) + else: + with pytest.raises(ValidationError): + validate_voluntary_exit_persistent( + validator, + state.current_epoch(slots_per_epoch), + persistent_committee_period, + ) + + @pytest.mark.parametrize( ( 'num_validators',