Skip to content

Commit 50f8fa6

Browse files
authored
Merge pull request #4001 from jtraglia/improve-initial-earliest-exit-epoch
Update initial `earliest_exit_epoch` calculation & add tests
2 parents 17feed7 + 93d9ee6 commit 50f8fa6

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

specs/electra/fork.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,12 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
7474
epoch = deneb.get_current_epoch(pre)
7575
latest_execution_payload_header = pre.latest_execution_payload_header
7676

77-
exit_epochs = [v.exit_epoch for v in pre.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
78-
if not exit_epochs:
79-
exit_epochs = [get_current_epoch(pre)]
80-
earliest_exit_epoch = max(exit_epochs) + 1
77+
earliest_exit_epoch = compute_activation_exit_epoch(get_current_epoch(pre))
78+
for validator in pre.validators:
79+
if validator.exit_epoch != FAR_FUTURE_EPOCH:
80+
if validator.exit_epoch > earliest_exit_epoch:
81+
earliest_exit_epoch = validator.exit_epoch
82+
earliest_exit_epoch += Epoch(1)
8183

8284
post = BeaconState(
8385
# Versioning

tests/core/pyspec/eth2spec/test/electra/fork/test_electra_fork_basic.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,61 @@ def test_fork_has_compounding_withdrawal_credential(spec, phases, state):
130130
post_state = yield from run_fork_test(post_spec, state)
131131

132132
assert len(post_state.pending_deposits) > 0
133+
134+
135+
@with_phases(phases=[DENEB], other_phases=[ELECTRA])
136+
@spec_test
137+
@with_state
138+
@with_meta_tags(ELECTRA_FORK_TEST_META_TAGS)
139+
def test_fork_earliest_exit_epoch_no_validator_exits(spec, phases, state):
140+
# advance state so the current epoch is not zero
141+
next_epoch(spec, state)
142+
next_epoch(spec, state)
143+
next_epoch(spec, state)
144+
145+
post_spec = phases[ELECTRA]
146+
post_state = yield from run_fork_test(post_spec, state)
147+
148+
# the earliest exit epoch should be the compute_activation_exit_epoch + 1
149+
current_epoch = post_spec.compute_epoch_at_slot(post_state.slot)
150+
expected_earliest_exit_epoch = post_spec.compute_activation_exit_epoch(current_epoch) + 1
151+
assert post_state.earliest_exit_epoch == expected_earliest_exit_epoch
152+
153+
154+
@with_phases(phases=[DENEB], other_phases=[ELECTRA])
155+
@spec_test
156+
@with_state
157+
@with_meta_tags(ELECTRA_FORK_TEST_META_TAGS)
158+
def test_fork_earliest_exit_epoch_is_max_validator_exit_epoch(spec, phases, state):
159+
# assign some validators exit epochs
160+
state.validators[0].exit_epoch = 20
161+
state.validators[1].exit_epoch = 30
162+
state.validators[2].exit_epoch = 10
163+
164+
post_state = yield from run_fork_test(phases[ELECTRA], state)
165+
166+
# the earliest exit epoch should be the greatest validator exit epoch + 1
167+
expected_earliest_exit_epoch = post_state.validators[1].exit_epoch + 1
168+
assert post_state.earliest_exit_epoch == expected_earliest_exit_epoch
169+
170+
171+
@with_phases(phases=[DENEB], other_phases=[ELECTRA])
172+
@spec_test
173+
@with_state
174+
@with_meta_tags(ELECTRA_FORK_TEST_META_TAGS)
175+
def test_fork_earliest_exit_epoch_less_than_current_epoch(spec, phases, state):
176+
# assign a validator an exit epoch
177+
state.validators[0].exit_epoch = 1
178+
179+
# advance state so the current epoch is not zero
180+
next_epoch(spec, state)
181+
next_epoch(spec, state)
182+
next_epoch(spec, state)
183+
184+
post_spec = phases[ELECTRA]
185+
post_state = yield from run_fork_test(post_spec, state)
186+
187+
# the earliest exit epoch should be the compute_activation_exit_epoch + 1
188+
current_epoch = post_spec.compute_epoch_at_slot(post_state.slot)
189+
expected_earliest_exit_epoch = post_spec.compute_activation_exit_epoch(current_epoch) + 1
190+
assert post_state.earliest_exit_epoch == expected_earliest_exit_epoch

0 commit comments

Comments
 (0)