diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3457a81f90..8274d6369e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -45,7 +45,7 @@ jobs: - name: Run Tox (CPython) if: "${{ !startsWith(matrix.py, 'pypy') }}" - run: tox -e static,optimized,py3 + run: tox -e static,py3_eest,optimized,py3 - name: Run Tox (PyPy) if: "${{ startsWith(matrix.py, 'pypy') }}" diff --git a/.gitignore b/.gitignore index 144dc04c86..fd08eaf32b 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,5 @@ pip-delete-this-directory.txt tests/execution-spec-generated-tests tests/fixtures tests/t8n_testdata + +fixtures diff --git a/.gitmodules b/.gitmodules index e69de29bb2..4e4c40f501 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "eest_tests/execution-spec-tests"] + path = eest_tests/execution-spec-tests + url = https://github.com/ethereum/execution-spec-tests.git diff --git a/eest_tests/conftest.py b/eest_tests/conftest.py new file mode 100644 index 0000000000..190ce053af --- /dev/null +++ b/eest_tests/conftest.py @@ -0,0 +1,8 @@ +import pytest +from ethereum_spec_tools.evm_tools.t8n.transition_tool import EELST8N +from ethereum_clis import TransitionTool + + +@pytest.hookimpl(tryfirst=True) +def pytest_configure(config): + TransitionTool.set_default_tool(EELST8N) diff --git a/eest_tests/execution-spec-tests b/eest_tests/execution-spec-tests new file mode 160000 index 0000000000..6cf5107cee --- /dev/null +++ b/eest_tests/execution-spec-tests @@ -0,0 +1 @@ +Subproject commit 6cf5107cee0eae1c2d4dd6a5ef5bc729aea6c379 diff --git a/pyproject.toml b/pyproject.toml index 7ea422b829..0d5cac2270 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,8 @@ dependencies = [ "ethereum-types>=0.2.1,<0.3", "ethereum-rlp>=0.1.4,<0.2", "cryptography>=45.0.1,<46", + "ethereum-execution-spec-tests @ git+https://github.com/ethereum/execution-spec-tests@6cf5107cee0eae1c2d4dd6a5ef5bc729aea6c379", + "ethereum-spec-evm-resolver @ git+https://github.com/petertdavies/ethereum-spec-evm-resolver", ] [tool.setuptools] @@ -152,11 +154,11 @@ packages = [ [project.optional-dependencies] test = [ - "pytest>=7.4.0,<8", + "pytest>=8,<9", "pytest-cov>=4.1.0,<5", "pytest-xdist>=3.3.1,<4", "GitPython>=3.1.0,<3.2", - "filelock>=3.12.3,<3.13", + "filelock>=3.15.1,<4", "requests", "requests-cache>=1.2.1,<2", ] @@ -195,6 +197,7 @@ ethereum-spec-sync = "ethereum_spec_tools.sync:main" ethereum-spec-new-fork = "ethereum_spec_tools.new_fork:main" ethereum-spec-patch = "ethereum_spec_tools.patch_tool:main" ethereum-spec-evm = "ethereum_spec_tools.evm_tools:main" +ethereum-spec-fill = "cli.pytest_commands.fill:fill" [project.entry-points."docc.plugins"] "ethereum_spec_tools.docc.discover" = "ethereum_spec_tools.docc:EthereumDiscover" diff --git a/src/ethereum_spec_tools/evm_tools/t8n/transition_tool.py b/src/ethereum_spec_tools/evm_tools/t8n/transition_tool.py new file mode 100644 index 0000000000..c84503cfb0 --- /dev/null +++ b/src/ethereum_spec_tools/evm_tools/t8n/transition_tool.py @@ -0,0 +1,128 @@ +""" +Implementation of the EELS T8N for execution-spec-tests. +""" + +import json +import tempfile +from io import StringIO +from typing import Any, Dict, Optional + +from ethereum_clis.clis.execution_specs import ExecutionSpecsExceptionMapper +from ethereum_clis.file_utils import dump_files_to_directory +from ethereum_clis.transition_tool import TransitionTool, model_dump_config +from ethereum_clis.types import TransitionToolOutput +from ethereum_test_forks import Fork + +import ethereum + +from .. import create_parser +from ..utils import get_supported_forks +from . import T8N + + +class EELST8N(TransitionTool): + """Implementation of the EELS T8N for execution-spec-tests.""" + + def __init__( + self, + *, + trace: bool = False, + ): + """Initialize the EELS Transition Tool interface.""" + self.exception_mapper = ExecutionSpecsExceptionMapper() + self.trace = trace + self._info_metadata: Optional[Dict[str, Any]] = {} + + def version(self) -> str: + """Version of the t8n tool.""" + return ethereum.__version__ + + def is_fork_supported(self, fork: Fork) -> bool: + """Return True if the fork is supported by the tool.""" + return fork.transition_tool_name() in get_supported_forks() + + def evaluate( + self, + *, + transition_tool_data: TransitionTool.TransitionToolData, + debug_output_path: str = "", + slow_request: bool = False, # noqa: U100, F841 + ) -> TransitionToolOutput: + """ + Evaluate using the EELS T8N entry point. + """ + request_data = transition_tool_data.get_request_data() + request_data_json = request_data.model_dump( + mode="json", **model_dump_config + ) + + t8n_args = [ + "t8n", + "--input.alloc=stdin", + "--input.env=stdin", + "--input.txs=stdin", + "--output.result=stdout", + "--output.body=stdout", + "--output.alloc=stdout", + f"--state.fork={request_data_json['state']['fork']}", + f"--state.chainid={request_data_json['state']['chainid']}", + f"--state.reward={request_data_json['state']['reward']}", + ] + + if transition_tool_data.state_test: + t8n_args.append("--state-test") + + temp_dir = tempfile.TemporaryDirectory() + if self.trace: + t8n_args.extend( + [ + "--trace", + "--trace.memory", + "--trace.returndata", + f"--output.basedir={temp_dir.name}", + ] + ) + + parser = create_parser() + t8n_options = parser.parse_args(t8n_args) + + out_stream = StringIO() + + in_stream = StringIO(json.dumps(request_data_json["input"])) + + t8n = T8N(t8n_options, out_stream, in_stream) + t8n.run() + + output_dict = json.loads(out_stream.getvalue()) + output: TransitionToolOutput = TransitionToolOutput.model_validate( + output_dict, context={"exception_mapper": self.exception_mapper} + ) + + if debug_output_path: + dump_files_to_directory( + debug_output_path, + { + "input/alloc.json": request_data.input.alloc, + "input/env.json": request_data.input.env, + "input/txs.json": [ + tx.model_dump(mode="json", **model_dump_config) + for tx in request_data.input.txs + ], + }, + ) + + dump_files_to_directory( + debug_output_path, + { + "output/alloc.json": output.alloc, + "output/result.json": output.result, + }, + ) + + if self.trace: + self.collect_traces( + output.result.receipts, temp_dir, debug_output_path + ) + temp_dir.cleanup() + + return output diff --git a/tox.ini b/tox.ini index 3724650694..8ae7dd2a26 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] min_version = 2.0 -envlist = py3,pypy3,static +envlist = py3,pypy3,py3_eest,static [testenv:static] extras = @@ -30,7 +30,26 @@ commands = --no-cov-on-fail \ --cov-branch \ --ignore-glob='tests/fixtures/*' \ - --basetemp="{temp_dir}/pytest" + --basetemp="{temp_dir}/pytest" \ + tests + +[testenv:py3_eest] +extras = + test +commands = + fill \ + -m "not slow and not zkevm and not benchmark" \ + -n auto --maxprocesses 6 \ + --basetemp="{temp_dir}/pytest" \ + --clean \ + eest_tests/execution-spec-tests/tests + fill \ + -m "not slow and not zkevm and not benchmark" \ + -n auto --maxprocesses 6 \ + --basetemp="{temp_dir}/pytest" \ + --clean \ + --fork Osaka \ + eest_tests/execution-spec-tests/tests/osaka [testenv:pypy3] extras = @@ -38,14 +57,25 @@ extras = passenv = PYPY_GC_MAX commands = - pytest \ + fill \ --tb=no \ --show-capture=no \ --disable-warnings \ - -m "not slow" \ - -n auto --maxprocesses 5 \ - --ignore-glob='tests/fixtures/*' \ - --basetemp="{temp_dir}/pytest" + -m "not slow and not zkevm and not benchmark" \ + -n auto --maxprocesses 3 \ + --basetemp="{temp_dir}/pytest" \ + --clean \ + eest_tests/execution-spec-tests/tests + fill \ + --tb=no \ + --show-capture=no \ + --disable-warnings \ + -m "not slow and not zkevm and not benchmark" \ + -n auto --maxprocesses 3 \ + --basetemp="{temp_dir}/pytest" \ + --clean \ + --fork Osaka \ + eest_tests/execution-spec-tests/tests/osaka [testenv:optimized] extras = @@ -60,7 +90,8 @@ commands = --ignore-glob='tests/fixtures/*' \ --ignore-glob='tests/test_t8n.py' \ --basetemp="{temp_dir}/pytest" \ - --optimized + --optimized \ + tests [testenv:doc] basepython = python3 diff --git a/vulture_whitelist.py b/vulture_whitelist.py index 8c0cc7c08c..ae8bcbe564 100644 --- a/vulture_whitelist.py +++ b/vulture_whitelist.py @@ -10,6 +10,7 @@ ) from ethereum_spec_tools.evm_tools.t8n.env import Ommer from ethereum_spec_tools.evm_tools.t8n.evm_trace import Trace, FinalTrace +from ethereum_spec_tools.evm_tools.t8n.transition_tool import EELST8N from ethereum_spec_tools.lint.lints.glacier_forks_hygiene import ( GlacierForksHygiene, ) @@ -54,6 +55,13 @@ _EvmToolHandler.do_POST _EvmToolHandler.log_request +# src/ethereum_spec_tools/evm_tools/transition_tool.py +EELST8N +EELST8N._info_metadata +EELST8N.version +EELST8N.is_fork_supported +EELST8N.evaluate + # src/ethereum_spec_tools/loaders/fixture_loader.py Load._network diff --git a/whitelist.txt b/whitelist.txt index 62cef423cc..7edf451b1b 100644 --- a/whitelist.txt +++ b/whitelist.txt @@ -494,4 +494,7 @@ SECP256R1P secp256r1 sig -CLZ \ No newline at end of file +CLZ +EELST8N +clis +T8 \ No newline at end of file