Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion src/ethereum_spec_tools/evm_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def main(
in_file = sys.stdin

if options.evm_tool == "t8n":
t8n_tool = T8N(options, out_file, in_file)
t8n_tool = T8N(options, in_file, out_file)
return t8n_tool.run()
elif options.evm_tool == "b11r":
b11r_tool = B11R(options, out_file, in_file)
Expand Down
2 changes: 1 addition & 1 deletion src/ethereum_spec_tools/evm_tools/statetest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def run_test_case(
if output_basedir is not None:
t8n_options.output_basedir = output_basedir

t8n = T8N(t8n_options, out_stream, in_stream)
t8n = T8N(t8n_options, in_stream, out_stream)
t8n.run_state_test()
return t8n.result

Expand Down
70 changes: 67 additions & 3 deletions src/ethereum_spec_tools/evm_tools/t8n/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@
import json
import os
from functools import partial
from typing import Any, TextIO
from typing import Any, Optional, TextIO, Union

from ethereum_rlp import rlp
from ethereum_types.numeric import U64, U256, Uint

from ethereum import trace
from ethereum.exceptions import EthereumException, InvalidBlock
from ethereum_clis.types import TransitionToolRequest
from ethereum_spec_tools.forks import Hardfork
from ethereum_clis.types import TransitionToolOutput
from ethereum_clis.clis.execution_specs import ExecutionSpecsExceptionMapper

from ..loaders.fixture_loader import Load
from ..loaders.fork_loader import ForkLoad
from ..utils import (
FatalException,
get_module_name,
get_module_name_json_input,
get_stream_logger,
parse_hex_or_int,
)
Expand Down Expand Up @@ -80,13 +84,59 @@ class T8N(Load):
"""The class that carries out the transition"""

def __init__(
self, options: Any, out_file: TextIO, in_file: TextIO
self,
options: Any,
in_file: Union[TransitionToolRequest, TextIO],
out_file: Optional[TextIO] = None
) -> None:
self.out_file = out_file
self.in_file = in_file
self.options = options
self.forks = Hardfork.discover()

if isinstance(in_file, TransitionToolRequest):
self._init_from_pydantic(options, in_file)
else:
if out_file is None:
raise ValueError("out_file is required for JSON file input")
self._init_from_json(options, in_file, out_file)

def _init_from_pydantic(self, options: Any, in_file: TransitionToolRequest) -> None:
fork_module, self.fork_block = get_module_name(
self.forks, options, in_file.input.env
)
self.fork = ForkLoad(fork_module)
self.exception_mapper = ExecutionSpecsExceptionMapper()

if options.trace:
trace_memory = getattr(options, "trace.memory", False)
trace_stack = not getattr(options, "trace.nostack", False)
trace_return_data = getattr(options, "trace.returndata")
trace.set_evm_trace(
partial(
evm_trace,
trace_memory=trace_memory,
trace_stack=trace_stack,
trace_return_data=trace_return_data,
output_basedir=options.output_basedir,
)
)
self.logger = get_stream_logger("T8N")

super().__init__(
options.state_fork,
fork_module,
)

self.chain_id = parse_hex_or_int(options.state_chainid, U64)
self.alloc = Alloc(self, in_file.input.alloc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should re-use the Alloc, Env and Txs types defined by EEST. And if the json inputs are provided, we could just serialize/de-serialize using pydantic. That way, we can get of a lot of redundant code in env.py and t8n_types.py.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right! :) The first thing i tried was to re-use the types (pydantic models) from EEST (to not have these intermediary classes at all) But seems like our execution-spec code isn't directly able to work with those (without some translations/conversions)? To be able to utilize the EEST types directly would definitely be the most efficient - any suggestions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would something like this work?

  1. If EEST types are the input: EEST types -> EELS BlockEnvironment -> run the t8n tool
  2. If json input: json input -> EEST types -> EELS BlockEnvironment -> run the t8n tool

You might have to transform the EEST Alloc to the relevant fork's State dataclass but I think this is still much cleaner than what we currently have.

self.env = Env(self, in_file.input.env)
self.txs = Txs(self, in_file.input.txs)
self.result = Result(
self.env.block_difficulty, self.env.base_fee_per_gas
)

def _init_from_json(self, options: Any, in_file: TextIO, out_file: TextIO) -> None:
if "stdin" in (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we still be able to consume json files if needed? We will need to continue supporting that use case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes! with this commit dc31f96 I added the dual input option where we preserve the older json input route

options.input_env,
options.input_alloc,
Expand All @@ -96,7 +146,7 @@ def __init__(
else:
stdin = None

fork_module, self.fork_block = get_module_name(
fork_module, self.fork_block = get_module_name_json_input(
self.forks, self.options, stdin
)
self.fork = ForkLoad(fork_module)
Expand Down Expand Up @@ -311,7 +361,21 @@ def run(self) -> int:

json_state = self.alloc.to_json()
json_result = self.result.to_json()
if isinstance(self.in_file, TransitionToolRequest):
json_output = {}

txs_rlp = "0x" + rlp.encode(self.txs.all_txs).hex()
json_output["body"] = txs_rlp
json_output["alloc"] = json_state
json_output["result"] = json_result
output: TransitionToolOutput = TransitionToolOutput.model_validate(
json_output, context={"exception_mapper": self.exception_mapper}
)
return output
else:
return self._write_json_output(json_state, json_result)

def _write_json_output(self, json_state: Any, json_result: Any) -> int:
json_output = {}

if self.options.output_body == "stdout":
Expand Down
Loading