Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement: Fixture generation split based on hive specificity #301

Merged
merged 9 commits into from
Sep 21, 2023
4 changes: 2 additions & 2 deletions docs/getting_started/executing_tests_command_line.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Output:
```console
usage: fill [-h] [--evm-bin EVM_BIN] [--traces] [--solc-bin SOLC_BIN]
[--filler-path FILLER_PATH] [--output OUTPUT] [--flat-output]
[--disable-hive] [--forks] [--fork FORK] [--from FROM]
[--enable-hive] [--forks] [--fork FORK] [--from FROM]
[--until UNTIL] [--test-help]

options:
Expand All @@ -136,7 +136,7 @@ Arguments defining filler location and output:
deleted.
--flat-output Output each test case in the directory without the
folder structure.
--disable-hive Output tests skipping hive-related properties.
--enable-hive Output test fixtures with the hive-specific properties.

Arguments defining debug behavior:
--t8n-dump-dir T8N_DUMP_DIR
Expand Down
2 changes: 2 additions & 0 deletions src/ethereum_test_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
FixtureEngineNewPayload,
Header,
HistoryStorageAddress,
HiveFixture,
JSONEncoder,
Removable,
Storage,
Expand Down Expand Up @@ -77,6 +78,7 @@
"FixtureEngineNewPayload",
"Header",
"HistoryStorageAddress",
"HiveFixture",
"Initcode",
"JSONEncoder",
"Opcode",
Expand Down
2 changes: 2 additions & 0 deletions src/ethereum_test_tools/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
Hash,
Header,
HeaderNonce,
HiveFixture,
JSONEncoder,
Number,
Removable,
Expand Down Expand Up @@ -77,6 +78,7 @@
"Header",
"HeaderNonce",
"HistoryStorageAddress",
"HiveFixture",
"JSONEncoder",
"Number",
"Removable",
Expand Down
147 changes: 93 additions & 54 deletions src/ethereum_test_tools/common/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2631,7 +2631,8 @@ class FixtureBlock:
Representation of an Ethereum block within a test Fixture.
"""

rlp: Bytes = field(
rlp: Optional[Bytes] = field(
spencer-tb marked this conversation as resolved.
Show resolved Hide resolved
default=None,
json_encoder=JSONEncoder.Field(),
)
block_header: Optional[FixtureHeader] = field(
Expand All @@ -2641,13 +2642,6 @@ class FixtureBlock:
to_json=True,
),
)
new_payload: Optional[FixtureEngineNewPayload] = field(
default=None,
json_encoder=JSONEncoder.Field(
name="engineNewPayload",
to_json=True,
),
)
expected_exception: Optional[str] = field(
default=None,
json_encoder=JSONEncoder.Field(
Expand Down Expand Up @@ -2689,9 +2683,9 @@ class FixtureBlock:


@dataclass(kw_only=True)
class Fixture:
class BaseFixture:
"""
Cross-client compatible Ethereum test fixture.
Base Ethereum test fixture class.
"""

info: Dict[str, str] = field(
Expand All @@ -2701,36 +2695,78 @@ class Fixture:
to_json=True,
),
)
blocks: List[FixtureBlock] = field(
fork: str = field(
json_encoder=JSONEncoder.Field(
name="blocks",
to_json=True,
name="network",
),
)
fcu_version: Optional[int] = field(
name: str = field(
default="",
json_encoder=JSONEncoder.Field(
name="engineFcuVersion",
skip=True,
),
)
genesis: FixtureHeader = field(
_json: Dict[str, Any] | None = field(
default=None,
json_encoder=JSONEncoder.Field(
name="genesisBlockHeader",
to_json=True,
skip=True,
),
)

def __post_init__(self):
"""
Post init hook to convert to JSON after instantiation.
"""
self._json = to_json(self)

def to_json(self) -> Dict[str, Any]:
"""
Convert to JSON.
"""
assert self._json is not None, "Fixture not initialized"
self._json["_info"] = self.info
return self._json

def fill_info(
self,
t8n: TransitionTool,
ref_spec: ReferenceSpec | None,
):
"""
Fill the info field for this fixture
"""
self.info["filling-transition-tool"] = t8n.version()
if ref_spec is not None:
ref_spec.write_info(self.info)


@dataclass(kw_only=True)
class Fixture(BaseFixture):
"""
Cross-client specific test fixture information.
"""

genesis_rlp: Bytes = field(
json_encoder=JSONEncoder.Field(
name="genesisRLP",
),
)
head: Hash = field(
genesis: FixtureHeader = field(
json_encoder=JSONEncoder.Field(
name="lastblockhash",
name="genesisBlockHeader",
to_json=True,
),
)
fork: str = field(
blocks: Optional[List[FixtureBlock]] = field(
default=None,
json_encoder=JSONEncoder.Field(
name="network",
name="blocks",
to_json=True,
),
)
head: Hash = field(
json_encoder=JSONEncoder.Field(
name="lastblockhash",
),
)
pre_state: Mapping[str, Account] = field(
Expand All @@ -2753,42 +2789,45 @@ class Fixture:
name="sealEngine",
),
)
name: str = field(
default="",


@dataclass(kw_only=True)
class HiveFixture(BaseFixture):
"""
Hive specific test fixture information.
"""

genesis: FixtureHeader = field(
json_encoder=JSONEncoder.Field(
skip=True,
name="genesisBlockHeader",
to_json=True,
),
)

_json: Dict[str, Any] | None = field(
payloads: Optional[List[Optional[FixtureEngineNewPayload]]] = field(
default=None,
json_encoder=JSONEncoder.Field(
skip=True,
name="engineNewPayloads",
to_json=True,
),
)
fcu_version: Optional[int] = field(
default=None,
json_encoder=JSONEncoder.Field(
name="engineFcuVersion",
),
)
pre_state: Mapping[str, Account] = field(
json_encoder=JSONEncoder.Field(
name="pre",
cast_type=Alloc,
to_json=True,
),
)
post_state: Optional[Mapping[str, Account]] = field(
default=None,
json_encoder=JSONEncoder.Field(
name="postState",
cast_type=Alloc,
to_json=True,
),
)

def __post_init__(self):
"""
Post init hook to convert to JSON after instantiation.
"""
self._json = to_json(self)

def to_json(self) -> Dict[str, Any]:
"""
Convert to JSON.
"""
assert self._json is not None, "Fixture not initialized"
self._json["_info"] = self.info
return self._json

def fill_info(
self,
t8n: TransitionTool,
ref_spec: ReferenceSpec | None,
):
"""
Fill the info field for this fixture
"""
self.info["filling-transition-tool"] = t8n.version()
if ref_spec is not None:
ref_spec.write_info(self.info)
50 changes: 34 additions & 16 deletions src/ethereum_test_tools/filling/fill.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""
Filler object definitions.
"""
from typing import List, Optional
from typing import List, Optional, Union

from ethereum_test_forks import Fork
from evm_transition_tool import TransitionTool

from ..common import Fixture, alloc_to_accounts
from ..common import Fixture, HiveFixture, alloc_to_accounts
from ..reference_spec.reference_spec import ReferenceSpec
from ..spec import BaseTest

Expand All @@ -18,35 +18,53 @@ def fill_test(
engine: str,
spec: ReferenceSpec | None,
eips: Optional[List[int]] = None,
) -> Fixture:
) -> Optional[Union[Fixture, HiveFixture]]:
"""
Fills fixtures for the specified fork.
"""
t8n.reset_traces()

pre, genesis_rlp, genesis = test_spec.make_genesis(t8n, fork)

(blocks, head, alloc, fcu_version) = test_spec.make_blocks(
(blocks, payloads, head, alloc, fcu_version) = test_spec.make_blocks(
t8n,
genesis,
pre,
fork,
eips=eips,
)

fork_name = fork.name()
fixture = Fixture(
blocks=blocks,
genesis=genesis,
genesis_rlp=genesis_rlp,
head=head,
fork="+".join([fork_name] + [str(eip) for eip in eips]) if eips is not None else fork_name,
pre_state=pre,
post_state=alloc_to_accounts(alloc),
seal_engine=engine,
name=test_spec.tag,
fcu_version=fcu_version,
network_info = (
"+".join([fork.name()] + [str(eip) for eip in eips]) if eips is not None else fork.name()
)

fixture: Union[Fixture, HiveFixture]
if test_spec.base_test_config.enable_hive:
if fork.engine_new_payload_version() is not None:
fixture = HiveFixture(
payloads=payloads,
fcu_version=fcu_version,
genesis=genesis,
fork=network_info,
pre_state=pre,
post_state=alloc_to_accounts(alloc),
name=test_spec.tag,
)
else: # pre Merge tests are not supported in Hive
# TODO: remove this logic. if hive enabled set --from to Merge
return None
else:
fixture = Fixture(
blocks=blocks,
genesis=genesis,
genesis_rlp=genesis_rlp,
head=head,
fork=network_info,
pre_state=pre,
post_state=alloc_to_accounts(alloc),
seal_engine=engine,
name=test_spec.tag,
)
fixture.fill_info(t8n, spec)

return fixture
13 changes: 10 additions & 3 deletions src/ethereum_test_tools/spec/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Bytes,
Environment,
FixtureBlock,
FixtureEngineNewPayload,
FixtureHeader,
Hash,
Transaction,
Expand Down Expand Up @@ -86,9 +87,9 @@ class BaseTestConfig:
General configuration that all tests must support.
"""

disable_hive: bool = False
enable_hive: bool = False
"""
Disable any hive-related properties that the output could contain.
Enable any hive-related properties that the output could contain.
"""


Expand Down Expand Up @@ -127,7 +128,13 @@ def make_blocks(
fork: Fork,
chain_id: int = 1,
eips: Optional[List[int]] = None,
) -> Tuple[List[FixtureBlock], Hash, Dict[str, Any], Optional[int]]:
) -> Tuple[
Optional[List[FixtureBlock]],
Optional[List[Optional[FixtureEngineNewPayload]]],
Hash,
Dict[str, Any],
Optional[int],
]:
"""
Generate the blockchain that must be executed sequentially during test.
"""
Expand Down
Loading