diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py new file mode 100644 index 000000000..13e1c3f3b --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py @@ -0,0 +1,44 @@ +from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block +from eth2spec.test.helpers.state import state_transition_and_sign_block + + +process_calls = ( + 'process_justification_and_finalization' + 'process_crosslinks' + 'process_rewards_and_penalties' + 'process_registry_updates' + 'process_reveal_deadlines' + 'process_challenge_deadlines' + 'process_slashings' + 'process_final_updates' + 'after_process_final_updates' +) + + +def run_epoch_processing_to(spec, state, process_name: str): + """ + Run the epoch processing functions up to ``process_name`` (incl.), yielding: + - pre-state ('pre'), state before calling ``process_name`` + - post-state ('post'), state after calling ``process_name`` + """ + # transition state to slot before state transition + slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 + block = build_empty_block_for_next_slot(spec, state) + block.slot = slot + sign_block(spec, state, block) + state_transition_and_sign_block(spec, state, block) + + # cache state before epoch transition + spec.process_slot(state) + + # process components of epoch transition before final-updates + for name in process_calls: + if name == process_name: + break + # only run when present. Later phases introduce more to the epoch-processing. + if hasattr(spec, name): + getattr(spec, name)(state) + + yield 'pre', state + getattr(spec, process_name)(state) + yield 'post', state diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py index d51191efb..a38435b96 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py @@ -4,41 +4,19 @@ from eth2spec.test.context import spec_state_test, with_all_phases from eth2spec.test.helpers.state import ( next_epoch, next_slot, - state_transition_and_sign_block, ) -from eth2spec.test.helpers.block import apply_empty_block, sign_block +from eth2spec.test.helpers.block import apply_empty_block from eth2spec.test.helpers.attestations import ( add_attestation_to_state, - build_empty_block_for_next_slot, fill_aggregate_attestation, get_valid_attestation, sign_attestation, ) +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to -def run_process_crosslinks(spec, state, valid=True): - """ - Run ``process_crosslinks``, yielding: - - pre-state ('pre') - - post-state ('post'). - If ``valid == False``, run expecting ``AssertionError`` - """ - # transition state to slot before state transition - slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 - block = build_empty_block_for_next_slot(spec, state) - block.slot = slot - sign_block(spec, state, block) - state_transition_and_sign_block(spec, state, block) - - # cache state before epoch transition - spec.process_slot(state) - - # process components of epoch transition before processing crosslinks - spec.process_justification_and_finalization(state) - - yield 'pre', state - spec.process_crosslinks(state) - yield 'post', state +def run_process_crosslinks(spec, state): + yield from run_epoch_processing_to(spec, state, 'process_crosslinks') @with_all_phases diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py new file mode 100644 index 000000000..ca2f9eb10 --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py @@ -0,0 +1,53 @@ +from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to + + +def run_process_final_updates(spec, state): + yield from run_epoch_processing_to(spec, state, 'process_final_updates') + + +@with_all_phases +@spec_state_test +def test_eth1_vote_reset(spec, state): + # skip ahead to near the end of the voting period + state.slot = spec.SLOTS_PER_ETH1_VOTING_PERIOD - 2 + for i in range(state.slot + 1): # add a vote for each skipped slot. + state.eth1_data_votes.append( + spec.Eth1Data(deposit_root=b'\xaa' * 32, + deposit_count=state.eth1_deposit_index, + block_hash=b'\xbb' * 32)) + + yield from run_process_final_updates(spec, state) + + assert len(state.eth1_data_votes) == 0 + + +@with_all_phases +@spec_state_test +def test_effective_balance_hysteresis(spec, state): + # Set some edge cases for balances + max = spec.MAX_EFFECTIVE_BALANCE + min = spec.EJECTION_BALANCE + inc = spec.EFFECTIVE_BALANCE_INCREMENT + half_inc = inc // 2 + cases = [ + (max, max, max), # as is + (max, max - 1, max - inc), # round down, step lower + (max, max + 1, max), # round down + (max, max - inc, max - inc), # exactly 1 step lower + (max, max - inc - 1, max - (2 * inc)), # just 1 over 1 step lower + (max, max - inc + 1, max - inc), # close to 1 step lower + (min, min + (half_inc * 3), min), # bigger balance, but not high enough + (min, min + (half_inc * 3) + 1, min + inc), # bigger balance, high enough, but small step + (min, min + (half_inc * 4) - 1, min + inc), # bigger balance, high enough, close to double step + (min, min + (half_inc * 4), min + (2 * inc)), # exact two step balance increment + (min, min + (half_inc * 4) + 1, min + (2 * inc)), # over two steps, round down + ] + for i, (pre_eff, bal, _) in enumerate(cases): + state.validators[i].effective_balance = pre_eff + state.balances[i] = bal + + yield from run_process_final_updates(spec, state) + + for i, (_, _, post_eff) in enumerate(cases): + assert state.validators[i].effective_balance == post_eff diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py index 6c1319a5b..ae76e95c2 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py @@ -1,33 +1,10 @@ -from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block -from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block +from eth2spec.test.helpers.state import next_epoch from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to -def run_process_registry_updates(spec, state, valid=True): - """ - Run ``process_crosslinks``, yielding: - - pre-state ('pre') - - post-state ('post'). - If ``valid == False``, run expecting ``AssertionError`` - """ - # transition state to slot before state transition - slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 - block = build_empty_block_for_next_slot(spec, state) - block.slot = slot - sign_block(spec, state, block) - state_transition_and_sign_block(spec, state, block) - - # cache state before epoch transition - spec.process_slot(state) - - # process components of epoch transition before registry update - spec.process_justification_and_finalization(state) - spec.process_crosslinks(state) - spec.process_rewards_and_penalties(state) - - yield 'pre', state - spec.process_registry_updates(state) - yield 'post', state +def run_process_registry_updates(spec, state): + yield from run_epoch_processing_to(spec, state, 'process_registry_updates') def mock_deposit(spec, state, index):