From 4a91c939624c1f09026908d2af4f147dd8fca909 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 12 May 2021 14:58:32 -0600 Subject: [PATCH] add epoch processing tests for inactivity udpates --- specs/altair/beacon-chain.md | 4 + .../test_process_inactivity_updates.py | 113 ++++++++++++++++++ .../altair/rewards/test_inactivity_scores.py | 5 +- .../eth2spec/test/helpers/epoch_processing.py | 3 +- .../test/helpers/inactivity_scores.py | 5 + tests/formats/epoch_processing/README.md | 3 +- 6 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py create mode 100644 tests/core/pyspec/eth2spec/test/helpers/inactivity_scores.py diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index a0aa458fc..3a5cec869 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -606,6 +606,10 @@ def process_justification_and_finalization(state: BeaconState) -> None: ```python def process_inactivity_updates(state: BeaconState) -> None: + # Score updates Based on previous epoch participation, skip genesis epoch + if get_current_epoch(state) == GENESIS_EPOCH: + return + for index in get_eligible_validator_indices(state): # Increase inactivity score of inactive validators if index in get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)): 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 new file mode 100644 index 000000000..0efdf3e10 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py @@ -0,0 +1,113 @@ +from random import Random + +from eth2spec.test.context import spec_state_test, with_altair_and_later +from eth2spec.test.helpers.inactivity_scores import randomize_inactivity_scores +from eth2spec.test.helpers.state import ( + next_epoch_via_block, +) +from eth2spec.test.helpers.epoch_processing import ( + run_epoch_processing_with +) + + +def set_full_participation(spec, state): + full_flags = spec.ParticipationFlags(0) + for flag_index in range(len(spec.PARTICIPATION_FLAG_WEIGHTS)): + full_flags = spec.add_flag(full_flags, flag_index) + + for index in range(len(state.validators)): + state.previous_epoch_participation[index] = full_flags + + +def randomize_flags(spec, state, rng=Random(2080)): + for index in range(len(state.validators)): + # ~1/3 have bad head or bad target or not timely enough + is_timely_correct_head = rng.randint(0, 2) != 0 + flags = state.previous_epoch_participation[index] + + def set_flag(index, value): + nonlocal flags + flag = spec.ParticipationFlags(2**index) + if value: + flags |= flag + else: + flags &= 0xff ^ flag + + set_flag(spec.TIMELY_HEAD_FLAG_INDEX, is_timely_correct_head) + if is_timely_correct_head: + # If timely head, then must be timely target + set_flag(spec.TIMELY_TARGET_FLAG_INDEX, True) + # If timely head, then must be timely source + set_flag(spec.TIMELY_SOURCE_FLAG_INDEX, True) + else: + # ~50% of remaining have bad target or not timely enough + set_flag(spec.TIMELY_TARGET_FLAG_INDEX, rng.choice([True, False])) + # ~50% of remaining have bad source or not timely enough + set_flag(spec.TIMELY_SOURCE_FLAG_INDEX, rng.choice([True, False])) + state.previous_epoch_participation[index] = flags + + +def run_process_inactivity_updates(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_inactivity_updates') + + +@with_altair_and_later +@spec_state_test +def test_genesis(spec, state): + yield from run_process_inactivity_updates(spec, state) + + +# +# Genesis epoch processing is skipped +# Thus all of following tests all go past genesis epoch to test core functionality +# + +@with_altair_and_later +@spec_state_test +def test_all_zero_inactivity_scores_empty_participation(spec, state): + state.inactivity_scores = [0] * len(state.validators) + yield from run_process_inactivity_updates(spec, state) + + +@with_altair_and_later +@spec_state_test +def test_all_zero_inactivity_scores_random_participation(spec, state): + next_epoch_via_block(spec, state) + state.inactivity_scores = [0] * len(state.validators) + randomize_flags(spec, state) + yield from run_process_inactivity_updates(spec, state) + + +@with_altair_and_later +@spec_state_test +def test_all_zero_inactivity_scores_full_participation(spec, state): + next_epoch_via_block(spec, state) + state.inactivity_scores = [0] * len(state.validators) + set_full_participation(spec, state) + yield from run_process_inactivity_updates(spec, state) + + +@with_altair_and_later +@spec_state_test +def test_random_inactivity_scores_empty_participation(spec, state): + next_epoch_via_block(spec, state) + randomize_inactivity_scores(spec, state, rng=Random(9999)) + yield from run_process_inactivity_updates(spec, state) + + +@with_altair_and_later +@spec_state_test +def test_random_inactivity_scores_random_participation(spec, state): + next_epoch_via_block(spec, state) + randomize_inactivity_scores(spec, state, rng=Random(22222)) + randomize_flags(spec, state) + yield from run_process_inactivity_updates(spec, state) + + +@with_altair_and_later +@spec_state_test +def test_random_inactivity_scores_full_participation(spec, state): + next_epoch_via_block(spec, state) + randomize_inactivity_scores(spec, state, rng=Random(33333)) + set_full_participation(spec, state) + yield from run_process_inactivity_updates(spec, state) diff --git a/tests/core/pyspec/eth2spec/test/altair/rewards/test_inactivity_scores.py b/tests/core/pyspec/eth2spec/test/altair/rewards/test_inactivity_scores.py index a523b5157..a35095278 100644 --- a/tests/core/pyspec/eth2spec/test/altair/rewards/test_inactivity_scores.py +++ b/tests/core/pyspec/eth2spec/test/altair/rewards/test_inactivity_scores.py @@ -8,14 +8,11 @@ from eth2spec.test.context import ( single_phase, low_balances, misc_balances, ) +from eth2spec.test.helpers.inactivity_scores import randomize_inactivity_scores from eth2spec.test.helpers.rewards import leaking import eth2spec.test.helpers.rewards as rewards_helpers -def randomize_inactivity_scores(spec, state, minimum=0, maximum=50000, rng=Random(4242)): - state.inactivity_scores = [rng.randint(minimum, maximum) for _ in range(len(state.validators))] - - @with_altair_and_later @spec_state_test def test_random_inactivity_scores_0(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py index 3c47c4895..c783692fc 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py +++ b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py @@ -9,6 +9,7 @@ def get_process_calls(spec): # or the old function will stick around. return [ 'process_justification_and_finalization', + 'process_inactivity_updates', # altair 'process_rewards_and_penalties', 'process_registry_updates', 'process_reveal_deadlines', # custody game @@ -26,7 +27,7 @@ def get_process_calls(spec): 'process_participation_flag_updates' if is_post_altair(spec) else ( 'process_participation_record_updates' ), - 'process_sync_committee_updates', + 'process_sync_committee_updates', # altair 'process_shard_epoch_increment' # sharding ] diff --git a/tests/core/pyspec/eth2spec/test/helpers/inactivity_scores.py b/tests/core/pyspec/eth2spec/test/helpers/inactivity_scores.py new file mode 100644 index 000000000..5c28bfc24 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/helpers/inactivity_scores.py @@ -0,0 +1,5 @@ +from random import Random + + +def randomize_inactivity_scores(spec, state, minimum=0, maximum=50000, rng=Random(4242)): + state.inactivity_scores = [rng.randint(minimum, maximum) for _ in range(len(state.validators))] diff --git a/tests/formats/epoch_processing/README.md b/tests/formats/epoch_processing/README.md index 3ac2a28c4..c917e3f75 100644 --- a/tests/formats/epoch_processing/README.md +++ b/tests/formats/epoch_processing/README.md @@ -32,9 +32,8 @@ The provided pre-state is already transitioned to just before the specific sub-t Sub-transitions: -Sub-transitions: - - `justification_and_finalization` +- `inactivity_penalty_updates` - `rewards_and_penalties` - `registry_updates` - `slashings`