diff --git a/.gitmodules b/.gitmodules index e69de29bb2..012bfa5074 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "eest_tests"] + path = eest_tests + url = https://github.com/ethereum/execution-spec-tests diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000000..02caaf3efb --- /dev/null +++ b/conftest.py @@ -0,0 +1,8 @@ +import pytest +from ethereum_spec_tools.evm_tools.t8n.transition_tool import ExecutionSpecsWrapper +from ethereum_clis import TransitionTool + + +@pytest.hookimpl(tryfirst=True) +def pytest_configure(config): + TransitionTool.set_default_tool(ExecutionSpecsWrapper) \ No newline at end of file diff --git a/eest_tests b/eest_tests new file mode 160000 index 0000000000..8180a5b144 --- /dev/null +++ b/eest_tests @@ -0,0 +1 @@ +Subproject commit 8180a5b1446d7ef57ba36d09174d9fd3fe228a98 diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000000..d251b60a2b --- /dev/null +++ b/pytest.ini @@ -0,0 +1,23 @@ +[pytest] +console_output_style = count +minversion = 7.0 +python_files = *.py +testpaths = tests/ +markers = + slow + pre_alloc_modify + ported_from +addopts = + -p pytest_plugins.concurrency + -p pytest_plugins.filler.pre_alloc + -p pytest_plugins.solc.solc + -p pytest_plugins.filler.filler + -p pytest_plugins.filler.static_filler + -p pytest_plugins.shared.execute_fill + -p pytest_plugins.forks.forks + -p pytest_plugins.help.help + --tb short + --ignore tests/cancun/eip4844_blobs/point_evaluation_vectors/ +# these customizations require the pytest-custom-report plugin +report_passed_verbose = FILLED +report_xpassed_verbose = XFILLED diff --git a/setup.cfg b/setup.cfg index c8fdb34765..51cf72545f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -135,6 +135,7 @@ install_requires = py-ecc>=8.0.0b2,<9 ethereum-types>=0.2.1,<0.3 ethereum-rlp>=0.1.1,<0.2 + ethereum-execution-spec-tests @ git+https://github.com/gurukamath/execution-spec-tests@4732abc2aa2fa23365ef3b54349126314758d11d [options.package_data] ethereum = @@ -157,6 +158,7 @@ console_scripts = 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 docc.plugins = ethereum_spec_tools.docc.discover = ethereum_spec_tools.docc:EthereumDiscover @@ -171,11 +173,11 @@ docc.plugins.html = [options.extras_require] 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 diff --git a/src/ethereum/prague/vm/interpreter.py b/src/ethereum/prague/vm/interpreter.py index bd61d38c1f..826abf63cd 100644 --- a/src/ethereum/prague/vm/interpreter.py +++ b/src/ethereum/prague/vm/interpreter.py @@ -125,6 +125,9 @@ def process_message_call(message: Message) -> MessageCallOutput: if message.tx_env.authorizations != (): refund_counter += set_delegation(message) + message.code = get_account( + block_env.state, message.code_address + ).code delegated_address = get_delegated_code_address(message.code) if delegated_address is not None: message.disable_precompiles = True diff --git a/src/ethereum_spec_tools/evm_tools/t8n/env.py b/src/ethereum_spec_tools/evm_tools/t8n/env.py index 9a9a0bf7d4..ebc00b127d 100644 --- a/src/ethereum_spec_tools/evm_tools/t8n/env.py +++ b/src/ethereum_spec_tools/evm_tools/t8n/env.py @@ -294,22 +294,17 @@ def read_block_hashes(self, data: Any, t8n: "T8N") -> None: # Read the block hashes block_hashes: List[Any] = [] - # The hex key strings provided might not have standard formatting - clean_block_hashes: Dict[int, Hash32] = {} - if "blockHashes" in data: - for key, value in data["blockHashes"].items(): - int_key = int(key, 16) - clean_block_hashes[int_key] = Hash32(hex_to_bytes(value)) - # Store a maximum of 256 block hashes. max_blockhash_count = min(Uint(256), self.block_number) for number in range( self.block_number - max_blockhash_count, self.block_number ): - if number in clean_block_hashes.keys(): - block_hashes.append(clean_block_hashes[number]) + if "blockHashes" in data and str(number) in data["blockHashes"]: + block_hashes.append( + Hash32(hex_to_bytes(data["blockHashes"][str(number)])) + ) else: - block_hashes.append(None) + block_hashes.append(None) self.block_hashes = block_hashes 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..16325b40a4 --- /dev/null +++ b/src/ethereum_spec_tools/evm_tools/t8n/transition_tool.py @@ -0,0 +1,103 @@ +from ethereum_clis import TransitionTool +from ethereum_clis.clis.execution_specs import ExecutionSpecsExceptionMapper +from ethereum_clis.transition_tool import model_dump_config +import ethereum +import tempfile + +from . import T8N +from .. import create_parser +from io import StringIO, TextIOWrapper +import json +from ethereum_clis.types import TransitionToolOutput +from ethereum_clis.file_utils import dump_files_to_directory + +class ExecutionSpecsWrapper(TransitionTool): + t8n_use_eels: bool = True + detect_binary_pattern = "" + + def __init__( + self, + *, + trace: bool = False, + ): + """Initialize the EELS Transition Tool interface.""" + super().__init__( + exception_mapper=ExecutionSpecsExceptionMapper(), trace=trace + ) + + def version(self): + return ethereum.__version__ + + def _evaluate_custom_t8n(self, t8n_data, debug_output_path): + request_data = t8n_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 t8n_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 +