add epoch processing tests for inactivity udpates

This commit is contained in:
Danny Ryan 2021-05-12 14:58:32 -06:00
parent fa1bdabace
commit 4a91c93962
No known key found for this signature in database
GPG Key ID: 2765A792E42CE07A
6 changed files with 126 additions and 7 deletions

View File

@ -606,6 +606,10 @@ def process_justification_and_finalization(state: BeaconState) -> None:
```python ```python
def process_inactivity_updates(state: BeaconState) -> None: 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): for index in get_eligible_validator_indices(state):
# Increase inactivity score of inactive validators # Increase inactivity score of inactive validators
if index in get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)): if index in get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)):

View File

@ -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)

View File

@ -8,14 +8,11 @@ from eth2spec.test.context import (
single_phase, single_phase,
low_balances, misc_balances, low_balances, misc_balances,
) )
from eth2spec.test.helpers.inactivity_scores import randomize_inactivity_scores
from eth2spec.test.helpers.rewards import leaking from eth2spec.test.helpers.rewards import leaking
import eth2spec.test.helpers.rewards as rewards_helpers 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 @with_altair_and_later
@spec_state_test @spec_state_test
def test_random_inactivity_scores_0(spec, state): def test_random_inactivity_scores_0(spec, state):

View File

@ -9,6 +9,7 @@ def get_process_calls(spec):
# or the old function will stick around. # or the old function will stick around.
return [ return [
'process_justification_and_finalization', 'process_justification_and_finalization',
'process_inactivity_updates', # altair
'process_rewards_and_penalties', 'process_rewards_and_penalties',
'process_registry_updates', 'process_registry_updates',
'process_reveal_deadlines', # custody game '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_flag_updates' if is_post_altair(spec) else (
'process_participation_record_updates' 'process_participation_record_updates'
), ),
'process_sync_committee_updates', 'process_sync_committee_updates', # altair
'process_shard_epoch_increment' # sharding 'process_shard_epoch_increment' # sharding
] ]

View File

@ -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))]

View File

@ -32,9 +32,8 @@ The provided pre-state is already transitioned to just before the specific sub-t
Sub-transitions: Sub-transitions:
Sub-transitions:
- `justification_and_finalization` - `justification_and_finalization`
- `inactivity_penalty_updates`
- `rewards_and_penalties` - `rewards_and_penalties`
- `registry_updates` - `registry_updates`
- `slashings` - `slashings`