Skip to content

Commit 9f3344a

Browse files
committed
add eels transition tool
1 parent 57cd36c commit 9f3344a

File tree

3 files changed

+179
-18
lines changed

3 files changed

+179
-18
lines changed

src/ethereum_clis/clis/eels_t8n.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
"""
2+
EELS T8N transition tool.
3+
4+
https://github.com/ethereum/execution-specs
5+
"""
6+
7+
from abc import ABC, abstractmethod
8+
from typing import List
9+
10+
from ethereum_clis import TransitionTool, TransitionToolOutput
11+
from ethereum_test_base_types import BlobSchedule
12+
from ethereum_test_forks import Fork
13+
from ethereum_test_types import Alloc, Environment, Transaction
14+
15+
16+
class EELST8NWrapper(TransitionTool, ABC):
17+
"""
18+
Abstract base class for EELS T8N wrapper implementations.
19+
20+
This class provides an interface for calling the EELS T8N entrypoint directly
21+
without going through a binary. It implements a registration mechanism for
22+
concrete implementations to be set as the default wrapper.
23+
24+
Attributes:
25+
_default (class): The registered default subclass to use for instantiation.
26+
Should be set via `set_default()`.
27+
28+
Usage:
29+
1. Create a concrete subclass implementing all abstract methods
30+
2. Register it using `EELST8NWrapper.set_default(YourSubclass)`
31+
3. Instantiate via `EELST8NWrapper.default()`
32+
33+
"""
34+
35+
_default = None
36+
37+
@classmethod
38+
def set_default(cls, default_cls):
39+
"""
40+
Register a default subclass for instantiation.
41+
42+
Args:
43+
default_cls: The subclass to register as default
44+
45+
Raises:
46+
TypeError: If `default_cls` is not a subclass of EELST8NWrapper
47+
48+
"""
49+
if not issubclass(default_cls, cls):
50+
raise TypeError(f"{default_cls.__name__} is not a subclass of {cls.__name__}")
51+
cls._default = default_cls
52+
53+
@classmethod
54+
def default(cls, *args, **kwargs):
55+
"""
56+
Instantiate the registered default implementation.
57+
58+
Returns:
59+
Instance of the registered default subclass
60+
61+
Raises:
62+
RuntimeError: If no default implementation has been registered
63+
64+
"""
65+
if cls._default is None:
66+
raise RuntimeError("Default subclass not set!")
67+
return cls._default(*args, **kwargs)
68+
69+
@abstractmethod
70+
def version(self):
71+
"""Return the version string of the transition tool implementation."""
72+
pass
73+
74+
@abstractmethod
75+
def is_fork_supported(self, fork) -> bool:
76+
"""Check if a specific fork is supported by the tool."""
77+
pass
78+
79+
@abstractmethod
80+
def _evaluate_eels_t8n(
81+
self,
82+
*,
83+
t8n_data: TransitionTool.TransitionToolData,
84+
debug_output_path: str = "",
85+
) -> TransitionToolOutput:
86+
"""
87+
Execute the transition tool implementation (core functionality).
88+
89+
This must be implemented by concrete subclasses to provide the actual
90+
transition tool evaluation logic.
91+
92+
Args:
93+
t8n_data: Input data for the state transition
94+
debug_output_path: Optional path for writing debug output
95+
96+
Returns:
97+
TransitionToolOutput: Result of the state transition
98+
99+
"""
100+
pass
101+
102+
def evaluate(
103+
self,
104+
*,
105+
alloc: Alloc,
106+
txs: List[Transaction],
107+
env: Environment,
108+
fork: Fork,
109+
chain_id: int,
110+
reward: int,
111+
blob_schedule: BlobSchedule | None,
112+
debug_output_path: str = "",
113+
state_test: bool = False,
114+
slow_request: bool = False,
115+
) -> TransitionToolOutput:
116+
"""
117+
Execute the relevant evaluate method as required by the `t8n` tool.
118+
119+
If a client's `t8n` tool varies from the default behavior, this method
120+
can be overridden.
121+
"""
122+
fork_name = fork.transition_tool_name(
123+
block_number=env.number,
124+
timestamp=env.timestamp,
125+
)
126+
if env.number == 0:
127+
reward = -1
128+
t8n_data = self.TransitionToolData(
129+
alloc=alloc,
130+
txs=txs,
131+
env=env,
132+
fork_name=fork_name,
133+
chain_id=chain_id,
134+
reward=reward,
135+
blob_schedule=blob_schedule,
136+
state_test=state_test,
137+
)
138+
139+
return self._evaluate_eels_t8n(t8n_data=t8n_data, debug_output_path=debug_output_path)

