From d1bedbfbec74828eccdaee9ac9d9d91ddb0b1386 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 7 May 2021 11:05:27 -0700 Subject: [PATCH 1/8] Update inactivity penalty deltas processing Two main changes: 1. Inactivity scores are modified to decrease slowly for inactive validators when we are not in a leak, and quickly for active validators 2. The inactivity penalties are applied even not during a leak (note that inactivity _scores_ decrease when outside of a leak) This has the effect that the inactivity leak "overshoots" the target of finalizing again, and keeps leaking balances a bit more. For inactive validators, this PR sets post-leak recovery to happen 3x faster than the during-leak increase, so if a validator loses 3% during a leak, if they stay offline they should expect to lose another 1% until their score decreases back to zero. --- specs/altair/beacon-chain.md | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index b2ee93557..681d4aad0 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -120,6 +120,7 @@ This patch updates a few configuration values to move penalty parameters closer | - | - | | `SYNC_COMMITTEE_SIZE` | `uint64(2**9)` (= 512) | | `INACTIVITY_SCORE_BIAS` | `uint64(4)` | +| `INACTIVITY_SCORE_RECOVERY_RATE` | `uint64(16)` | ### Time parameters @@ -397,17 +398,16 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S """ rewards = [Gwei(0) for _ in range(len(state.validators))] penalties = [Gwei(0) for _ in range(len(state.validators))] - if is_in_inactivity_leak(state): - previous_epoch = get_previous_epoch(state) - matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) - for index in get_eligible_validator_indices(state): - for (_, weight) in get_flag_indices_and_weights(): - # This inactivity penalty cancels the flag reward corresponding to the flag index - penalties[index] += Gwei(get_base_reward(state, index) * weight // WEIGHT_DENOMINATOR) - if index not in matching_target_indices: - penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] - penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR - penalties[index] += Gwei(penalty_numerator // penalty_denominator) + previous_epoch = get_previous_epoch(state) + matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) + for index in get_eligible_validator_indices(state): + for (_, weight) in get_flag_indices_and_weights(): + # This inactivity penalty cancels the flag reward corresponding to the flag index + penalties[index] += Gwei(get_base_reward(state, index) * weight // WEIGHT_DENOMINATOR) + if index not in matching_target_indices: + penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] + penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR + penalties[index] += Gwei(penalty_numerator // penalty_denominator) return rewards, penalties ``` @@ -621,11 +621,14 @@ def process_justification_and_finalization(state: BeaconState) -> None: ```python def process_inactivity_updates(state: BeaconState) -> None: 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)): - if state.inactivity_scores[index] > 0: - state.inactivity_scores[index] -= 1 - elif is_in_inactivity_leak(state): + state.inactivity_scores[index] -= min(1, state.inactivity_scores[index]) + else: state.inactivity_scores[index] += INACTIVITY_SCORE_BIAS + # Decrease the score of all validators for forgiveness when not during a leak + if not if is_in_inactivity_leak(state): + state.inactivity_scores[index] -= min(INACTIVITY_SCORE_RECOVERY_RATE, state.inactivity_scores[index]) ``` #### Rewards and penalties From e31a2af87b33c7b2063386d527cbde5208894948 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 11 May 2021 08:15:04 -0600 Subject: [PATCH 2/8] remove extra if --- specs/altair/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 210b7e4b7..4c6f47590 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -634,7 +634,7 @@ def process_inactivity_updates(state: BeaconState) -> None: else: state.inactivity_scores[index] += INACTIVITY_SCORE_BIAS # Decrease the score of all validators for forgiveness when not during a leak - if not if is_in_inactivity_leak(state): + if not is_in_inactivity_leak(state): state.inactivity_scores[index] -= min(INACTIVITY_SCORE_RECOVERY_RATE, state.inactivity_scores[index]) ``` From 8bb0531f584756f248e52ab44eb23ec8c6ac9f5c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 11 May 2021 16:23:31 -0600 Subject: [PATCH 3/8] only give counter-weight penalty if leak. TMP --- specs/altair/beacon-chain.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 4c6f47590..56b6b0b79 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -407,9 +407,11 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S previous_epoch = get_previous_epoch(state) matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) for index in get_eligible_validator_indices(state): - for (_, weight) in get_flag_indices_and_weights(): - # This inactivity penalty cancels the flag reward corresponding to the flag index - penalties[index] += Gwei(get_base_reward(state, index) * weight // WEIGHT_DENOMINATOR) + if is_in_inactivity_leak(state): + # TODO: to be removed in PR 2399 + for (_, weight) in get_flag_indices_and_weights(): + # This inactivity penalty cancels the flag reward corresponding to the flag index + penalties[index] += Gwei(get_base_reward(state, index) * weight // WEIGHT_DENOMINATOR) if index not in matching_target_indices: penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR From fa1bdabaced8ccbce63f1e03f2cbba7e6a1d0860 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 12 May 2021 14:23:11 -0600 Subject: [PATCH 4/8] add random inactivity scores tests --- .../altair/rewards/test_inactivity_scores.py | 121 ++++++++++++++++++ .../test/phase0/rewards/test_random.py | 10 +- 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 tests/core/pyspec/eth2spec/test/altair/rewards/test_inactivity_scores.py 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 new file mode 100644 index 000000000..a523b5157 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/altair/rewards/test_inactivity_scores.py @@ -0,0 +1,121 @@ +from random import Random + +from eth2spec.test.context import ( + with_altair_and_later, + spec_test, + spec_state_test, + with_custom_state, + single_phase, + low_balances, misc_balances, +) +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): + randomize_inactivity_scores(spec, state, rng=Random(9999)) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9999)) + + +@with_altair_and_later +@spec_state_test +def test_random_inactivity_scores_1(spec, state): + randomize_inactivity_scores(spec, state, rng=Random(10000)) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(10000)) + + +@with_altair_and_later +@spec_state_test +def test_half_zero_half_random_inactivity_scores(spec, state): + randomize_inactivity_scores(spec, state, rng=Random(10101)) + half_val_point = len(state.validators) // 2 + state.inactivity_scores = [0] * half_val_point + state.inactivity_scores[half_val_point:] + + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(10101)) + + +@with_altair_and_later +@spec_state_test +def test_random_high_inactivity_scores(spec, state): + randomize_inactivity_scores(spec, state, minimum=500000, maximum=5000000, rng=Random(9998)) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9998)) + + +@with_altair_and_later +@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE) +@spec_test +@single_phase +def test_random_inactivity_scores_low_balances_0(spec, state): + randomize_inactivity_scores(spec, state, rng=Random(11111)) + yield from rewards_helpers.run_test_full_random(spec, state) + + +@with_altair_and_later +@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: 0) +@spec_test +@single_phase +def test_random_inactivity_scores_low_balances_1(spec, state): + randomize_inactivity_scores(spec, state, rng=Random(22222)) + yield from rewards_helpers.run_test_full_random(spec, state) + + +@with_altair_and_later +@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE) +@spec_test +@single_phase +def test_full_random_misc_balances(spec, state): + randomize_inactivity_scores(spec, state, rng=Random(33333)) + yield from rewards_helpers.run_test_full_random(spec, state) + + +# +# Leaking variants +# + +@with_altair_and_later +@spec_state_test +@leaking() +def test_random_inactivity_scores_leaking_0(spec, state): + randomize_inactivity_scores(spec, state, rng=Random(9999)) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9999)) + + +@with_altair_and_later +@spec_state_test +@leaking() +def test_random_inactivity_scores_leaking_1(spec, state): + randomize_inactivity_scores(spec, state, rng=Random(10000)) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(10000)) + + +@with_altair_and_later +@spec_state_test +@leaking() +def test_half_zero_half_random_inactivity_scores_leaking(spec, state): + randomize_inactivity_scores(spec, state, rng=Random(10101)) + half_val_point = len(state.validators) // 2 + state.inactivity_scores = [0] * half_val_point + state.inactivity_scores[half_val_point:] + + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(10101)) + + +@with_altair_and_later +@spec_state_test +@leaking() +def test_random_high_inactivity_scores_leaking(spec, state): + randomize_inactivity_scores(spec, state, minimum=500000, maximum=5000000, rng=Random(9998)) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9998)) + + +@with_altair_and_later +@spec_state_test +@leaking(epochs=5) +def test_random_high_inactivity_scores_leaking_5_epochs(spec, state): + randomize_inactivity_scores(spec, state, minimum=500000, maximum=5000000, rng=Random(9998)) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9998)) diff --git a/tests/core/pyspec/eth2spec/test/phase0/rewards/test_random.py b/tests/core/pyspec/eth2spec/test/phase0/rewards/test_random.py index ae44c6640..51bd27c14 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/rewards/test_random.py +++ b/tests/core/pyspec/eth2spec/test/phase0/rewards/test_random.py @@ -39,7 +39,15 @@ def test_full_random_3(spec, state): @with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE) @spec_test @single_phase -def test_full_random_low_balances(spec, state): +def test_full_random_low_balances_0(spec, state): + yield from rewards_helpers.run_test_full_random(spec, state) + + +@with_all_phases +@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: 0) +@spec_test +@single_phase +def test_full_random_low_balances_1(spec, state): yield from rewards_helpers.run_test_full_random(spec, state) From 4a91c939624c1f09026908d2af4f147dd8fca909 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 12 May 2021 14:58:32 -0600 Subject: [PATCH 5/8] 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` From 57e13fd5b2cd0c57ab0e693207348d002684e726 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 13 May 2021 10:59:29 -0600 Subject: [PATCH 6/8] add INACTIVITY_SCORE_RECOVERY_RATE to configs --- configs/mainnet/altair.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/mainnet/altair.yaml b/configs/mainnet/altair.yaml index e387fc87a..a6761b142 100644 --- a/configs/mainnet/altair.yaml +++ b/configs/mainnet/altair.yaml @@ -22,6 +22,8 @@ EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 512 # --------------------------------------------------------------- # 2**2 (= 4) INACTIVITY_SCORE_BIAS: 4 +# 2**4 (= 16) +INACTIVITY_SCORE_RECOVERY_RATE: 16 # Signature domains From 699a3f837eea9774971b3a897de81b6ec7a680e5 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 13 May 2021 13:38:43 -0600 Subject: [PATCH 7/8] pr feedback --- configs/minimal/altair.yaml | 2 ++ specs/altair/beacon-chain.md | 2 +- .../epoch_processing/test_process_inactivity_updates.py | 1 + .../test/altair/rewards/test_inactivity_scores.py | 2 +- tests/core/pyspec/eth2spec/test/helpers/rewards.py | 1 - .../pyspec/eth2spec/test/phase0/rewards/test_random.py | 8 ++++---- tests/formats/epoch_processing/README.md | 1 + 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/configs/minimal/altair.yaml b/configs/minimal/altair.yaml index a66b5c7ca..f9b8401e1 100644 --- a/configs/minimal/altair.yaml +++ b/configs/minimal/altair.yaml @@ -22,6 +22,8 @@ EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 8 # --------------------------------------------------------------- # 2**2 (= 4) INACTIVITY_SCORE_BIAS: 4 +# 2**4 (= 16) +INACTIVITY_SCORE_RECOVERY_RATE: 16 # Signature domains diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 3a5cec869..5ce01e6b0 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -606,7 +606,7 @@ 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 + # Score updates based on previous epoch participation, skip genesis epoch if get_current_epoch(state) == GENESIS_EPOCH: return 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 0efdf3e10..7e501dedc 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 @@ -65,6 +65,7 @@ def test_genesis(spec, state): @with_altair_and_later @spec_state_test def test_all_zero_inactivity_scores_empty_participation(spec, state): + next_epoch_via_block(spec, state) state.inactivity_scores = [0] * len(state.validators) 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 a35095278..9eca9a92a 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 @@ -54,7 +54,7 @@ def test_random_inactivity_scores_low_balances_0(spec, state): @with_altair_and_later -@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: 0) +@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE) @spec_test @single_phase def test_random_inactivity_scores_low_balances_1(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/helpers/rewards.py b/tests/core/pyspec/eth2spec/test/helpers/rewards.py index f563c70a2..fa412c974 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/rewards.py +++ b/tests/core/pyspec/eth2spec/test/helpers/rewards.py @@ -265,7 +265,6 @@ _cache_dict = LRU(size=10) def leaking(epochs=None): - def deco(fn): def entry(*args, spec, state, **kw): # If the pre-state is not already known in the LRU, then take it, diff --git a/tests/core/pyspec/eth2spec/test/phase0/rewards/test_random.py b/tests/core/pyspec/eth2spec/test/phase0/rewards/test_random.py index 51bd27c14..b1af64a77 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/rewards/test_random.py +++ b/tests/core/pyspec/eth2spec/test/phase0/rewards/test_random.py @@ -40,15 +40,15 @@ def test_full_random_3(spec, state): @spec_test @single_phase def test_full_random_low_balances_0(spec, state): - yield from rewards_helpers.run_test_full_random(spec, state) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(5050)) @with_all_phases -@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: 0) +@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE) @spec_test @single_phase def test_full_random_low_balances_1(spec, state): - yield from rewards_helpers.run_test_full_random(spec, state) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(6060)) @with_all_phases @@ -56,4 +56,4 @@ def test_full_random_low_balances_1(spec, state): @spec_test @single_phase def test_full_random_misc_balances(spec, state): - yield from rewards_helpers.run_test_full_random(spec, state) + yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(7070)) diff --git a/tests/formats/epoch_processing/README.md b/tests/formats/epoch_processing/README.md index c917e3f75..d9abcaf98 100644 --- a/tests/formats/epoch_processing/README.md +++ b/tests/formats/epoch_processing/README.md @@ -43,5 +43,6 @@ Sub-transitions: - `randao_mixes_reset` - `historical_roots_update` - `participation_record_updates` +- `sync_committee_updates` The resulting state should match the expected `post` state. From 049210bd8aea9c5015b2313534c0522fa7e845ea Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 13 May 2021 13:44:41 -0600 Subject: [PATCH 8/8] utilize new randomize functions in process_inactivity_updates testing --- .../test_process_inactivity_updates.py | 40 ++++--------------- 1 file changed, 8 insertions(+), 32 deletions(-) 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 7e501dedc..1cc8cb9ae 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 @@ -8,6 +8,9 @@ from eth2spec.test.helpers.state import ( from eth2spec.test.helpers.epoch_processing import ( run_epoch_processing_with ) +from eth2spec.test.helpers.random import ( + randomize_attestation_participation, +) def set_full_participation(spec, state): @@ -16,37 +19,10 @@ def set_full_participation(spec, state): full_flags = spec.add_flag(full_flags, flag_index) for index in range(len(state.validators)): + state.current_epoch_participation[index] = full_flags 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') @@ -75,7 +51,7 @@ def test_all_zero_inactivity_scores_empty_participation(spec, state): 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) + randomize_attestation_participation(spec, state, rng=Random(5555)) yield from run_process_inactivity_updates(spec, state) @@ -83,8 +59,8 @@ def test_all_zero_inactivity_scores_random_participation(spec, state): @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) + state.inactivity_scores = [0] * len(state.validators) yield from run_process_inactivity_updates(spec, state) @@ -100,8 +76,8 @@ def test_random_inactivity_scores_empty_participation(spec, state): @spec_state_test def test_random_inactivity_scores_random_participation(spec, state): next_epoch_via_block(spec, state) + randomize_attestation_participation(spec, state, rng=Random(22222)) randomize_inactivity_scores(spec, state, rng=Random(22222)) - randomize_flags(spec, state) yield from run_process_inactivity_updates(spec, state) @@ -109,6 +85,6 @@ def test_random_inactivity_scores_random_participation(spec, state): @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) + randomize_inactivity_scores(spec, state, rng=Random(33333)) yield from run_process_inactivity_updates(spec, state)