Skip to content

Commit 68bcc19

Browse files
authored
Merge pull request ethereum#1957 from ethereum/testgenphase1
Enable test generation for phase1
2 parents a109105 + 87220f8 commit 68bcc19

36 files changed

+396
-267
lines changed

.circleci/config.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,11 @@ jobs:
130130
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
131131
- restore_pyspec_cached_venv
132132
- run:
133-
name: Run linter
133+
name: Run linter for pyspec
134134
command: make lint
135+
- run:
136+
name: Run linter for test generators
137+
command: make lint_generators
135138
build_deposit_contract:
136139
docker:
137140
- image: ethereum/solc:0.6.11-alpine

Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
SPEC_DIR = ./specs
22
SSZ_DIR = ./ssz
33
TEST_LIBS_DIR = ./tests/core
4+
TEST_GENERATORS_DIR = ./tests/generators
45
PY_SPEC_DIR = $(TEST_LIBS_DIR)/pyspec
56
TEST_VECTOR_DIR = ../eth2.0-spec-tests/tests
67
GENERATOR_DIR = ./tests/generators
@@ -113,6 +114,10 @@ lint: pyspec
113114
flake8 --config $(LINTER_CONFIG_FILE) ./eth2spec \
114115
&& mypy --config-file $(LINTER_CONFIG_FILE) -p eth2spec.phase0 -p eth2spec.phase1
115116

117+
lint_generators: pyspec
118+
. venv/bin/activate; cd $(TEST_GENERATORS_DIR); \
119+
flake8 --config $(LINTER_CONFIG_FILE)
120+
116121
compile_deposit_contract:
117122
@cd $(SOLIDITY_DEPOSIT_CONTRACT_DIR)
118123
@git submodule update --recursive --init

configs/mainnet/phase0.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Note: the intention of this file (for now) is to illustrate what a mainnet configuration could look like.
33
# Some of these constants may still change before the launch of Phase 0.
44

5+
CONFIG_NAME: "mainnet"
56

67
# Misc
78
# ---------------------------------------------------------------

configs/mainnet/phase1.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Mainnet preset - phase 1
22

3+
CONFIG_NAME: "mainnet"
34

45
# phase1-fork
56
# ---------------------------------------------------------------

configs/minimal/phase0.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Minimal preset
22

3+
CONFIG_NAME: "minimal"
34

45
# Misc
56
# ---------------------------------------------------------------

configs/minimal/phase1.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Minimal preset - phase 1
22

3+
CONFIG_NAME: "minimal"
34

45
# phase1-fork
56
# ---------------------------------------------------------------

setup.py

+4
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ def get_spec(file_name: str) -> SpecObject:
119119
from eth2spec.utils.hash_function import hash
120120
121121
SSZObject = TypeVar('SSZObject', bound=View)
122+
123+
CONFIG_NAME = 'mainnet'
122124
'''
123125
PHASE1_IMPORTS = '''from eth2spec.phase0 import spec as phase0
124126
from eth2spec.config.config_util import apply_constants_config
@@ -151,6 +153,8 @@ def get_spec(file_name: str) -> SpecObject:
151153
SSZVariableName = str
152154
GeneralizedIndex = NewType('GeneralizedIndex', int)
153155
SSZObject = TypeVar('SSZObject', bound=View)
156+
157+
CONFIG_NAME = 'mainnet'
154158
'''
155159
SUNDRY_CONSTANTS_FUNCTIONS = '''
156160
def ceillog2(x: int) -> uint64:

tests/core/gen_helpers/gen_base/gen_runner.py

+23-8
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22
from pathlib import Path
33
import sys
44
from typing import Iterable, AnyStr, Any, Callable
5+
import traceback
56

67
from ruamel.yaml import (
78
YAML,
89
)
910

1011
from gen_base.gen_typing import TestProvider
1112

13+
from eth2spec.test import context
14+
from eth2spec.test.exceptions import SkippedTest
15+
16+
17+
# Flag that the runner does NOT run test via pytest
18+
context.is_pytest = False
19+
1220

1321
def validate_output_dir(path_str):
1422
path = Path(path_str)
@@ -134,14 +142,20 @@ def output_part(out_kind: str, name: str, fn: Callable[[Path, ], None]):
134142

