Merge pull request #2706 from ethereum/transition-reuse

This commit is contained in:
Hsiao-Wei Wang 2021-11-20 08:21:22 +08:00 committed by GitHub
commit 29beba6ab5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 353 additions and 199 deletions

View File

@ -1,12 +1,16 @@
import random
from eth2spec.test.context import (
MINIMAL,
fork_transition_test,
ForkMeta,
ALTAIR,
with_presets,
with_fork_metas,
)
from eth2spec.test.helpers.constants import (
ALL_PRE_POST_FORKS,
MINIMAL,
)
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.helpers.fork_transition import (
do_altair_fork,
do_fork,
transition_until_fork,
transition_to_next_epoch_and_append_blocks,
)
@ -21,7 +25,7 @@ from eth2spec.test.helpers.random import (
# Exit
#
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
@with_presets([MINIMAL],
reason="only test with enough validators such that at least one exited index is not in sync committee")
def test_transition_with_one_fourth_exiting_validators_exit_post_fork(state,
@ -59,7 +63,7 @@ def test_transition_with_one_fourth_exiting_validators_exit_post_fork(state,
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# ensure that some of the current sync committee members are exiting
@ -81,7 +85,7 @@ def test_transition_with_one_fourth_exiting_validators_exit_post_fork(state,
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_with_one_fourth_exiting_validators_exit_at_fork(state,
fork_epoch,
spec,
@ -117,7 +121,7 @@ def test_transition_with_one_fourth_exiting_validators_exit_at_fork(state,
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
@ -127,9 +131,13 @@ def test_transition_with_one_fourth_exiting_validators_exit_at_fork(state,
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)))
some_sync_committee_exited = any(set(exited_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
if post_spec.fork == ALTAIR:
# in Altair fork, the sync committee members would be set with only active validators
assert not some_sync_committee_exited
else:
assert some_sync_committee_exited
# continue regular state transition with new spec into next epoch
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)
@ -143,7 +151,7 @@ def test_transition_with_one_fourth_exiting_validators_exit_at_fork(state,
#
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_with_non_empty_activation_queue(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create some deposits before the transition
@ -161,7 +169,7 @@ def test_transition_with_non_empty_activation_queue(state, fork_epoch, spec, pos
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
@ -171,7 +179,7 @@ def test_transition_with_non_empty_activation_queue(state, fork_epoch, spec, pos
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_with_activation_at_fork_epoch(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create some deposits before the transition
@ -191,7 +199,7 @@ def test_transition_with_activation_at_fork_epoch(state, fork_epoch, spec, post_
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch

View File

@ -1,13 +1,18 @@
from eth2spec.test.context import fork_transition_test
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.context import (
ForkMeta,
with_fork_metas,
)
from eth2spec.test.helpers.constants import (
ALL_PRE_POST_FORKS,
)
from eth2spec.test.helpers.fork_transition import (
do_altair_fork,
do_fork,
transition_until_fork,
transition_to_next_epoch_and_append_blocks,
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=7)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=7) for pre, post in ALL_PRE_POST_FORKS])
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).
@ -22,7 +27,7 @@ def test_transition_with_leaking_pre_fork(state, fork_epoch, spec, post_spec, pr
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state
@ -35,7 +40,7 @@ def test_transition_with_leaking_pre_fork(state, fork_epoch, spec, post_spec, pr
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=6)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=6) for pre, post in ALL_PRE_POST_FORKS])
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).
@ -50,7 +55,7 @@ def test_transition_with_leaking_at_fork(state, fork_epoch, spec, post_spec, pre
# irregular state transition to handle fork:
blocks = []
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# check post transition state

View File

@ -1,8 +1,13 @@
from eth2spec.test.context import (
ForkMeta,
always_bls,
fork_transition_test,
with_fork_metas,
with_presets,
)
from eth2spec.test.helpers.constants import (
ALL_PRE_POST_FORKS,
MINIMAL,
)
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.helpers.fork_transition import (
OperationType,
run_transition_with_operation,
@ -13,7 +18,7 @@ from eth2spec.test.helpers.fork_transition import (
# PROPOSER_SLASHING
#
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
@always_bls
def test_transition_with_proposer_slashing_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
@ -31,7 +36,7 @@ def test_transition_with_proposer_slashing_right_after_fork(state, fork_epoch, s
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
@always_bls
def test_transition_with_proposer_slashing_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
@ -54,7 +59,7 @@ def test_transition_with_proposer_slashing_right_before_fork(state, fork_epoch,
#
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
@always_bls
def test_transition_with_attester_slashing_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
@ -72,7 +77,7 @@ def test_transition_with_attester_slashing_right_after_fork(state, fork_epoch, s
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
@always_bls
def test_transition_with_attester_slashing_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
@ -95,7 +100,7 @@ def test_transition_with_attester_slashing_right_before_fork(state, fork_epoch,
#
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_with_deposit_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create a deposit right *after* the transition
@ -112,7 +117,7 @@ def test_transition_with_deposit_right_after_fork(state, fork_epoch, spec, post_
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_with_deposit_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create a deposit right *before* the transition
@ -134,11 +139,12 @@ def test_transition_with_deposit_right_before_fork(state, fork_epoch, spec, post
#
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=260)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=66) for pre, post in ALL_PRE_POST_FORKS])
@with_presets([MINIMAL], reason="too slow")
def test_transition_with_voluntary_exit_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create a voluntary exit right *after* the transition.
fork_epoch=260 because mainnet `SHARD_COMMITTEE_PERIOD` is 256 epochs.
fork_epoch=66 because minimal preset `SHARD_COMMITTEE_PERIOD` is 64 epochs.
"""
# Fast forward to the future epoch so that validator can do voluntary exit
state.slot = spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
@ -155,11 +161,12 @@ def test_transition_with_voluntary_exit_right_after_fork(state, fork_epoch, spec
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=260)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=66) for pre, post in ALL_PRE_POST_FORKS])
@with_presets([MINIMAL], reason="too slow")
def test_transition_with_voluntary_exit_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create a voluntary exit right *before* the transition.
fork_epoch=260 because mainnet `SHARD_COMMITTEE_PERIOD` is 256 epochs.
fork_epoch=66 because minimal preset `SHARD_COMMITTEE_PERIOD` is 64 epochs.
"""
# Fast forward to the future epoch so that validator can do voluntary exit
state.slot = spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH

View File

@ -1,12 +1,15 @@
import random
from eth2spec.test.context import (
MINIMAL,
fork_transition_test,
ForkMeta,
with_fork_metas,
with_presets,
)
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.helpers.constants import (
ALL_PRE_POST_FORKS,
MINIMAL,
)
from eth2spec.test.helpers.fork_transition import (
do_altair_fork,
do_fork,
transition_to_next_epoch_and_append_blocks,
transition_until_fork,
)
@ -15,7 +18,7 @@ from eth2spec.test.helpers.random import (
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=1)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=1) for pre, post in ALL_PRE_POST_FORKS])
@with_presets([MINIMAL],
reason="only test with enough validators such that at least one exited index is not in sync committee")
def test_transition_with_one_fourth_slashed_active_validators_pre_fork(state,
@ -45,7 +48,7 @@ def test_transition_with_one_fourth_slashed_active_validators_pre_fork(state,
yield "pre", state
# irregular state transition to handle fork:
state, _ = do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
state, _ = do_fork(state, spec, post_spec, fork_epoch, with_block=False)
# ensure that some of the current sync committee members are slashed
slashed_pubkeys = [state.validators[index].pubkey for index in slashed_indices]

View File

@ -1,12 +1,17 @@
import random
from eth2spec.test.context import fork_transition_test
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.context import (
ForkMeta,
with_fork_metas,
)
from eth2spec.test.helpers.constants import (
ALL_PRE_POST_FORKS,
)
from eth2spec.test.helpers.state import (
next_epoch_via_signed_block,
)
from eth2spec.test.helpers.attestations import next_slots_with_attestations
from eth2spec.test.helpers.fork_transition import (
do_altair_fork,
do_fork,
no_blocks,
only_at,
skip_slots,
@ -15,7 +20,7 @@ from eth2spec.test.helpers.fork_transition import (
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_normal_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Transition from the initial ``state`` to the epoch after the ``fork_epoch``,
@ -34,7 +39,7 @@ def test_normal_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag
])
# irregular state transition to handle fork:
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
@ -51,7 +56,7 @@ def test_normal_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_missing_first_post_block(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Transition from the initial ``state`` to the epoch after the ``fork_epoch``,
@ -71,7 +76,7 @@ def test_transition_missing_first_post_block(state, fork_epoch, spec, post_spec,
])
# irregular state transition to handle fork:
state, _ = do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
state, _ = do_fork(state, spec, post_spec, fork_epoch, with_block=False)
# continue regular state transition with new spec into next epoch
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks)
@ -88,7 +93,7 @@ def test_transition_missing_first_post_block(state, fork_epoch, spec, post_spec,
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_missing_last_pre_fork_block(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Transition from the initial ``state`` to the epoch after the ``fork_epoch``,
@ -109,7 +114,7 @@ def test_transition_missing_last_pre_fork_block(state, fork_epoch, spec, post_sp
])
# irregular state transition to handle fork:
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
@ -127,7 +132,7 @@ def test_transition_missing_last_pre_fork_block(state, fork_epoch, spec, post_sp
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_only_blocks_post_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Transition from the initial ``state`` to the epoch after the ``fork_epoch``,
@ -148,7 +153,7 @@ def test_transition_only_blocks_post_fork(state, fork_epoch, spec, post_spec, pr
])
# irregular state transition to handle fork:
state, _ = do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
state, _ = do_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
@ -215,7 +220,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_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
@ -253,7 +258,7 @@ def _run_transition_test_with_attestations(state,
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=3)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=3) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_with_finality(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Transition from the initial ``state`` to the epoch after the ``fork_epoch``,
@ -262,7 +267,7 @@ def test_transition_with_finality(state, fork_epoch, spec, post_spec, pre_tag, p
yield from _run_transition_test_with_attestations(state, fork_epoch, spec, post_spec, pre_tag, post_tag)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=3)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=3) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_with_random_three_quarters_participation(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Transition from the initial ``state`` to the epoch after the ``fork_epoch``,
@ -289,7 +294,7 @@ def test_transition_with_random_three_quarters_participation(state, fork_epoch,
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=3)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=3) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_with_random_half_participation(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
rng = random.Random(2020)
@ -313,7 +318,7 @@ def test_transition_with_random_half_participation(state, fork_epoch, spec, post
)
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=3) for pre, post in ALL_PRE_POST_FORKS])
def test_transition_with_no_attestations_until_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Transition from the initial ``state`` to the ``fork_epoch`` with no attestations,
@ -332,7 +337,7 @@ def test_transition_with_no_attestations_until_after_fork(state, fork_epoch, spe
])
# irregular state transition to handle fork:
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition but add attestations

View File

@ -1,5 +1,7 @@
import pytest
from copy import deepcopy
from dataclasses import dataclass
from eth2spec.phase0 import mainnet as spec_phase0_mainnet, minimal as spec_phase0_minimal
from eth2spec.altair import mainnet as spec_altair_mainnet, minimal as spec_altair_minimal
from eth2spec.merge import mainnet as spec_merge_mainnet, minimal as spec_merge_minimal
@ -7,12 +9,16 @@ from eth2spec.utils import bls
from .exceptions import SkippedTest
from .helpers.constants import (
SpecForkName, PresetBaseName,
PHASE0, ALTAIR, MERGE, MINIMAL, MAINNET,
ALL_PHASES, FORKS_BEFORE_ALTAIR, FORKS_BEFORE_MERGE,
ALL_FORK_UPGRADES,
)
from .helpers.typing import SpecForkName, PresetBaseName
from .helpers.genesis import create_genesis_state
from .utils import vector_test, with_meta_tags, build_transition_test
from .utils import (
vector_test,
with_meta_tags,
)
from random import Random
from typing import Any, Callable, Sequence, TypedDict, Protocol, Dict
@ -50,6 +56,13 @@ class SpecMerge(Spec):
...
@dataclass(frozen=True)
class ForkMeta:
pre_fork_name: str
post_fork_name: str
fork_epoch: int
spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
MINIMAL: {
PHASE0: spec_phase0_minimal,
@ -86,7 +99,6 @@ _custom_state_cache_dict = LRU(size=10)
def with_custom_state(balances_fn: Callable[[Any], Sequence[int]],
threshold_fn: Callable[[Any], int]):
def deco(fn):
def entry(*args, spec: Spec, phases: SpecForks, **kw):
# make a key for the state, unique to the fork + config (incl preset choice) and balances/activations
key = (spec.fork, spec.config.__hash__(), spec.__file__, balances_fn, threshold_fn)
@ -104,7 +116,7 @@ def with_custom_state(balances_fn: Callable[[Any], Sequence[int]],
return deco
def default_activation_threshold(spec):
def default_activation_threshold(spec: Spec):
"""
Helper method to use the default balance activation threshold for state creation for tests.
Usage: `@with_custom_state(threshold_fn=default_activation_threshold, ...)`
@ -112,7 +124,7 @@ def default_activation_threshold(spec):
return spec.MAX_EFFECTIVE_BALANCE
def zero_activation_threshold(spec):
def zero_activation_threshold(spec: Spec):
"""
Helper method to use 0 gwei as the activation threshold for state creation for tests.
Usage: `@with_custom_state(threshold_fn=zero_activation_threshold, ...)`
@ -120,7 +132,7 @@ def zero_activation_threshold(spec):
return 0
def default_balances(spec):
def default_balances(spec: Spec):
"""
Helper method to create a series of default balances.
Usage: `@with_custom_state(balances_fn=default_balances, ...)`
@ -129,7 +141,7 @@ def default_balances(spec):
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
def scaled_churn_balances(spec):
def scaled_churn_balances(spec: Spec):
"""
Helper method to create enough validators to scale the churn limit.
(This is *firmly* over the churn limit -- thus the +2 instead of just +1)
@ -143,7 +155,7 @@ def scaled_churn_balances(spec):
with_state = with_custom_state(default_balances, default_activation_threshold)
def low_balances(spec):
def low_balances(spec: Spec):
"""
Helper method to create a series of low balances.
Usage: `@with_custom_state(balances_fn=low_balances, ...)`
@ -154,7 +166,7 @@ def low_balances(spec):
return [low_balance] * num_validators
def misc_balances(spec):
def misc_balances(spec: Spec):
"""
Helper method to create a series of balances that includes some misc. balances.
Usage: `@with_custom_state(balances_fn=misc_balances, ...)`
@ -166,7 +178,7 @@ def misc_balances(spec):
return balances
def misc_balances_in_default_range_with_many_validators(spec):
def misc_balances_in_default_range_with_many_validators(spec: Spec):
"""
Helper method to create a series of balances that includes some misc. balances but
none that are below the ``EJECTION_BALANCE``.
@ -182,7 +194,7 @@ def misc_balances_in_default_range_with_many_validators(spec):
return balances
def low_single_balance(spec):
def low_single_balance(spec: Spec):
"""
Helper method to create a single of balance of 1 Gwei.
Usage: `@with_custom_state(balances_fn=low_single_balance, ...)`
@ -190,7 +202,7 @@ def low_single_balance(spec):
return [1]
def large_validator_set(spec):
def large_validator_set(spec: Spec):
"""
Helper method to create a large series of default balances.
Usage: `@with_custom_state(balances_fn=default_balances, ...)`
@ -347,6 +359,66 @@ def with_all_phases_except(exclusion_phases):
return decorator
def _get_preset_targets(kw):
preset_name = DEFAULT_TEST_PRESET
if 'preset' in kw:
preset_name = kw.pop('preset')
return spec_targets[preset_name]
def _get_run_phases(phases, kw):
"""
Return the fork names for the base `spec` in test cases
"""
if 'phase' in kw:
# Limit phases if one explicitly specified
phase = kw.pop('phase')
if phase not in phases:
dump_skipping_message(f"doesn't support this fork: {phase}")
return None
run_phases = [phase]
else:
# If pytest `--fork` flag is set, filter out the rest of the forks
run_phases = set(phases).intersection(DEFAULT_PYTEST_FORKS)
return run_phases
def _get_available_phases(run_phases, other_phases):
"""
Return the available fork names for multi-phase tests
"""
available_phases = set(run_phases)
if other_phases is not None:
available_phases |= set(other_phases)
return available_phases
def _run_test_case_with_phases(fn, phases, other_phases, kw, args, is_fork_transition=False):
run_phases = _get_run_phases(phases, kw)
if len(run_phases) == 0:
if not is_fork_transition:
dump_skipping_message("none of the recognized phases are executable, skipping test.")
return None
available_phases = _get_available_phases(run_phases, other_phases)
targets = _get_preset_targets(kw)
# Populate all phases for multi-phase tests
phase_dir = {}
for phase in available_phases:
phase_dir[phase] = targets[phase]
# Return is ignored whenever multiple phases are ran.
# This return is for test generators to emit python generators (yielding test vector outputs)
for phase in run_phases:
ret = fn(spec=targets[phase], phases=phase_dir, *args, **kw)
return ret
def with_phases(phases, other_phases=None):
"""
Decorator factory that returns a decorator that runs a test for the appropriate phases.
@ -354,49 +426,22 @@ def with_phases(phases, other_phases=None):
"""
def decorator(fn):
def wrapper(*args, **kw):
run_phases = set(phases).intersection(DEFAULT_PYTEST_FORKS)
# limit phases if one explicitly specified
if 'phase' in kw:
phase = kw.pop('phase')
if phase not in phases:
dump_skipping_message(f"doesn't support this fork: {phase}")
return None
run_phases = [phase]
if PHASE0 not in run_phases and ALTAIR not in run_phases and MERGE not in run_phases:
dump_skipping_message("none of the recognized phases are executable, skipping test.")
return None
available_phases = set(run_phases)
if other_phases is not None:
available_phases |= set(other_phases)
preset_name = DEFAULT_TEST_PRESET
if 'preset' in kw:
preset_name = kw.pop('preset')
targets = spec_targets[preset_name]
# Populate all phases for multi-phase tests
phase_dir = {}
if PHASE0 in available_phases:
phase_dir[PHASE0] = targets[PHASE0]
if ALTAIR in available_phases:
phase_dir[ALTAIR] = targets[ALTAIR]
if MERGE in available_phases:
phase_dir[MERGE] = targets[MERGE]
# return is ignored whenever multiple phases are ran.
# This return is for test generators to emit python generators (yielding test vector outputs)
if PHASE0 in run_phases:
ret = fn(spec=targets[PHASE0], phases=phase_dir, *args, **kw)
if ALTAIR in run_phases:
ret = fn(spec=targets[ALTAIR], phases=phase_dir, *args, **kw)
if MERGE in run_phases:
ret = fn(spec=targets[MERGE], phases=phase_dir, *args, **kw)
# TODO: merge, sharding, custody_game and das are not executable yet.
# Tests that specify these features will not run, and get ignored for these specific phases.
if 'fork_metas' in kw:
fork_metas = kw.pop('fork_metas')
if 'phase' in kw:
# When running test generator, it sets specific `phase`
phase = kw['phase']
_phases = [phase]
_other_phases = [ALL_FORK_UPGRADES[phase]]
ret = _run_test_case_with_phases(fn, _phases, _other_phases, kw, args, is_fork_transition=True)
else:
# When running pytest, go through `fork_metas` instead of using `phases`
for fork_meta in fork_metas:
_phases = [fork_meta.pre_fork_name]
_other_phases = [fork_meta.post_fork_name]
ret = _run_test_case_with_phases(fn, _phases, _other_phases, kw, args, is_fork_transition=True)
else:
ret = _run_test_case_with_phases(fn, phases, other_phases, kw, args)
return ret
return wrapper
return decorator
@ -481,10 +526,25 @@ def only_generator(reason):
return _decorator
def fork_transition_test(pre_fork_name, post_fork_name, fork_epoch=None):
#
# Fork transition state tests
#
def set_fork_metas(fork_metas: Sequence[ForkMeta]):
def decorator(fn):
def wrapper(*args, **kwargs):
return fn(*args, fork_metas=fork_metas, **kwargs)
return wrapper
return decorator
def with_fork_metas(fork_metas: Sequence[ForkMeta]):
"""
A decorator to construct a "transition" test from one fork of the eth2 spec
to another.
A decorator to construct a "transition" test from one fork to another.
Decorator takes a list of `ForkMeta` and each item defines `pre_fork_name`,
`post_fork_name`, and `fork_epoch`.
Decorator assumes a transition from the `pre_fork_name` fork to the
`post_fork_name` fork. The user can supply a `fork_epoch` at which the
@ -502,15 +562,65 @@ def fork_transition_test(pre_fork_name, post_fork_name, fork_epoch=None):
`post_tag`: a function to tag data as belonging to `post_fork_name` fork.
Used to discriminate data during consumption of the generated spec tests.
"""
def _wrapper(fn):
@with_phases([pre_fork_name], other_phases=[post_fork_name])
@spec_test
@with_state
def _adapter(*args, **kwargs):
wrapped = build_transition_test(fn,
pre_fork_name,
post_fork_name,
fork_epoch=fork_epoch)
return wrapped(*args, **kwargs)
return _adapter
return _wrapper
run_yield_fork_meta = yield_fork_meta(fork_metas)
run_with_phases = with_phases(ALL_PHASES)
run_set_fork_metas = set_fork_metas(fork_metas)
def decorator(fn):
return run_set_fork_metas(run_with_phases(spec_test(with_state(run_yield_fork_meta(fn)))))
return decorator
def yield_fork_meta(fork_metas: Sequence[ForkMeta]):
"""
Yield meta fields to `meta.yaml` and pass post spec and meta fields to `fn`.
"""
def decorator(fn):
def wrapper(*args, **kw):
phases = kw.pop('phases')
spec = kw["spec"]
try:
fork_meta = next(filter(lambda m: m.pre_fork_name == spec.fork, fork_metas))
except StopIteration:
dump_skipping_message(f"doesn't support this fork: {spec.fork}")
post_spec = phases[fork_meta.post_fork_name]
# Reset counter
pre_fork_counter = 0
def pre_tag(obj):
nonlocal pre_fork_counter
pre_fork_counter += 1
return obj
def post_tag(obj):
return obj
yield "post_fork", "meta", fork_meta.post_fork_name
has_fork_epoch = False
if fork_meta.fork_epoch:
kw["fork_epoch"] = fork_meta.fork_epoch
has_fork_epoch = True
yield "fork_epoch", "meta", fork_meta.fork_epoch
result = fn(
*args,
post_spec=post_spec,
pre_tag=pre_tag,
post_tag=post_tag,
**kw,
)
if result is not None:
for part in result:
if part[0] == "fork_epoch":
has_fork_epoch = True
yield part
assert has_fork_epoch
if pre_fork_counter > 0:
yield "fork_block", "meta", pre_fork_counter - 1
return wrapper
return decorator

View File

@ -21,6 +21,14 @@ TESTGEN_FORKS = (PHASE0, ALTAIR, MERGE)
FORKS_BEFORE_ALTAIR = (PHASE0,)
FORKS_BEFORE_MERGE = (PHASE0, ALTAIR)
ALL_FORK_UPGRADES = {
# pre_fork_name: post_fork_name
PHASE0: ALTAIR,
ALTAIR: MERGE,
}
ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items()
AFTER_MERGE_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key not in FORKS_BEFORE_ALTAIR}
AFTER_MERGE_PRE_POST_FORKS = AFTER_MERGE_UPGRADES.items()
#
# Config

View File

@ -9,6 +9,10 @@ from eth2spec.test.helpers.block import (
build_empty_block,
sign_block,
)
from eth2spec.test.helpers.constants import (
ALTAIR,
MERGE,
)
from eth2spec.test.helpers.deposits import (
prepare_state_and_deposit,
)
@ -133,17 +137,25 @@ def state_transition_across_slots_with_ignoring_proposers(spec,
next_slot(spec, state)
def do_altair_fork(state, spec, post_spec, fork_epoch, with_block=True, operation_dict=None):
def do_fork(state, spec, post_spec, fork_epoch, with_block=True, operation_dict=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)
if post_spec.fork == ALTAIR:
state = post_spec.upgrade_to_altair(state)
elif post_spec.fork == MERGE:
state = post_spec.upgrade_to_merge(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 post_spec.fork == ALTAIR:
assert state.fork.previous_version == post_spec.config.GENESIS_FORK_VERSION
assert state.fork.current_version == post_spec.config.ALTAIR_FORK_VERSION
elif post_spec.fork == MERGE:
assert state.fork.previous_version == post_spec.config.ALTAIR_FORK_VERSION
assert state.fork.current_version == post_spec.config.MERGE_FORK_VERSION
if with_block:
return state, _state_transition_and_sign_block_at_slot(post_spec, state, operation_dict=operation_dict)
@ -280,7 +292,7 @@ def run_transition_with_operation(state,
# irregular state transition to handle fork:
_operation_at_slot = operation_dict if is_at_fork else None
state, block = do_altair_fork(state, spec, post_spec, fork_epoch, operation_dict=_operation_at_slot)
state, block = do_fork(state, spec, post_spec, fork_epoch, operation_dict=_operation_at_slot)
blocks.append(post_tag(block))
if is_at_fork:

View File

@ -0,0 +1,35 @@
from eth2spec.test.context import (
ForkMeta,
with_fork_metas,
)
from eth2spec.test.helpers.constants import (
AFTER_MERGE_PRE_POST_FORKS,
)
from eth2spec.test.helpers.fork_transition import (
do_fork,
transition_to_next_epoch_and_append_blocks,
transition_until_fork,
)
@with_fork_metas([
ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in AFTER_MERGE_PRE_POST_FORKS
])
def test_sample_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
transition_until_fork(spec, state, fork_epoch)
# check pre state
assert spec.get_current_epoch(state) < fork_epoch
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)
yield "blocks", blocks
yield "post", state

View File

@ -1,12 +1,10 @@
from .utils import (
vector_test,
with_meta_tags,
build_transition_test,
)
__all__ = [ # avoid "unused import" lint error
"vector_test",
"with_meta_tags",
"build_transition_test",
]

View File

@ -1,4 +1,3 @@
import inspect
from typing import Dict, Any
from eth2spec.utils.ssz.ssz_typing import View
from eth2spec.utils.ssz.ssz_impl import serialize
@ -94,50 +93,3 @@ def with_meta_tags(tags: Dict[str, Any]):
yield k, 'meta', v
return entry
return runner
def build_transition_test(fn, pre_fork_name, post_fork_name, fork_epoch=None):
"""
Handles the inner plumbing to generate `transition_test`s.
See that decorator in `context.py` for more information.
"""
def _adapter(*args, **kwargs):
post_spec = kwargs["phases"][post_fork_name]
pre_fork_counter = 0
def pre_tag(obj):
nonlocal pre_fork_counter
pre_fork_counter += 1
return obj
def post_tag(obj):
return obj
yield "post_fork", "meta", post_fork_name
has_fork_epoch = False
if fork_epoch:
kwargs["fork_epoch"] = fork_epoch
has_fork_epoch = True
yield "fork_epoch", "meta", fork_epoch
# massage args to handle an optional custom state using
# `with_custom_state` decorator
expected_args = inspect.getfullargspec(fn)
if "phases" not in expected_args.kwonlyargs:
kwargs.pop("phases", None)
for part in fn(*args,
post_spec=post_spec,
pre_tag=pre_tag,
post_tag=post_tag,
**kwargs):
if part[0] == "fork_epoch":
has_fork_epoch = True
yield part
assert has_fork_epoch
if pre_fork_counter > 0:
yield "fork_block", "meta", pre_fork_counter - 1
return _adapter

View File

@ -1,6 +1,14 @@
from typing import Iterable
from eth2spec.test.helpers.constants import ALTAIR, MINIMAL, MAINNET, PHASE0
from eth2spec.test.helpers.constants import (
MINIMAL,
MAINNET,
ALL_PRE_POST_FORKS,
)
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
from eth2spec.gen_helpers.gen_from_tests.gen import (
generate_from_tests,
)
from eth2spec.test.altair.transition import (
test_transition as test_altair_transition,
test_activations_and_exits as test_altair_activations_and_exits,
@ -8,9 +16,9 @@ from eth2spec.test.altair.transition import (
test_slashing as test_altair_slashing,
test_operations as test_altair_operations,
)
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
from eth2spec.gen_helpers.gen_from_tests.gen import generate_from_tests
from eth2spec.test.merge.transition import (
test_transition as test_merge_transition,
)
def create_provider(tests_src, preset_name: str, pre_fork_name: str, post_fork_name: str) -> gen_typing.TestProvider:
@ -31,18 +39,21 @@ 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),
(PHASE0, ALTAIR, test_altair_activations_and_exits),
(PHASE0, ALTAIR, test_altair_leaking),
(PHASE0, ALTAIR, test_altair_slashing),
(PHASE0, ALTAIR, test_altair_operations),
)
if __name__ == "__main__":
for pre_fork, post_fork, transition_test_module in TRANSITION_TESTS:
gen_runner.run_generator("transition", [
create_provider(transition_test_module, MINIMAL, pre_fork, post_fork),
create_provider(transition_test_module, MAINNET, pre_fork, post_fork),
])
altair_tests = (
test_altair_transition,
test_altair_activations_and_exits,
test_altair_leaking,
test_altair_slashing,
test_altair_operations,
)
merge_tests = (
test_merge_transition,
)
all_tests = altair_tests + merge_tests
for transition_test_module in all_tests:
for pre_fork, post_fork in ALL_PRE_POST_FORKS:
gen_runner.run_generator("transition", [
create_provider(transition_test_module, MINIMAL, pre_fork, post_fork),
create_provider(transition_test_module, MAINNET, pre_fork, post_fork),
])