mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-02-21 23:08:12 +00:00
switch configuration to named tuple for reliable hashing, add test for config override functionality
This commit is contained in:
parent
e8b0c46138
commit
f5c647b47b
4
setup.py
4
setup.py
@ -326,7 +326,7 @@ from dataclasses import (
|
|||||||
field,
|
field,
|
||||||
)
|
)
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar
|
Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar, NamedTuple
|
||||||
)
|
)
|
||||||
|
|
||||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes
|
from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes
|
||||||
@ -603,7 +603,7 @@ def objects_to_spec(preset_name: str,
|
|||||||
out += f' # {vardef.comment}'
|
out += f' # {vardef.comment}'
|
||||||
return out
|
return out
|
||||||
|
|
||||||
config_spec = '@dataclass\nclass Configuration(object):\n'
|
config_spec = 'class Configuration(NamedTuple):\n'
|
||||||
config_spec += ' PRESET_BASE: str\n'
|
config_spec += ' PRESET_BASE: str\n'
|
||||||
config_spec += '\n'.join(f' {k}: {v.type_name if v.type_name is not None else "int"}'
|
config_spec += '\n'.join(f' {k}: {v.type_name if v.type_name is not None else "int"}'
|
||||||
for k, v in spec_object.config_vars.items())
|
for k, v in spec_object.config_vars.items())
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
from eth2spec.test.context import spec_configured_state_test, with_phases
|
||||||
|
from eth2spec.test.helpers.constants import ALTAIR
|
||||||
|
|
||||||
|
|
||||||
|
@with_phases([ALTAIR])
|
||||||
|
@spec_configured_state_test({
|
||||||
|
'GENESIS_FORK_VERSION': '0x12345678',
|
||||||
|
'ALTAIR_FORK_VERSION': '0x11111111',
|
||||||
|
'ALTAIR_FORK_EPOCH': 4
|
||||||
|
})
|
||||||
|
def test_config_override(spec, state):
|
||||||
|
assert spec.config.ALTAIR_FORK_EPOCH == 4
|
||||||
|
assert spec.config.GENESIS_FORK_VERSION != spec.Version('0x00000000')
|
||||||
|
assert spec.config.GENESIS_FORK_VERSION == spec.Version('0x12345678')
|
||||||
|
assert spec.config.ALTAIR_FORK_VERSION == spec.Version('0x11111111')
|
||||||
|
assert state.fork.current_version == spec.Version('0x11111111')
|
||||||
|
# TODO: it would be nice if the create_genesis_state actually outputs a state
|
||||||
|
# for the fork with a slot that matches at least the fork boundary.
|
||||||
|
# assert spec.get_current_epoch(state) >= 4
|
@ -23,9 +23,13 @@ from lru import LRU
|
|||||||
# TODO: currently phases are defined as python modules.
|
# TODO: currently phases are defined as python modules.
|
||||||
# It would be better if they would be more well-defined interfaces for stronger typing.
|
# It would be better if they would be more well-defined interfaces for stronger typing.
|
||||||
|
|
||||||
|
class Configuration(Protocol):
|
||||||
|
PRESET_BASE: str
|
||||||
|
|
||||||
|
|
||||||
class Spec(Protocol):
|
class Spec(Protocol):
|
||||||
version: str
|
fork: str
|
||||||
|
config: Configuration
|
||||||
|
|
||||||
|
|
||||||
class SpecPhase0(Spec):
|
class SpecPhase0(Spec):
|
||||||
@ -78,8 +82,8 @@ def with_custom_state(balances_fn: Callable[[Any], Sequence[int]],
|
|||||||
def deco(fn):
|
def deco(fn):
|
||||||
|
|
||||||
def entry(*args, spec: Spec, phases: SpecForks, **kw):
|
def entry(*args, spec: Spec, phases: SpecForks, **kw):
|
||||||
# make a key for the state
|
# make a key for the state, unique to the fork + config (incl preset choice) and balances/activations
|
||||||
key = (spec.fork, spec.config.PRESET_BASE, spec.__file__, balances_fn, threshold_fn)
|
key = (spec.fork, spec.config.__hash__(), spec.__file__, balances_fn, threshold_fn)
|
||||||
global _custom_state_cache_dict
|
global _custom_state_cache_dict
|
||||||
if key not in _custom_state_cache_dict:
|
if key not in _custom_state_cache_dict:
|
||||||
state = _prepare_state(balances_fn, threshold_fn, spec, phases)
|
state = _prepare_state(balances_fn, threshold_fn, spec, phases)
|
||||||
@ -211,6 +215,14 @@ def spec_state_test(fn):
|
|||||||
return spec_test(with_state(single_phase(fn)))
|
return spec_test(with_state(single_phase(fn)))
|
||||||
|
|
||||||
|
|
||||||
|
def spec_configured_state_test(conf):
|
||||||
|
overrides = with_config_overrides(conf)
|
||||||
|
|
||||||
|
def decorator(fn):
|
||||||
|
return spec_test(overrides(with_state(single_phase(fn))))
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def expect_assertion_error(fn):
|
def expect_assertion_error(fn):
|
||||||
bad = False
|
bad = False
|
||||||
try:
|
try:
|
||||||
@ -380,9 +392,11 @@ def with_presets(preset_bases, reason=None):
|
|||||||
|
|
||||||
def with_config_overrides(config_overrides):
|
def with_config_overrides(config_overrides):
|
||||||
"""
|
"""
|
||||||
Decorator that applies a dict of config value overrides to the spec during execution.
|
WARNING: the spec_test decorator must wrap this, to ensure the decorated test actually runs.
|
||||||
This may be slow due to having to reload the spec modules,
|
This decorator forces the test to yield, and pytest doesn't run generator tests, and instead silently passes it.
|
||||||
since the specs uses globals instead of a configuration object.
|
Use 'spec_configured_state_test' instead of 'spec_state_test' if you are unsure.
|
||||||
|
|
||||||
|
This is a decorator that applies a dict of config value overrides to the spec during execution.
|
||||||
"""
|
"""
|
||||||
def decorator(fn):
|
def decorator(fn):
|
||||||
def wrapper(*args, spec: Spec, **kw):
|
def wrapper(*args, spec: Spec, **kw):
|
||||||
@ -390,9 +404,16 @@ def with_config_overrides(config_overrides):
|
|||||||
old_config = spec.config
|
old_config = spec.config
|
||||||
|
|
||||||
# apply our overrides to a copy of it, and apply it to the spec
|
# apply our overrides to a copy of it, and apply it to the spec
|
||||||
tmp_config = deepcopy(old_config)
|
tmp_config = deepcopy(old_config._asdict()) # not a private method, there are multiple
|
||||||
tmp_config.update(config_overrides)
|
tmp_config.update(config_overrides)
|
||||||
spec.config = tmp_config
|
config_types = spec.Configuration.__annotations__
|
||||||
|
# Retain types of all config values
|
||||||
|
test_config = {k: config_types[k](v) for k, v in tmp_config.items()}
|
||||||
|
|
||||||
|
# Output the config for test vectors (TODO: check config YAML encoding)
|
||||||
|
yield 'config', test_config
|
||||||
|
|
||||||
|
spec.config = spec.Configuration(**test_config)
|
||||||
|
|
||||||
# Run the function
|
# Run the function
|
||||||
out = fn(*args, spec=spec, **kw)
|
out = fn(*args, spec=spec, **kw)
|
||||||
|
@ -6,4 +6,8 @@ ZERO_BYTES32 = b'\x00' * 32
|
|||||||
|
|
||||||
|
|
||||||
def hash(x: Union[bytes, bytearray, memoryview]) -> Bytes32:
|
def hash(x: Union[bytes, bytearray, memoryview]) -> Bytes32:
|
||||||
return Bytes32(sha256(x).digest())
|
try:
|
||||||
|
return Bytes32(sha256(x).digest())
|
||||||
|
except TypeError:
|
||||||
|
print(x)
|
||||||
|
raise Exception("bad")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user