Skip to content

chore(deps): create eels t8n transition tool #1807

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

Merged
merged 4 commits into from
Jun 28, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions src/ethereum_clis/clis/eels_t8n.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
"""
EELS T8N transition tool.

https://github.com/ethereum/execution-specs
"""

from abc import ABC, abstractmethod
from typing import List

from ethereum_clis import TransitionTool, TransitionToolOutput
from ethereum_test_base_types import BlobSchedule
from ethereum_test_forks import Fork
from ethereum_test_types import Alloc, Environment, Transaction


class EELST8NWrapper(TransitionTool, ABC):
"""
Abstract base class for EELS T8N wrapper implementations.

This class provides an interface for calling the EELS T8N entrypoint directly
without going through a binary. It implements a registration mechanism for
concrete implementations to be set as the default wrapper.

Attributes:
_default (class): The registered default subclass to use for instantiation.
Should be set via `set_default()`.

Usage:
1. Create a concrete subclass implementing all abstract methods
2. Register it using `EELST8NWrapper.set_default(YourSubclass)`
3. Instantiate via `EELST8NWrapper.default()`

"""

_default = None

@classmethod
def set_default(cls, default_cls):
"""
Register a default subclass for instantiation.

Args:
default_cls: The subclass to register as default

Raises:
TypeError: If `default_cls` is not a subclass of EELST8NWrapper

"""
if not issubclass(default_cls, cls):
raise TypeError(f"{default_cls.__name__} is not a subclass of {cls.__name__}")
cls._default = default_cls

@classmethod
def default(cls, *args, **kwargs):
"""
Instantiate the registered default implementation.

Returns:
Instance of the registered default subclass

Raises:
RuntimeError: If no default implementation has been registered

"""
if cls._default is None:
raise RuntimeError("Default subclass not set!")
return cls._default(*args, **kwargs)

@abstractmethod
def version(self):
"""Return the version string of the transition tool implementation."""
pass

@abstractmethod
def is_fork_supported(self, fork) -> bool:
"""Check if a specific fork is supported by the tool."""
pass

@abstractmethod
def _evaluate_eels_t8n(
self,
*,
t8n_data: TransitionTool.TransitionToolData,
debug_output_path: str = "",
) -> TransitionToolOutput:
"""
Execute the transition tool implementation (core functionality).

This must be implemented by concrete subclasses to provide the actual
transition tool evaluation logic.

Args:
t8n_data: Input data for the state transition
debug_output_path: Optional path for writing debug output

Returns:
TransitionToolOutput: Result of the state transition

"""
pass

def evaluate(
self,
*,
alloc: Alloc,
txs: List[Transaction],
env: Environment,
fork: Fork,
chain_id: int,
reward: int,
blob_schedule: BlobSchedule | None,
debug_output_path: str = "",
state_test: bool = False,
slow_request: bool = False,
) -> TransitionToolOutput:
"""
Execute the relevant evaluate method as required by the `t8n` tool.

If a client's `t8n` tool varies from the default behavior, this method
can be overridden.
"""
fork_name = fork.transition_tool_name(
block_number=env.number,
timestamp=env.timestamp,
)
if env.number == 0:
reward = -1
t8n_data = self.TransitionToolData(
alloc=alloc,
txs=txs,
env=env,
fork_name=fork_name,
chain_id=chain_id,
reward=reward,
blob_schedule=blob_schedule,
state_test=state_test,
)

