Skip to content

Commit 877817c

Browse files
authored
Merge pull request #3562 from ethereum/refactor-context
Refactor pyspec to reduce some hardcoded spec fork names
2 parents bf09b9a + 9fbf751 commit 877817c

File tree

10 files changed

+135
-226
lines changed

10 files changed

+135
-226
lines changed

Makefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \
3434
$(wildcard $(SPEC_DIR)/_features/*/*/*.md) \
3535
$(wildcard $(SSZ_DIR)/*.md)
3636

37-
ALL_EXECUTABLE_SPECS = phase0 altair bellatrix capella deneb eip6110 whisk
37+
ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb eip6110 eip7002 whisk
3838
# The parameters for commands. Use `foreach` to avoid listing specs again.
39-
COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPECS), --cov=eth2spec.$S.$(TEST_PRESET_TYPE))
40-
PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPECS), ./eth2spec/$S)
41-
MYPY_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPECS), -p eth2spec.$S)
39+
COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE))
40+
PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S)
41+
MYPY_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), -p eth2spec.$S)
4242

4343
COV_HTML_OUT=.htmlcov
4444
COV_HTML_OUT_DIR=$(PY_SPEC_DIR)/$(COV_HTML_OUT)
@@ -74,7 +74,7 @@ partial_clean:
7474
rm -rf $(TEST_REPORT_DIR)
7575
rm -rf eth2spec.egg-info dist build
7676
rm -rf build;
77-
@for spec_name in $(ALL_EXECUTABLE_SPECS) ; do \
77+
@for spec_name in $(ALL_EXECUTABLE_SPEC_NAMES) ; do \
7878
echo $$spec_name; \
7979
rm -rf $(ETH2SPEC_MODULE_DIR)/$$spec_name; \
8080
done

docs/docs/new-feature.md

Lines changed: 6 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@
1111
- [4. Add `fork.md`](#4-add-forkmd)
1212
- [5. Make it executable](#5-make-it-executable)
1313
- [B: Make it executable for pytest and test generator](#b-make-it-executable-for-pytest-and-test-generator)
14-
- [1. Add `light-client/*` docs if you updated the content of `BeaconBlock`](#1-add-light-client-docs-if-you-updated-the-content-of-beaconblock)
14+
- [1. [Optional] Add `light-client/*` docs if you updated the content of `BeaconBlock`](#1-optional-add-light-client-docs-if-you-updated-the-content-of-beaconblock)
1515
- [2. Add the mainnet and minimal presets and update the configs](#2-add-the-mainnet-and-minimal-presets-and-update-the-configs)
1616
- [3. Update `context.py`](#3-update-contextpy)
1717
- [4. Update `constants.py`](#4-update-constantspy)
1818
- [5. Update `genesis.py`:](#5-update-genesispy)
19-
- [6. To add fork transition tests, update fork_transition.py](#6-to-add-fork-transition-tests-update-fork_transitionpy)
20-
- [7. Update CI configurations](#7-update-ci-configurations)
19+
- [6. Update CI configurations](#6-update-ci-configurations)
2120
- [Others](#others)
2221
- [Bonus](#bonus)
2322
- [Need help?](#need-help)
@@ -58,6 +57,8 @@ You can refer to the previous fork's `fork.md` file.
5857
- Update [`pysetup/constants.py`](https://github.com/ethereum/consensus-specs/blob/dev/constants.py) with the new feature name as Pyspec `constants.py` defined.
5958
- Update [`pysetup/spec_builders/__init__.py`](https://github.com/ethereum/consensus-specs/blob/dev/pysetup/spec_builders/__init__.py). Implement a new `<FEATURE_NAME>SpecBuilder` in `pysetup/spec_builders/<FEATURE_NAME>.py` with the new feature name. e.g., `EIP9999SpecBuilder`. Append it to the `spec_builders` list.
6059
- Update [`pysetup/md_doc_paths.py`](https://github.com/ethereum/consensus-specs/blob/dev/pysetup/md_doc_paths.py): add the path of the new markdown files in `get_md_doc_paths` function if needed.
60+
- Update `PREVIOUS_FORK_OF` setting in both [`test/helpers/constants.py`](https://github.com/ethereum/consensus-specs/blob/dev/constants.py) and [`pysetup/md_doc_paths.py`](https://github.com/ethereum/consensus-specs/blob/dev/pysetup/md_doc_paths.py).
61+
- NOTE: since these two modules (the pyspec itself and the spec builder tool) must be separate, the fork sequence setting has to be defined again.
6162

6263
## B: Make it executable for pytest and test generator
6364

@@ -70,24 +71,7 @@ You can refer to the previous fork's `fork.md` file.
7071
- Update configs: `configs/mainnet.yaml` and `configs/minimal.yaml`
7172

7273
### 3. Update [`context.py`](https://github.com/ethereum/consensus-specs/blob/dev/tests/core/pyspec/eth2spec/test/context.py)
73-
- Update `spec_targets` by adding `<NEW_FEATURE>`
74-
75-
```python
76-
from eth2spec.eip9999 import mainnet as spec_eip9999_mainnet, minimal as spec_eip9999_minimal
77-
78-
...
79-
80-
spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
81-
MINIMAL: {
82-
...
83-
EIP9999: spec_eip9999_minimal,
84-
},
85-
MAINNET: {
86-
...
87-
EIP9999: spec_eip9999_mainnet
88-
},
89-
}
90-
```
74+
- [Optional] Add `with_<new-feature-name>_and_later` decorator for writing pytest cases. e.g., `with_capella_and_later`.
9175

9276
### 4. Update [`constants.py`](https://github.com/ethereum/consensus-specs/blob/dev/tests/core/pyspec/eth2spec/test/helpers/constants.py)
9377
- Add `<NEW_FEATURE>` to `ALL_PHASES` and `TESTGEN_FORKS`
@@ -96,20 +80,6 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
9680

9781
We use `create_genesis_state` to create the default `state` in tests.
9882

99-
- Update `create_genesis_state` by adding `fork_version` setting:
100-
101-
```python
102-
def create_genesis_state(spec, validator_balances, activation_threshold):
103-
...
104-
if spec.fork == ALTAIR:
105-
current_version = spec.config.ALTAIR_FORK_VERSION
106-
...
107-
elif spec.fork == EIP9999:
108-
# Add the previous fork version of given fork
109-
previous_version = spec.config.<PREVIOUS_FORK_VERSION>
110-
current_version = spec.config.EIP9999_FORK_VERSION
111-
```
112-
11383
- If the given feature changes `BeaconState` fields, you have to set the initial values by adding:
11484

11585
```python
@@ -123,32 +93,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
12393

12494
- If the given feature changes `ExecutionPayload` fields, you have to set the initial values by updating `get_sample_genesis_execution_payload_header` helper.
12595

126-
### 6. To add fork transition tests, update [fork_transition.py](https://github.com/ethereum/consensus-specs/blob/dev/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py)
127-
128-
```python
129-
def do_fork(state, spec, post_spec, fork_epoch, with_block=True, sync_aggregate=None, operation_dict=None):
130-
...
131-
132-
if post_spec.fork == ALTAIR:
133-
state = post_spec.upgrade_to_altair(state)
134-
...
135-
elif post_spec.fork == EIP9999:
136-
state = post_spec.upgrade_to_eip9999(state)
137-
138-
...
139-
140-
if post_spec.fork == ALTAIR:
141-
assert state.fork.previous_version == post_spec.config.GENESIS_FORK_VERSION
142-
assert state.fork.current_version == post_spec.config.ALTAIR_FORK_VERSION
143-
...
144-
elif post_spec.fork == EIP9999:
145-
assert state.fork.previous_version == post_spec.config.<PREVIOUS_FORK_VERSION>
146-
assert state.fork.current_version == post_spec.config.EIP9999_FORK_VERSION
147-
148-
...
149-
```
150-
151-
### 7. Update CI configurations
96+
### 6. Update CI configurations
15297
- Update [GitHub Actions config](https://github.com/ethereum/consensus-specs/blob/dev/.github/workflows/run-tests.yml)
15398
- Update `pyspec-tests.strategy.matrix.version` list by adding new feature to it
15499
- Update [CircleCI config](https://github.com/ethereum/consensus-specs/blob/dev/.circleci/config.yml)

tests/core/pyspec/eth2spec/test/context.py

Lines changed: 11 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,35 @@
33
from dataclasses import dataclass
44
import importlib
55

6-
from eth2spec.phase0 import mainnet as spec_phase0_mainnet, minimal as spec_phase0_minimal
7-
from eth2spec.altair import mainnet as spec_altair_mainnet, minimal as spec_altair_minimal
8-
from eth2spec.bellatrix import mainnet as spec_bellatrix_mainnet, minimal as spec_bellatrix_minimal
9-
from eth2spec.capella import mainnet as spec_capella_mainnet, minimal as spec_capella_minimal
10-
from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal
11-
from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal
12-
from eth2spec.whisk import mainnet as spec_whisk_mainnet, minimal as spec_whisk_minimal
13-
from eth2spec.eip7002 import mainnet as spec_eip7002_mainnet, minimal as spec_eip7002_minimal
146
from eth2spec.utils import bls
157

168
from .exceptions import SkippedTest
179
from .helpers.constants import (
1810
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
1911
EIP6110, EIP7002,
2012
WHISK,
21-
MINIMAL, MAINNET,
13+
MINIMAL,
2214
ALL_PHASES,
23-
ALL_FORK_UPGRADES,
15+
POST_FORK_OF,
2416
ALLOWED_TEST_RUNNER_FORKS,
2517
LIGHT_CLIENT_TESTING_FORKS,
2618
)
2719
from .helpers.forks import is_post_fork
28-
from .helpers.typing import SpecForkName, PresetBaseName
2920
from .helpers.genesis import create_genesis_state
21+
from .helpers.typing import (
22+
Spec,
23+
SpecForks,
24+
)
25+
from .helpers.specs import (
26+
spec_targets,
27+
)
3028
from .utils import (
3129
vector_test,
3230
with_meta_tags,
3331
)
3432

3533
from random import Random
36-
from typing import Any, Callable, Sequence, TypedDict, Protocol, Dict
34+
from typing import Any, Callable, Sequence, Dict
3735

3836
from lru import LRU
3937

@@ -44,72 +42,13 @@
4442
DEFAULT_PYTEST_FORKS = ALL_PHASES
4543

4644

47-
# TODO: currently phases are defined as python modules.
48-
# It would be better if they would be more well-defined interfaces for stronger typing.
49-
50-
class Configuration(Protocol):
51-
PRESET_BASE: str
52-
53-
54-
class Spec(Protocol):
55-
fork: str
56-
config: Configuration
57-
58-
59-
class SpecPhase0(Spec):
60-
...
61-
62-
63-
class SpecAltair(Spec):
64-
...
65-
66-
67-
class SpecBellatrix(Spec):
68-
...
69-
70-
71-
class SpecCapella(Spec):
72-
...
73-
74-
7545
@dataclass(frozen=True)
7646
class ForkMeta:
7747
pre_fork_name: str
7848
post_fork_name: str
7949
fork_epoch: int
8050

8151

82-
spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
83-
MINIMAL: {
84-
PHASE0: spec_phase0_minimal,
85-
ALTAIR: spec_altair_minimal,
86-
BELLATRIX: spec_bellatrix_minimal,
87-
CAPELLA: spec_capella_minimal,
88-
DENEB: spec_deneb_minimal,
89-
EIP6110: spec_eip6110_minimal,
90-
EIP7002: spec_eip7002_minimal,
91-
WHISK: spec_whisk_minimal,
92-
},
93-
MAINNET: {
94-
PHASE0: spec_phase0_mainnet,
95-
ALTAIR: spec_altair_mainnet,
96-
BELLATRIX: spec_bellatrix_mainnet,
97-
CAPELLA: spec_capella_mainnet,
98-
DENEB: spec_deneb_mainnet,
99-
EIP6110: spec_eip6110_mainnet,
100-
EIP7002: spec_eip7002_mainnet,
101-
WHISK: spec_whisk_mainnet,
102-
},
103-
}
104-
105-
106-
class SpecForks(TypedDict, total=False):
107-
PHASE0: SpecPhase0
108-
ALTAIR: SpecAltair
109-
BELLATRIX: SpecBellatrix
110-
CAPELLA: SpecCapella
111-
112-
11352
def _prepare_state(balances_fn: Callable[[Any], Sequence[int]], threshold_fn: Callable[[Any], int],
11453
spec: Spec, phases: SpecForks):
11554
balances = balances_fn(spec)
@@ -530,7 +469,7 @@ def wrapper(*args, **kw):
530469
# When running test generator, it sets specific `phase`
531470
phase = kw['phase']
532471
_phases = [phase]
533-
_other_phases = [ALL_FORK_UPGRADES[phase]]
472+
_other_phases = [POST_FORK_OF[phase]]
534473
ret = _run_test_case_with_phases(fn, _phases, _other_phases, kw, args, is_fork_transition=True)
535474
else:
536475
# When running pytest, go through `fork_metas` instead of using `phases`

tests/core/pyspec/eth2spec/test/helpers/constants.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,34 @@
4545
# Forks allowed in the test runner `--fork` flag, to fail fast in case of typos
4646
ALLOWED_TEST_RUNNER_FORKS = (*ALL_PHASES, WHISK)
4747

48-
ALL_FORK_UPGRADES = {
48+
# NOTE: the same definition as in `pysetup/md_doc_paths.py`
49+
PREVIOUS_FORK_OF = {
50+
# post_fork_name: pre_fork_name
51+
PHASE0: None,
52+
ALTAIR: PHASE0,
53+
BELLATRIX: ALTAIR,
54+
CAPELLA: BELLATRIX,
55+
DENEB: CAPELLA,
56+
# Experimental patches
57+
EIP6110: DENEB,
58+
WHISK: CAPELLA,
59+
EIP7002: CAPELLA,
60+
}
61+
62+
# For fork transition tests
63+
POST_FORK_OF = {
4964
# pre_fork_name: post_fork_name
5065
PHASE0: ALTAIR,
5166
ALTAIR: BELLATRIX,
5267
BELLATRIX: CAPELLA,
5368
CAPELLA: DENEB,
5469
DENEB: EIP6110,
5570
}
56-
ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items()
57-
AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key != PHASE0}
58-
AFTER_BELLATRIX_PRE_POST_FORKS = AFTER_BELLATRIX_UPGRADES.items()
59-
AFTER_CAPELLA_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items()
60-
if key not in [PHASE0, ALTAIR]}
61-
AFTER_CAPELLA_PRE_POST_FORKS = AFTER_CAPELLA_UPGRADES.items()
62-
AFTER_DENEB_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items()
63-
if key not in [PHASE0, ALTAIR, BELLATRIX]}
64-
AFTER_DENEB_PRE_POST_FORKS = AFTER_DENEB_UPGRADES.items()
71+
72+
ALL_PRE_POST_FORKS = POST_FORK_OF.items()
73+
DENEB_TRANSITION_UPGRADES_AND_AFTER = {key: value for key, value in POST_FORK_OF.items()
74+
if key not in [PHASE0, ALTAIR, BELLATRIX]}
75+
AFTER_DENEB_PRE_POST_FORKS = DENEB_TRANSITION_UPGRADES_AND_AFTER.items()
6576

6677
#
6778
# Config and Preset

tests/core/pyspec/eth2spec/test/helpers/fork_transition.py

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@
1111
)
1212
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
1313
from eth2spec.test.helpers.constants import (
14-
ALTAIR,
15-
BELLATRIX,
16-
CAPELLA,
17-
DENEB,
18-
EIP6110,
19-
EIP7002,
14+
PHASE0,
15+
POST_FORK_OF,
16+
PREVIOUS_FORK_OF,
2017
)
2118
from eth2spec.test.helpers.deposits import (
2219
prepare_state_and_deposit,
@@ -146,45 +143,37 @@ def state_transition_across_slots_with_ignoring_proposers(spec,
146143
next_slot(spec, state)
147144

148145

146+
def get_upgrade_fn(spec, fork):
147+
# pylint: disable=unused-argument
148+
# NOTE: `spec` is used for the `eval` call
149+
assert fork in POST_FORK_OF.values()
150+
try:
151+
# TODO: make all upgrade_to_* function names consistent?
152+
fn = eval(f"spec.upgrade_to_{fork}")
153+
return fn
154+
except Exception:
155+
raise ValueError(f"Unknown fork: {fork}")
156+
157+
149158
def do_fork(state, spec, post_spec, fork_epoch, with_block=True, sync_aggregate=None, operation_dict=None):
150159
spec.process_slots(state, state.slot + 1)
151160

152161
assert state.slot % spec.SLOTS_PER_EPOCH == 0
153162
assert spec.get_current_epoch(state) == fork_epoch
154163

155-
if post_spec.fork == ALTAIR:
156-
state = post_spec.upgrade_to_altair(state)
157-
elif post_spec.fork == BELLATRIX:
158-
state = post_spec.upgrade_to_bellatrix(state)
159-
elif post_spec.fork == CAPELLA:
160-
state = post_spec.upgrade_to_capella(state)
161-
elif post_spec.fork == DENEB:
162-
state = post_spec.upgrade_to_deneb(state)
163-
elif post_spec.fork == EIP6110:
164-
state = post_spec.upgrade_to_eip6110(state)
165-
elif post_spec.fork == EIP7002:
166-
state = post_spec.upgrade_to_eip7002(state)
164+
state = get_upgrade_fn(post_spec, post_spec.fork)(state)
167165

168166
assert state.fork.epoch == fork_epoch
169167

170-
if post_spec.fork == ALTAIR:
171-
assert state.fork.previous_version == post_spec.config.GENESIS_FORK_VERSION
172-
assert state.fork.current_version == post_spec.config.ALTAIR_FORK_VERSION
173-
elif post_spec.fork == BELLATRIX:
174-
assert state.fork.previous_version == post_spec.config.ALTAIR_FORK_VERSION
175-
assert state.fork.current_version == post_spec.config.BELLATRIX_FORK_VERSION
176-
elif post_spec.fork == CAPELLA:
177-
assert state.fork.previous_version == post_spec.config.BELLATRIX_FORK_VERSION
178-
assert state.fork.current_version == post_spec.config.CAPELLA_FORK_VERSION
179-
elif post_spec.fork == DENEB:
180-
assert state.fork.previous_version == post_spec.config.CAPELLA_FORK_VERSION
181-
assert state.fork.current_version == post_spec.config.DENEB_FORK_VERSION
182-
elif post_spec.fork == EIP6110:
183-
assert state.fork.previous_version == post_spec.config.DENEB_FORK_VERSION
184-
assert state.fork.current_version == post_spec.config.EIP6110_FORK_VERSION
185-
elif post_spec.fork == EIP7002:
186-
assert state.fork.previous_version == post_spec.config.CAPELLA_FORK_VERSION
187-
assert state.fork.current_version == post_spec.config.EIP7002_FORK_VERSION
168+
previous_fork = PREVIOUS_FORK_OF[post_spec.fork]
169+
if previous_fork == PHASE0:
170+
previous_version = spec.config.GENESIS_FORK_VERSION
171+
else:
172+
previous_version = getattr(post_spec.config, f"{previous_fork.upper()}_FORK_VERSION")
173+
current_version = getattr(post_spec.config, f"{post_spec.fork.upper()}_FORK_VERSION")
174+
175+
assert state.fork.previous_version == previous_version
176+
assert state.fork.current_version == current_version
188177

189178
if with_block:
190179
return state, _state_transition_and_sign_block_at_slot(

0 commit comments

Comments
 (0)