135143
written_part = False
136144
meta = dict()
137-
for (name, out_kind, data) in test_case.case_fn():
138-
written_part = True
139-
if out_kind == "meta":
140-
meta[name] = data
141-
if out_kind == "data":
142-
output_part("data", name, dump_yaml_fn(data, name, file_mode, yaml))
143-
if out_kind == "ssz":
144-
output_part("ssz", name, dump_ssz_fn(data, name, file_mode))
145+
146+
try:
147+
for (name, out_kind, data) in test_case.case_fn():
148+
written_part = True
149+
if out_kind == "meta":
150+
meta[name] = data
151+
if out_kind == "data":
152+
output_part("data", name, dump_yaml_fn(data, name, file_mode, yaml))
153+
if out_kind == "ssz":
154+
output_part("ssz", name, dump_ssz_fn(data, name, file_mode))
155+
except SkippedTest as e:
156+
print(e)
157+
continue
158+
145159
# Once all meta data is collected (if any), write it to a meta data file.
146160
if len(meta) != 0:
147161
written_part = True
@@ -152,6 +166,7 @@ def output_part(out_kind: str, name: str, fn: Callable[[Path, ], None]):
152166

153167
except Exception as e:
154168
print(f"ERROR: failed to generate vector(s) for test {case_dir}: {e}")
169+
traceback.print_exc()
155170
print(f"completed {generator_name}")
156171

157172

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
ruamel.yaml==0.16.5
22
eth-utils==1.6.0
3+
pytest>=4.4

tests/core/gen_helpers/setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
packages=['gen_base', 'gen_from_tests'],
66
install_requires=[
77
"ruamel.yaml==0.16.5",
8-
"eth-utils==1.6.0"
8+
"eth-utils==1.6.0",
9+
"pytest>=4.4",
910
]
1011
)

tests/core/pyspec/eth2spec/config/config_util.py

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ def load_config_file(configs_dir: str, presets_name: str) -> Dict[str, Any]:
5454
out[k] = [int(item) if item.isdigit() else item for item in v]
5555
elif isinstance(v, str) and v.startswith("0x"):
5656
out[k] = bytes.fromhex(v[2:])
57+
elif k == "CONFIG_NAME":
58+
out[k] = str(v)
5759
else:
5860
out[k] = int(v)
5961
return out