return self._evaluate_eels_t8n(t8n_data=t8n_data, debug_output_path=debug_output_path)
42 changes: 28 additions & 14 deletions src/pytest_plugins/filler/filler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from cli.gen_index import generate_fixtures_index
from config import AppConfig
from ethereum_clis import TransitionTool
from ethereum_clis.clis.eels_t8n import EELST8NWrapper
from ethereum_clis.clis.geth import FixtureConsumerTool
from ethereum_test_base_types import Account, Address, Alloc, ReferenceSpec
from ethereum_test_fixtures import (
Expand Down Expand Up @@ -117,6 +118,13 @@ def default_html_report_file_path() -> str:
def pytest_addoption(parser: pytest.Parser):
"""Add command-line options to pytest."""
evm_group = parser.getgroup("evm", "Arguments defining evm executable behavior")
evm_group.addoption(
"--eels",
action="store_true",
dest="eels",
default=False,
help="Use eels t8n entry point",
)
evm_group.addoption(
"--evm-bin",
action="store",
Expand Down Expand Up @@ -345,18 +353,21 @@ def pytest_configure(config):

# Instantiate the transition tool here to check that the binary path/trace option is valid.
# This ensures we only raise an error once, if appropriate, instead of for every test.
t8n = TransitionTool.from_binary_path(
binary_path=config.getoption("evm_bin"), trace=config.getoption("evm_collect_traces")
)
if (
isinstance(config.getoption("numprocesses"), int)
and config.getoption("numprocesses") > 0
and "Besu" in str(t8n.detect_binary_pattern)
):
pytest.exit(
"The Besu t8n tool does not work well with the xdist plugin; use -n=0.",
returncode=pytest.ExitCode.USAGE_ERROR,
if config.getoption("eels", False):
t8n = EELST8NWrapper.default(trace=config.getoption("evm_collect_traces"))
else:
t8n = TransitionTool.from_binary_path(
binary_path=config.getoption("evm_bin"), trace=config.getoption("evm_collect_traces")
)
if (
isinstance(config.getoption("numprocesses"), int)
and config.getoption("numprocesses") > 0
and "Besu" in str(t8n.detect_binary_pattern)
):
pytest.exit(
"The Besu t8n tool does not work well with the xdist plugin; use -n=0.",
returncode=pytest.ExitCode.USAGE_ERROR,
)

if "Tools" not in config.stash[metadata_key]:
config.stash[metadata_key]["Tools"] = {
Expand Down Expand Up @@ -555,9 +566,12 @@ def verify_fixtures_bin(request: pytest.FixtureRequest) -> Path | None:
@pytest.fixture(autouse=True, scope="session")
def t8n(request: pytest.FixtureRequest, evm_bin: Path) -> Generator[TransitionTool, None, None]:
"""Return configured transition tool."""
t8n = TransitionTool.from_binary_path(
binary_path=evm_bin, trace=request.config.getoption("evm_collect_traces")
)
if request.config.getoption("eels"):
t8n = EELST8NWrapper.default(trace=request.config.getoption("evm_collect_traces"))
else:
t8n = TransitionTool.from_binary_path(
binary_path=evm_bin, trace=request.config.getoption("evm_collect_traces")
)
if not t8n.exception_mapper.reliable:
warnings.warn(
f"The t8n tool that is currently being used to fill tests ({t8n.__class__.__name__}) "
Expand Down
16 changes: 12 additions & 4 deletions src/pytest_plugins/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from pytest import Mark, Metafunc

from ethereum_clis import TransitionTool
from ethereum_clis.clis.eels_t8n import EELST8NWrapper
from ethereum_test_forks import (
Fork,
get_deployed_forks,
Expand Down Expand Up @@ -524,11 +525,18 @@ def get_fork_option(config, option_name: str, parameter_name: str) -> Set[Fork]:
return

evm_bin = config.getoption("evm_bin", None)
if evm_bin is not None:
eels = config.getoption("eels", False)

if eels:
t8n = EELST8NWrapper.default()
elif evm_bin is not None:
t8n = TransitionTool.from_binary_path(binary_path=evm_bin)
config.unsupported_forks = frozenset( # type: ignore
fork for fork in selected_fork_set if not t8n.is_fork_supported(fork)
)
else:
pytest.exit("One of --eels or --evm-bin should be specified")

config.unsupported_forks = frozenset( # type: ignore
fork for fork in selected_fork_set if not t8n.is_fork_supported(fork)
)


@pytest.hookimpl(trylast=True)
Expand Down
Loading