Refactoring. Sort tests to specific files.

This commit is contained in:
Hsiao-Wei Wang 2021-10-13 00:26:23 +08:00
parent 95f940cc74
commit f0980a4ab9
No known key found for this signature in database
GPG Key ID: 1111A8A81778319E
6 changed files with 540 additions and 483 deletions

View File

@ -0,0 +1,210 @@
import random
from eth2spec.test.context import fork_transition_test
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.helpers.attestations import next_slots_with_attestations
from eth2spec.test.helpers.deposits import prepare_state_and_deposit
from eth2spec.test.helpers.fork_transition import (
do_altair_fork,
set_validators_exit_epoch,
state_transition_across_slots,
transition_until_fork,
)
from eth2spec.test.helpers.random import set_some_new_deposits
#
# Exit
#
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
def test_transition_with_one_fourth_exiting_validators_exit_post_fork(
state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
1/4 exiting but still active validators at the fork transition.
"""
exited_indices = set_validators_exit_epoch(spec, state, exit_epoch=10, rng=random.Random(5566), fraction=0.25)
transition_until_fork(spec, state, fork_epoch)
# check pre state
assert len(exited_indices) > 0
for index in exited_indices:
validator = state.validators[index]
assert not validator.slashed
assert fork_epoch < validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
assert not spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# ensure that some of the current sync committee members are exiting
exited_pubkeys = [state.validators[index].pubkey for index in exited_indices]
assert any(set(exited_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
state_transition_across_slots(post_spec, state, to_slot)
])
# check state
for index in exited_indices:
validator = state.validators[index]
assert not validator.slashed
assert post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
assert not post_spec.is_in_inactivity_leak(state)
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
def test_transition_with_one_fourth_exiting_validators_exit_at_fork(
state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
1/4 exiting but still active validators at the fork transition.
"""
exited_indices = set_validators_exit_epoch(spec, state, exit_epoch=2, rng=random.Random(5566), fraction=0.25)
transition_until_fork(spec, state, fork_epoch)
# check pre state
assert len(exited_indices) > 0
for index in exited_indices:
validator = state.validators[index]
assert not validator.slashed
assert fork_epoch == validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
assert not spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
for index in exited_indices:
validator = state.validators[index]
assert not validator.slashed
assert not post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
assert not post_spec.is_in_inactivity_leak(state)
# ensure that none of the current sync committee members are exited validators
exited_pubkeys = [state.validators[index].pubkey for index in exited_indices]
assert not any(set(exited_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
state_transition_across_slots(post_spec, state, to_slot)
])
yield "blocks", blocks
yield "post", state
#
# Activation
#
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
def test_transition_with_non_empty_activation_queue(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create some deposits before the transition
"""
transition_until_fork(spec, state, fork_epoch)
_, queuing_indices = set_some_new_deposits(spec, state, rng=random.Random(5566))
assert spec.get_current_epoch(state) < fork_epoch
assert len(queuing_indices) > 0
for validator_index in queuing_indices:
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
state_transition_across_slots(post_spec, state, to_slot)
])
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
def test_transition_with_deposit_at_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create a deposit at the transition
"""
transition_until_fork(spec, state, fork_epoch)
yield "pre", state
# create a new deposit
validator_index = len(state.validators)
amount = post_spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(post_spec, state, validator_index, amount, signed=True)
# irregular state transition to handle fork:
state, block = do_altair_fork(state, spec, post_spec, fork_epoch, deposits=[deposit])
blocks = []
blocks.append(post_tag(block))
assert not post_spec.is_active_validator(state.validators[validator_index], post_spec.get_current_epoch(state))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
state_transition_across_slots(post_spec, state, to_slot)
])
# finalize activation_eligibility_epoch
_, blocks_in_epoch, state = next_slots_with_attestations(
post_spec,
state,
spec.SLOTS_PER_EPOCH * 2,
fill_cur_epoch=True,
fill_prev_epoch=True,
)
blocks.extend([pre_tag(block) for block in blocks_in_epoch])
assert state.finalized_checkpoint.epoch == state.validators[validator_index].activation_eligibility_epoch
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
state_transition_across_slots(post_spec, state, to_slot)
])
assert state.validators[validator_index].activation_epoch < post_spec.FAR_FUTURE_EPOCH
to_slot = state.validators[validator_index].activation_epoch * post_spec.SLOTS_PER_EPOCH
blocks.extend([
post_tag(block) for block in
state_transition_across_slots(post_spec, state, to_slot)
])
assert post_spec.is_active_validator(state.validators[validator_index], post_spec.get_current_epoch(state))
yield "blocks", blocks
yield "post", state