tests/core/pyspec/eth2spec/debug/random_value.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,8 @@ def get_random_ssz_object(rng: Random,
6868
else:
6969
return typ(get_random_bytes_list(rng, rng.randint(0, min(max_bytes_length, typ.limit()))))
7070
if issubclass(typ, ByteVector):
71-
# Sanity, don't generate absurdly big random values
72-
# If a client is aiming to performance-test, they should create a benchmark suite.
73-
assert typ.type_byte_length() <= max_bytes_length
71+
# Random byte vectors can be bigger than max bytes size, e.g. custody chunk data.
72+
# No max-bytes-length limitation here.
7473
if mode == RandomizationMode.mode_zero:
7574
return typ(b'\x00' * typ.type_byte_length())
7675
elif mode == RandomizationMode.mode_max:

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

+70-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import pytest
2+
13
from eth2spec.phase0 import spec as spec_phase0
24
from eth2spec.phase1 import spec as spec_phase1
35
from eth2spec.utils import bls
46

7+
from .exceptions import SkippedTest
58
from .helpers.genesis import create_genesis_state
6-
79
from .utils import vector_test, with_meta_tags
810

911
from random import Random
@@ -22,11 +24,16 @@ def reload_specs():
2224
# Some of the Spec module functionality is exposed here to deal with phase-specific changes.
2325

2426
SpecForkName = NewType("SpecForkName", str)
27+
ConfigName = NewType("ConfigName", str)
2528

2629
PHASE0 = SpecForkName('phase0')
2730
PHASE1 = SpecForkName('phase1')
2831
ALL_PHASES = (PHASE0, PHASE1)
2932

33+
MAINNET = ConfigName('mainnet')
34+
MINIMAL = ConfigName('minimal')
35+
36+
3037
# TODO: currently phases are defined as python modules.
3138
# It would be better if they would be more well-defined interfaces for stronger typing.
3239

@@ -153,7 +160,7 @@ def low_single_balance(spec):
153160

154161
def large_validator_set(spec):
155162
"""
156-
Helper method to create a series of default balances.
163+
Helper method to create a large series of default balances.
157164
Usage: `@with_custom_state(balances_fn=default_balances, ...)`
158165
"""
159166
num_validators = 2 * spec.SLOTS_PER_EPOCH * spec.MAX_COMMITTEES_PER_SLOT * spec.TARGET_COMMITTEE_SIZE
@@ -184,6 +191,17 @@ def entry(*args, **kw):
184191
DEFAULT_BLS_ACTIVE = True
185192

186193

194+
is_pytest = True
195+
196+
197+
def dump_skipping_message(reason: str) -> None:
198+
message = f"[Skipped test] {reason}"
199+
if is_pytest:
200+
pytest.skip(message)
201+
else:
202+
raise SkippedTest(message)
203+
204+
187205
def spec_test(fn):
188206
# Bls switch must be wrapped by vector_test,
189207
# to fully go through the yielded bls switch data, before setting back the BLS setting.
@@ -255,6 +273,24 @@ def entry(*args, **kw):
255273
return entry
256274

257275

276+
def disable_process_reveal_deadlines(fn):
277+
"""
278+
Decorator to make a function execute with `process_reveal_deadlines` OFF.
279+
This is for testing long-range epochs transition without considering the reveal-deadline slashing effect.
280+
"""
281+
def entry(*args, spec: Spec, **kw):
282+
if hasattr(spec, 'process_reveal_deadlines'):
283+
old_state = spec.process_reveal_deadlines
284+
spec.process_reveal_deadlines = lambda state: None
285+
286+
yield from fn(*args, spec=spec, **kw)
287+
288+
if hasattr(spec, 'process_reveal_deadlines'):
289+
spec.process_reveal_deadlines = old_state
290+
291+
return with_meta_tags({'reveal_deadlines_setting': 1})(entry)
292+
293+
258294
def with_all_phases(fn):
259295
"""
260296
A decorator for running a test with every phase
@@ -284,7 +320,8 @@ def wrapper(*args, **kw):
284320
if 'phase' in kw:
285321
phase = kw.pop('phase')
286322
if phase not in phases:
287-
return
323+
dump_skipping_message(f"doesn't support this fork: {phase}")
324+
return None
288325
run_phases = [phase]
289326

290327
available_phases = set(run_phases)
@@ -309,3 +346,33 @@ def wrapper(*args, **kw):
309346
return ret
310347
return wrapper
311348
return decorator
349+
350+
351+
def with_configs(configs, reason=None):
352+
def decorator(fn):
353+
def wrapper(*args, spec: Spec, **kw):
354+
available_configs = set(configs)
355+
if spec.CONFIG_NAME not in available_configs:
356+
message = f"doesn't support this config: {spec.CONFIG_NAME}."
357+
if reason is not None:
358+
message = f"{message} Reason: {reason}"
359+
dump_skipping_message(message)
360+
return None
361+
362+
return fn(*args, spec=spec, **kw)
363+
return wrapper
364+
return decorator
365+
366+
367+
def only_full_crosslink(fn):
368+
def is_full_crosslink(spec, state):
369+
epoch = spec.compute_epoch_at_slot(state.slot)
370+
return spec.get_committee_count_per_slot(state, epoch) >= spec.get_active_shard_count(state)
371+
372+
def wrapper(*args, spec: Spec, state: Any, **kw):
373+
# TODO: update condition to "phase1+" if we have phase2
374+
if spec.fork == PHASE1 and not is_full_crosslink(spec, state):
375+
dump_skipping_message("only for full crosslink")
376+
return None
377+
return fn(*args, spec=spec, state=state, **kw)
378+
return wrapper
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class SkippedTest(Exception):
2+
...

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

-9
Original file line numberDiff line numberDiff line change
@@ -179,15 +179,6 @@ def get_sample_shard_transition(spec, start_slot, block_lengths):
179179
return shard_transition
180180

181181

182-
def get_custody_secret(spec, state, validator_index, epoch=None):
183-
period = spec.get_custody_period_for_validator(validator_index, epoch if epoch is not None
184-
else spec.get_current_epoch(state))
185-
epoch_to_sign = spec.get_randao_epoch_for_custody_period(period, validator_index)
186-
domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch_to_sign)
187-
signing_root = spec.compute_signing_root(spec.Epoch(epoch_to_sign), domain)
188-
return bls.Sign(privkeys[validator_index], signing_root)
189-
190-
191182
def get_custody_slashable_test_vector(spec, custody_secret, length, slashable=True):
192183
test_vector = get_custody_test_vector(length)
193184
offset = 0

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

-5
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,3 @@ def get_shard_transition_of_committee(spec, state, committee_index, shard_blocks
3535
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
3636
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=shard_blocks)
3737
return shard_transition
38-
39-
40-
def is_full_crosslink(spec, state):
41-
epoch = spec.compute_epoch_at_slot(state.slot)
42-
return spec.get_committee_count_per_slot(state, epoch) >= spec.get_active_shard_count(state)

tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py

-3
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,6 @@ def test_participants_already_slashed(spec, state):
193193
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
194194

195195

196-
# Some of the following tests are phase0 only: phase 1 lists participants with bitfields instead of index list.
197-
198-
199196
@with_all_phases
200197
@spec_state_test
201198
@always_bls

0 commit comments

Comments
 (0)