diff --git a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py index 9bc0f4841..2a971f4f0 100644 --- a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py +++ b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py @@ -18,6 +18,7 @@ from eth2spec.test.helpers.epoch_processing import ( from eth2spec.test.helpers.random import ( randomize_attestation_participation, randomize_previous_epoch_participation, + randomize_state, ) from eth2spec.test.helpers.rewards import leaking @@ -321,3 +322,58 @@ def test_some_exited_full_random_leaking(spec, state): # Check still in leak assert spec.is_in_inactivity_leak(state) + + +def _run_randomized_state_test_for_inactivity_updates(spec, state, rng=Random(13377331)): + randomize_inactivity_scores(spec, state, rng=rng) + randomize_state(spec, state, rng=rng) + + exited_validators = get_exited_validators(spec, state) + exited_but_not_slashed = [] + for index in exited_validators: + validator = state.validators[index] + if validator.slashed: + continue + exited_but_not_slashed.append(index) + + assert len(exited_but_not_slashed) > 0 + + some_exited_validator = exited_but_not_slashed[0] + + pre_score_for_exited_validator = state.inactivity_scores[some_exited_validator] + + assert pre_score_for_exited_validator != 0 + + assert len(set(state.inactivity_scores)) > 1 + + yield from run_inactivity_scores_test(spec, state) + + post_score_for_exited_validator = state.inactivity_scores[some_exited_validator] + assert pre_score_for_exited_validator == post_score_for_exited_validator + + +@with_altair_and_later +@spec_state_test +def test_randomized_state(spec, state): + """ + This test ensures that once a validator has exited, + their inactivity score does not change. + """ + rng = Random(10011001) + _run_randomized_state_test_for_inactivity_updates(spec, state, rng=rng) + + +@with_altair_and_later +@spec_state_test +@leaking() +def test_randomized_state_leaking(spec, state): + """ + This test ensures that once a validator has exited, + their inactivity score does not change, even during a leak. + Note that slashed validators are still subject to mutations + (refer ``get_eligible_validator_indices`). + """ + rng = Random(10011002) + _run_randomized_state_test_for_inactivity_updates(spec, state, rng=rng) + # Check still in leak + assert spec.is_in_inactivity_leak(state) diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index 48057a1d9..4e2621522 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -1,5 +1,6 @@ from random import Random +from eth2spec.test.context import is_post_altair from eth2spec.test.helpers.keys import pubkeys, privkeys from eth2spec.utils import bls from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_proof @@ -15,6 +16,8 @@ def mock_deposit(spec, state, index): state.validators[index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH state.validators[index].activation_epoch = spec.FAR_FUTURE_EPOCH state.validators[index].effective_balance = spec.MAX_EFFECTIVE_BALANCE + if is_post_altair(spec): + state.inactivity_scores[index] = 0 assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))