View File

@ -0,0 +1,106 @@
from eth2spec.test.context import fork_transition_test
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.helpers.fork_transition import (
do_altair_fork,
state_transition_across_slots,
transition_until_fork,
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=7)
def test_transition_with_leaking_pre_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Leaking starts at epoch 6 (MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2).
The leaking starts before the fork transition in this case.
"""
transition_until_fork(spec, state, fork_epoch)
assert spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
assert spec.is_in_inactivity_leak(state)
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
state_transition_across_slots(post_spec, state, to_slot)
])
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=6)
def test_transition_with_leaking_at_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Leaking starts at epoch 6 (MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2).
The leaking starts at the fork transition in this case.
"""
transition_until_fork(spec, state, fork_epoch)
assert not spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
assert spec.is_in_inactivity_leak(state)
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
state_transition_across_slots(post_spec, state, to_slot)
])
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=5)
def test_transition_with_leaking_post_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Leaking starts at epoch 6 (MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2).
The leaking starts after the fork transition in this case.
"""
transition_until_fork(spec, state, fork_epoch)
assert not spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
assert not spec.is_in_inactivity_leak(state)
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
state_transition_across_slots(post_spec, state, to_slot)
])
# check state again
assert spec.is_in_inactivity_leak(state)
yield "blocks", blocks
yield "post", state

View File

@ -0,0 +1,61 @@
import random
from eth2spec.test.context import fork_transition_test
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.helpers.fork_transition import (
do_altair_fork,
state_transition_across_slots_with_ignoring_proposers,
transition_until_fork,
)
from eth2spec.test.helpers.inactivity_scores import (
slash_some_validators_for_inactivity_scores_test,
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=1)
def test_transition_with_one_fourth_slashed_active_validators_pre_fork(
state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
1/4 validators are slashed but still active at the fork transition.
"""
# slash 1/4 validators
slashed_indices = slash_some_validators_for_inactivity_scores_test(
spec, state, rng=random.Random(5566), fraction=0.25)
assert len(slashed_indices) > 0
# check if some validators are slashed but still active
for validator_index in slashed_indices:
validator = state.validators[validator_index]
assert validator.slashed
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
assert not spec.is_in_inactivity_leak(state)
transition_until_fork(spec, state, fork_epoch)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# ensure that some of the current sync committee members are the slashed
slashed_pubkeys = [state.validators[index].pubkey for index in slashed_indices]
assert any(set(slashed_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
# since the proposer might have been slashed, here we only create blocks with non-slashed proposers
blocks.extend([
post_tag(block) for block in
state_transition_across_slots_with_ignoring_proposers(post_spec, state, to_slot, slashed_indices)
])
# check post state
for validator in state.validators:
assert post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
assert not post_spec.is_in_inactivity_leak(state)
yield "blocks", blocks
yield "post", state

View File

@ -3,139 +3,17 @@ from eth2spec.test.context import fork_transition_test
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.helpers.state import (
next_epoch_via_signed_block,
next_slot,
state_transition_and_sign_block,
transition_to,
)
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block
from eth2spec.test.helpers.deposits import (
prepare_state_and_deposit,
)
from eth2spec.test.helpers.attestations import next_slots_with_attestations
from eth2spec.test.helpers.random import set_some_new_deposits
from eth2spec.test.helpers.inactivity_scores import (
slash_some_validators_for_inactivity_scores_test,
from eth2spec.test.helpers.fork_transition import (
do_altair_fork,
no_blocks,
only_at,
skip_slots,
state_transition_across_slots,
)
def _state_transition_and_sign_block_at_slot(spec, state, deposits=None):
"""
Cribbed from ``transition_unsigned_block`` helper
where the early parts of the state transition have already
been applied to ``state``.
Used to produce a block during an irregular state transition.
"""
block = build_empty_block(spec, state)
# FIXME: not just passing `deposits`
if deposits is not None:
block.body.deposits = deposits
assert state.latest_block_header.slot < block.slot
assert state.slot == block.slot
spec.process_block(state, block)
block.state_root = state.hash_tree_root()
return sign_block(spec, state, block)
def _all_blocks(_):
return True
def _skip_slots(*slots):
"""
Skip making a block if its slot is
passed as an argument to this filter
"""
def f(state_at_prior_slot):
return state_at_prior_slot.slot + 1 not in slots
return f
def _no_blocks(_):
return False
def _only_at(slot):
"""
Only produce a block if its slot is ``slot``.
"""
def f(state_at_prior_slot):
return state_at_prior_slot.slot + 1 == slot
return f
def _state_transition_across_slots(spec, state, to_slot, block_filter=_all_blocks):
assert state.slot < to_slot
while state.slot < to_slot:
should_make_block = block_filter(state)
if should_make_block:
block = build_empty_block_for_next_slot(spec, state)
signed_block = state_transition_and_sign_block(spec, state, block)
yield signed_block
else:
next_slot(spec, state)
def _state_transition_across_slots_with_ignoring_proposers(spec, state, to_slot, ignoring_proposers):
"""
The slashed validators can't be proposers. Here we ignore the given `ignoring_proposers`
and ensure that the result state was computed with a block with slot >= to_slot.
"""
assert state.slot < to_slot
found_valid = False
while state.slot < to_slot or not found_valid:
future_state = state.copy()
next_slot(spec, future_state)
proposer_index = spec.get_beacon_proposer_index(future_state)
if proposer_index not in ignoring_proposers:
block = build_empty_block_for_next_slot(spec, state)
signed_block = state_transition_and_sign_block(spec, state, block)
yield signed_block
if state.slot >= to_slot:
found_valid = True
else:
next_slot(spec, state)
def _do_altair_fork(state, spec, post_spec, fork_epoch, with_block=True, deposits=None):
spec.process_slots(state, state.slot + 1)
assert state.slot % spec.SLOTS_PER_EPOCH == 0
assert spec.get_current_epoch(state) == fork_epoch
state = post_spec.upgrade_to_altair(state)
assert state.fork.epoch == fork_epoch
assert state.fork.previous_version == post_spec.config.GENESIS_FORK_VERSION
assert state.fork.current_version == post_spec.config.ALTAIR_FORK_VERSION
if with_block:
return state, _state_transition_and_sign_block_at_slot(post_spec, state, deposits=deposits)
else:
return state, None
def _set_validators_exit_epoch(spec, state, exit_epoch, rng=random.Random(40404040), fraction=0.25):
"""
Set some valdiators' exit_epoch.
"""
selected_count = int(len(state.validators) * fraction)
selected_indices = rng.sample(range(len(state.validators)), selected_count)
for validator_index in selected_indices:
state.validators[validator_index].exit_epoch = exit_epoch
state.validators[validator_index].withdrawable_epoch = (
exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
return selected_indices
def _transition_until_fork(spec, state, fork_epoch):
to_slot = fork_epoch * spec.SLOTS_PER_EPOCH - 1
transition_to(spec, state, to_slot)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
def test_normal_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
@ -151,18 +29,18 @@ def test_normal_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag
blocks = []
blocks.extend([
pre_tag(block) for block in
_state_transition_across_slots(spec, state, to_slot)
state_transition_across_slots(spec, state, to_slot)
])
# irregular state transition to handle fork:
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
state_transition_across_slots(post_spec, state, to_slot)
])
assert state.slot % post_spec.SLOTS_PER_EPOCH == 0
@ -192,17 +70,17 @@ def test_transition_missing_first_post_block(state, fork_epoch, spec, post_spec,
blocks = []
blocks.extend([
pre_tag(block) for block in
_state_transition_across_slots(spec, state, to_slot)
state_transition_across_slots(spec, state, to_slot)
])
# irregular state transition to handle fork:
state, _ = _do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
state, _ = do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
state_transition_across_slots(post_spec, state, to_slot)
])
assert state.slot % post_spec.SLOTS_PER_EPOCH == 0
@ -234,18 +112,18 @@ def test_transition_missing_last_pre_fork_block(state, fork_epoch, spec, post_sp
blocks = []
blocks.extend([
pre_tag(block) for block in
_state_transition_across_slots(spec, state, to_slot, block_filter=_skip_slots(last_slot_of_pre_fork))
state_transition_across_slots(spec, state, to_slot, block_filter=skip_slots(last_slot_of_pre_fork))
])
# irregular state transition to handle fork:
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
state_transition_across_slots(post_spec, state, to_slot)
])
assert state.slot % post_spec.SLOTS_PER_EPOCH == 0
@ -277,18 +155,18 @@ def test_transition_only_blocks_post_fork(state, fork_epoch, spec, post_spec, pr
blocks = []
blocks.extend([
pre_tag(block) for block in
_state_transition_across_slots(spec, state, to_slot, block_filter=_no_blocks)
state_transition_across_slots(spec, state, to_slot, block_filter=no_blocks)
])
# irregular state transition to handle fork:
state, _ = _do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
state, _ = do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
last_slot = (fork_epoch + 1) * post_spec.SLOTS_PER_EPOCH
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot, block_filter=_only_at(last_slot))
state_transition_across_slots(post_spec, state, to_slot, block_filter=only_at(last_slot))
])
assert state.slot % post_spec.SLOTS_PER_EPOCH == 0
@ -348,7 +226,7 @@ def _run_transition_test_with_attestations(state,
assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0
# irregular state transition to handle fork:
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
@ -461,11 +339,11 @@ def test_transition_with_no_attestations_until_after_fork(state, fork_epoch, spe
blocks = []
blocks.extend([
pre_tag(block) for block in
_state_transition_across_slots(spec, state, to_slot)
state_transition_across_slots(spec, state, to_slot)
])
# irregular state transition to handle fork:
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition but add attestations
@ -490,341 +368,3 @@ def test_transition_with_no_attestations_until_after_fork(state, fork_epoch, spe
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=1)
def test_transition_with_one_fourth_slashed_active_validators_pre_fork(
state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
1/4 validators are slashed but still active at the fork transition.
"""
# slash 1/4 validators
slashed_indices = slash_some_validators_for_inactivity_scores_test(
spec, state, rng=random.Random(5566), fraction=0.25)
assert len(slashed_indices) > 0
# check if some validators are slashed but still active
for validator_index in slashed_indices:
validator = state.validators[validator_index]
assert validator.slashed
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
assert not spec.is_in_inactivity_leak(state)
_transition_until_fork(spec, state, fork_epoch)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# ensure that some of the current sync committee members are the slashed
slashed_pubkeys = [state.validators[index].pubkey for index in slashed_indices]
assert any(set(slashed_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
# since the proposer might have been slashed, here we only create blocks with non-slashed proposers
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots_with_ignoring_proposers(post_spec, state, to_slot, slashed_indices)
])
# check post state
for validator in state.validators:
assert post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
assert not post_spec.is_in_inactivity_leak(state)
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
def test_transition_with_one_fourth_exiting_validators_exit_post_fork(
state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
1/4 exiting but still active validators at the fork transition.
"""
exited_indices = _set_validators_exit_epoch(spec, state, exit_epoch=10, rng=random.Random(5566), fraction=0.25)
_transition_until_fork(spec, state, fork_epoch)
# check pre state
assert len(exited_indices) > 0
for index in exited_indices:
validator = state.validators[index]
assert not validator.slashed
assert fork_epoch < validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
assert not spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# ensure that some of the current sync committee members are exiting
exited_pubkeys = [state.validators[index].pubkey for index in exited_indices]
assert any(set(exited_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
])
# check state
for index in exited_indices:
validator = state.validators[index]
assert not validator.slashed
assert post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
assert not post_spec.is_in_inactivity_leak(state)
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
def test_transition_with_one_fourth_exiting_validators_exit_at_fork(
state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
1/4 exiting but still active validators at the fork transition.
"""
exited_indices = _set_validators_exit_epoch(spec, state, exit_epoch=2, rng=random.Random(5566), fraction=0.25)
_transition_until_fork(spec, state, fork_epoch)
# check pre state
assert len(exited_indices) > 0
for index in exited_indices:
validator = state.validators[index]
assert not validator.slashed
assert fork_epoch == validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
assert not spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
for index in exited_indices:
validator = state.validators[index]
assert not validator.slashed
assert not post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
assert not post_spec.is_in_inactivity_leak(state)
# ensure that none of the current sync committee members are exited validators
exited_pubkeys = [state.validators[index].pubkey for index in exited_indices]
assert not any(set(exited_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
])
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=7)
def test_transition_with_leaking_pre_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Leaking starts at epoch 6 (MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2).
The leaking starts before the fork transition in this case.
"""
_transition_until_fork(spec, state, fork_epoch)
assert spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
assert spec.is_in_inactivity_leak(state)
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
])
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=6)
def test_transition_with_leaking_at_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Leaking starts at epoch 6 (MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2).
The leaking starts at the fork transition in this case.
"""
_transition_until_fork(spec, state, fork_epoch)
assert not spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
assert spec.is_in_inactivity_leak(state)
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
])
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=5)
def test_transition_with_leaking_post_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Leaking starts at epoch 6 (MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2).
The leaking starts after the fork transition in this case.
"""
_transition_until_fork(spec, state, fork_epoch)
assert not spec.is_in_inactivity_leak(state)
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
assert not spec.is_in_inactivity_leak(state)
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
])
# check state again
assert spec.is_in_inactivity_leak(state)
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=10)
def test_transition_with_non_empty_activation_queue(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create some deposits before the transition
"""
_transition_until_fork(spec, state, fork_epoch)
_, queuing_indices = set_some_new_deposits(spec, state, rng=random.Random(5566))
assert spec.get_current_epoch(state) < fork_epoch
assert len(queuing_indices) > 0
for validator_index in queuing_indices:
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
])
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=10)
def test_transition_with_deposit_at_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create a deposit at the transition
"""
_transition_until_fork(spec, state, fork_epoch)
yield "pre", state
# create a new deposit
validator_index = len(state.validators)
amount = post_spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(post_spec, state, validator_index, amount, signed=True)
# irregular state transition to handle fork:
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch, deposits=[deposit])
blocks = []
blocks.append(post_tag(block))
assert not post_spec.is_active_validator(state.validators[validator_index], post_spec.get_current_epoch(state))
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
])
# finalize activation_eligibility_epoch
_, blocks_in_epoch, state = next_slots_with_attestations(
post_spec,
state,
spec.SLOTS_PER_EPOCH * 2,
fill_cur_epoch=True,
fill_prev_epoch=True,
)
blocks.extend([pre_tag(block) for block in blocks_in_epoch])
assert state.finalized_checkpoint.epoch == state.validators[validator_index].activation_eligibility_epoch
# continue regular state transition with new spec into next epoch
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
])
assert state.validators[validator_index].activation_epoch < post_spec.FAR_FUTURE_EPOCH
to_slot = state.validators[validator_index].activation_epoch * post_spec.SLOTS_PER_EPOCH
blocks.extend([
post_tag(block) for block in
_state_transition_across_slots(post_spec, state, to_slot)
])
assert post_spec.is_active_validator(state.validators[validator_index], post_spec.get_current_epoch(state))
yield "blocks", blocks
yield "post", state