src/pytest_plugins/filler/filler.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from cli.gen_index import generate_fixtures_index
2323
from config import AppConfig
2424
from ethereum_clis import TransitionTool
25+
from ethereum_clis.clis.eels_t8n import EELST8NWrapper
2526
from ethereum_clis.clis.geth import FixtureConsumerTool
2627
from ethereum_test_base_types import Account, Address, Alloc, ReferenceSpec
2728
from ethereum_test_fixtures import (
@@ -117,6 +118,13 @@ def default_html_report_file_path() -> str:
117118
def pytest_addoption(parser: pytest.Parser):
118119
"""Add command-line options to pytest."""
119120
evm_group = parser.getgroup("evm", "Arguments defining evm executable behavior")
121+
evm_group.addoption(
122+
"--eels",
123+
action="store_true",
124+
dest="eels",
125+
default=False,
126+
help="Use eels t8n entry point",
127+
)
120128
evm_group.addoption(
121129
"--evm-bin",
122130
action="store",
@@ -345,18 +353,21 @@ def pytest_configure(config):
345353

346354
# Instantiate the transition tool here to check that the binary path/trace option is valid.
347355
# This ensures we only raise an error once, if appropriate, instead of for every test.
348-
t8n = TransitionTool.from_binary_path(
349-
binary_path=config.getoption("evm_bin"), trace=config.getoption("evm_collect_traces")
350-
)
351-
if (
352-
isinstance(config.getoption("numprocesses"), int)
353-
and config.getoption("numprocesses") > 0
354-
and "Besu" in str(t8n.detect_binary_pattern)
355-
):
356-
pytest.exit(
357-
"The Besu t8n tool does not work well with the xdist plugin; use -n=0.",
358-
returncode=pytest.ExitCode.USAGE_ERROR,
356+
if config.getoption("eels", False):
357+
t8n = EELST8NWrapper.default(trace=config.getoption("evm_collect_traces"))
358+
else:
359+
t8n = TransitionTool.from_binary_path(
360+
binary_path=config.getoption("evm_bin"), trace=config.getoption("evm_collect_traces")
359361
)
362+
if (
363+
isinstance(config.getoption("numprocesses"), int)
364+
and config.getoption("numprocesses") > 0
365+
and "Besu" in str(t8n.detect_binary_pattern)
366+
):
367+
pytest.exit(
368+
"The Besu t8n tool does not work well with the xdist plugin; use -n=0.",
369+
returncode=pytest.ExitCode.USAGE_ERROR,
370+
)
360371

361372
if "Tools" not in config.stash[metadata_key]:
362373
config.stash[metadata_key]["Tools"] = {
@@ -555,9 +566,12 @@ def verify_fixtures_bin(request: pytest.FixtureRequest) -> Path | None:
555566
@pytest.fixture(autouse=True, scope="session")
556567
def t8n(request: pytest.FixtureRequest, evm_bin: Path) -> Generator[TransitionTool, None, None]:
557568
"""Return configured transition tool."""
558-
t8n = TransitionTool.from_binary_path(
559-
binary_path=evm_bin, trace=request.config.getoption("evm_collect_traces")
560-
)
569+
if request.config.getoption("eels"):
570+
t8n = EELST8NWrapper.default(trace=request.config.getoption("evm_collect_traces"))
571+
else:
572+
t8n = TransitionTool.from_binary_path(
573+
binary_path=evm_bin, trace=request.config.getoption("evm_collect_traces")
574+
)
561575
if not t8n.exception_mapper.reliable:
562576
warnings.warn(
563577
f"The t8n tool that is currently being used to fill tests ({t8n.__class__.__name__}) "

src/pytest_plugins/forks/forks.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from pytest import Mark, Metafunc
1515

1616
from ethereum_clis import TransitionTool
17+
from ethereum_clis.clis.eels_t8n import EELST8NWrapper
1718
from ethereum_test_forks import (
1819
Fork,
1920
get_deployed_forks,
@@ -524,11 +525,18 @@ def get_fork_option(config, option_name: str, parameter_name: str) -> Set[Fork]:
524525
return
525526

526527
evm_bin = config.getoption("evm_bin", None)
527-
if evm_bin is not None:
528+
eels = config.getoption("eels", False)
529+
530+
if eels:
531+
t8n = EELST8NWrapper.default()
532+
elif evm_bin is not None:
528533
t8n = TransitionTool.from_binary_path(binary_path=evm_bin)
529-
config.unsupported_forks = frozenset( # type: ignore
530-
fork for fork in selected_fork_set if not t8n.is_fork_supported(fork)
531-
)
534+
else:
535+
pytest.exit("One of --eels or --evm-bin should be specified")
536+
537+
config.unsupported_forks = frozenset( # type: ignore
538+
fork for fork in selected_fork_set if not t8n.is_fork_supported(fork)
539+
)
532540

533541

534542
@pytest.hookimpl(trylast=True)

0 commit comments

Comments
 (0)