View File

@ -0,0 +1,130 @@
import random
from eth2spec.test.helpers.state import (
next_slot,
state_transition_and_sign_block,
transition_to,
)
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
build_empty_block,
sign_block,
)
def _state_transition_and_sign_block_at_slot(spec, state, deposits=None):
"""
Cribbed from ``transition_unsigned_block`` helper
where the early parts of the state transition have already
been applied to ``state``.
Used to produce a block during an irregular state transition.
"""
block = build_empty_block(spec, state)
# FIXME: not just passing `deposits`
if deposits is not None:
block.body.deposits = deposits
assert state.latest_block_header.slot < block.slot
assert state.slot == block.slot
spec.process_block(state, block)
block.state_root = state.hash_tree_root()
return sign_block(spec, state, block)
def _all_blocks(_):
return True
def skip_slots(*slots):
"""
Skip making a block if its slot is
passed as an argument to this filter
"""
def f(state_at_prior_slot):
return state_at_prior_slot.slot + 1 not in slots
return f
def no_blocks(_):
return False
def only_at(slot):
"""
Only produce a block if its slot is ``slot``.
"""
def f(state_at_prior_slot):
return state_at_prior_slot.slot + 1 == slot
return f
def state_transition_across_slots(spec, state, to_slot, block_filter=_all_blocks):
assert state.slot < to_slot
while state.slot < to_slot:
should_make_block = block_filter(state)
if should_make_block:
block = build_empty_block_for_next_slot(spec, state)
signed_block = state_transition_and_sign_block(spec, state, block)
yield signed_block
else:
next_slot(spec, state)
def state_transition_across_slots_with_ignoring_proposers(spec, state, to_slot, ignoring_proposers):
"""
The slashed validators can't be proposers. Here we ignore the given `ignoring_proposers`
and ensure that the result state was computed with a block with slot >= to_slot.
"""
assert state.slot < to_slot
found_valid = False
while state.slot < to_slot or not found_valid:
future_state = state.copy()
next_slot(spec, future_state)
proposer_index = spec.get_beacon_proposer_index(future_state)
if proposer_index not in ignoring_proposers:
block = build_empty_block_for_next_slot(spec, state)
signed_block = state_transition_and_sign_block(spec, state, block)
yield signed_block
if state.slot >= to_slot:
found_valid = True
else:
next_slot(spec, state)
def do_altair_fork(state, spec, post_spec, fork_epoch, with_block=True, deposits=None):
spec.process_slots(state, state.slot + 1)
assert state.slot % spec.SLOTS_PER_EPOCH == 0
assert spec.get_current_epoch(state) == fork_epoch
state = post_spec.upgrade_to_altair(state)
assert state.fork.epoch == fork_epoch
assert state.fork.previous_version == post_spec.config.GENESIS_FORK_VERSION
assert state.fork.current_version == post_spec.config.ALTAIR_FORK_VERSION
if with_block:
return state, _state_transition_and_sign_block_at_slot(post_spec, state, deposits=deposits)
else:
return state, None
def set_validators_exit_epoch(spec, state, exit_epoch, rng=random.Random(40404040), fraction=0.25):
"""
Set some valdiators' exit_epoch.
"""
selected_count = int(len(state.validators) * fraction)
selected_indices = rng.sample(range(len(state.validators)), selected_count)
for validator_index in selected_indices:
state.validators[validator_index].exit_epoch = exit_epoch
state.validators[validator_index].withdrawable_epoch = (
exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
return selected_indices
def transition_until_fork(spec, state, fork_epoch):
to_slot = fork_epoch * spec.SLOTS_PER_EPOCH - 1
transition_to(spec, state, to_slot)

View File

@ -1,7 +1,12 @@
from typing import Iterable
from eth2spec.test.helpers.constants import ALTAIR, MINIMAL, MAINNET, PHASE0
from eth2spec.test.altair.transition import test_transition as test_altair_transition
from eth2spec.test.altair.transition import (
test_transition as test_altair_transition,
test_activation as test_altair_activation,
test_leaking as test_altair_leaking,
test_slashing as test_altair_slashing,
)
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
from eth2spec.gen_helpers.gen_from_tests.gen import generate_from_tests
@ -25,7 +30,12 @@ def create_provider(tests_src, preset_name: str, pre_fork_name: str, post_fork_n
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
TRANSITION_TESTS = ((PHASE0, ALTAIR, test_altair_transition),)
TRANSITION_TESTS = (
(PHASE0, ALTAIR, test_altair_transition),
(PHASE0, ALTAIR, test_altair_activation),
(PHASE0, ALTAIR, test_altair_leaking),
(PHASE0, ALTAIR, test_altair_slashing),
)
if __name__ == "__main__":