Skip to content

Commit 7736d0b

Browse files
committed
Temp: Gather opcode counts in fixtures
1 parent 9a1cd16 commit 7736d0b

File tree

5 files changed

+76
-1
lines changed

5 files changed

+76
-1
lines changed

src/ethereum_clis/cli_types.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pathlib import Path
55
from typing import Annotated, Any, Dict, List, Self
66

7-
from pydantic import Field
7+
from pydantic import Field, PlainSerializer, PlainValidator
88

99
from ethereum_test_base_types import (
1010
BlobSchedule,
@@ -29,6 +29,7 @@
2929
Transaction,
3030
TransactionReceipt,
3131
)
32+
from ethereum_test_vm import Opcode, Opcodes
3233
from pytest_plugins.custom_logging import get_logger
3334

3435
logger = get_logger(__name__)
@@ -175,6 +176,40 @@ def print(self):
175176
tx.print()
176177

177178

179+
_opcode_synonyms = {
180+
"KECCAK256": "SHA3",
181+
}
182+
183+
184+
def validate_opcode(obj: Any) -> Opcodes:
185+
"""Validate an opcode from a string."""
186+
if isinstance(obj, Opcode) or isinstance(obj, Opcodes):
187+
return obj
188+
if isinstance(obj, str):
189+
if obj in _opcode_synonyms:
190+
obj = _opcode_synonyms[obj]
191+
for op in Opcodes:
192+
if str(op) == obj:
193+
return op
194+
raise Exception(f"Unable to validate {obj} (type={type(obj)})")
195+
196+
197+
class OpcodeCount(EthereumTestRootModel):
198+
"""Opcode count returned from the evm tool."""
199+
200+
root: Dict[
201+
Annotated[Opcodes, PlainValidator(validate_opcode), PlainSerializer(lambda o: str(o))], int
202+
]
203+
204+
def __add__(self, other: Self) -> Self:
205+
"""Add two instances of opcode count dictionaries."""
206+
assert isinstance(other, OpcodeCount), f"Incompatible type {type(other)}"
207+
new_dict = self.model_dump() | other.model_dump()
208+
for match_key in self.root.keys() & other.root.keys():
209+
new_dict[match_key] = self.root[match_key] + other.root[match_key]
210+
return OpcodeCount(new_dict)
211+
212+
178213
class Result(CamelModel):
179214
"""Result of a transition tool output."""
180215

@@ -202,6 +237,7 @@ class Result(CamelModel):
202237
BlockExceptionWithMessage | UndefinedException | None, ExceptionMapperValidator
203238
] = None
204239
traces: Traces | None = None
240+
opcode_count: OpcodeCount | None = None
205241

206242

207243
class TransitionToolInput(CamelModel):

src/ethereum_clis/clis/evmone.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class EvmOneTransitionTool(TransitionTool):
2525
binary: Path
2626
cached_version: Optional[str] = None
2727
trace: bool
28+
supports_opcode_count: ClassVar[bool] = True
2829

2930
def __init__(
3031
self,

src/ethereum_clis/transition_tool.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from ethereum_test_types import Alloc, Environment, Transaction
2626

2727
from .cli_types import (
28+
OpcodeCount,
2829
Traces,
2930
TransactionReceipt,
3031
TransactionTraces,
@@ -71,6 +72,7 @@ class TransitionTool(EthereumCLI):
7172
t8n_use_server: bool = False
7273
server_url: str | None = None
7374
process: Optional[subprocess.Popen] = None
75+
supports_opcode_count: ClassVar[bool] = False
7476

7577
supports_xdist: ClassVar[bool] = True
7678

@@ -248,6 +250,13 @@ def _evaluate_filesystem(
248250
"--state.chainid",
249251
str(t8n_data.chain_id),
250252
]
253+
if self.supports_opcode_count:
254+
args.extend(
255+
[
256+
"--opcode.count",
257+
"opcodes.json",
258+
]
259+
)
251260

252261
if self.trace:
253262
args.append("--trace")
@@ -308,6 +317,20 @@ def _evaluate_filesystem(
308317
output = TransitionToolOutput.model_validate(
309318
output_contents, context={"exception_mapper": self.exception_mapper}
310319
)
320+
if self.supports_opcode_count:
321+
opcode_count_file_path = Path(temp_dir.name) / "opcodes.json"
322+
if opcode_count_file_path.exists():
323+
opcode_count = OpcodeCount.model_validate_json(opcode_count_file_path.read_text())
324+
output.result.opcode_count = opcode_count
325+
326+
if debug_output_path:
327+
dump_files_to_directory(
328+
debug_output_path,
329+
{
330+
"opcodes.json": opcode_count.model_dump(),
331+
},
332+
)
333+
311334
if self.trace:
312335
output.result.traces = self.collect_traces(
313336
output.result.receipts, temp_dir, debug_output_path

src/ethereum_test_specs/base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from typing_extensions import Self
1616

1717
from ethereum_clis import Result, TransitionTool
18+
from ethereum_clis.types import OpcodeCount
1819
from ethereum_test_base_types import to_hex
1920
from ethereum_test_execution import BaseExecute, ExecuteFormat, LabeledExecuteFormat
2021
from ethereum_test_fixtures import (
@@ -75,6 +76,7 @@ class BaseTest(BaseModel):
7576
_operation_mode: OpMode | None = PrivateAttr(None)
7677
_gas_optimization: int | None = PrivateAttr(None)
7778
_gas_optimization_max_gas_limit: int | None = PrivateAttr(None)
79+
_opcode_count: OpcodeCount | None = PrivateAttr(None)
7880

7981
expected_benchmark_gas_used: int | None = None
8082
skip_gas_used_validation: bool = False
@@ -130,6 +132,7 @@ def from_test(
130132
)
131133
new_instance._request = base_test._request
132134
new_instance._operation_mode = base_test._operation_mode
135+
new_instance._opcode_count = base_test._opcode_count
133136
return new_instance
134137

135138
@classmethod

src/ethereum_test_specs/blockchain.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,12 @@ def generate_block_data(
531531
slow_request=self.is_tx_gas_heavy_test(),
532532
)
533533

534+
if transition_tool_output.result.opcode_count is not None:
535+
if self._opcode_count is None:
536+
self._opcode_count = transition_tool_output.result.opcode_count
537+
else:
538+
self._opcode_count += transition_tool_output.result.opcode_count
539+
534540
# One special case of the invalid transactions is the blob gas used,
535541
# since this value is not included in the transition tool result, but
536542
# it is included in the block header, and some clients check it before
@@ -760,6 +766,9 @@ def make_fixture(
760766
blob_schedule=FixtureBlobSchedule.from_blob_schedule(fork.blob_schedule()),
761767
chain_id=self.chain_id,
762768
),
769+
info={
770+
"opcode_count": self._opcode_count.model_dump(),
771+
},
763772
)
764773

765774
def make_hive_fixture(
@@ -825,6 +834,9 @@ def make_hive_fixture(
825834
chain_id=self.chain_id,
826835
blob_schedule=FixtureBlobSchedule.from_blob_schedule(fork.blob_schedule()),
827836
),
837+
"info": {
838+
"opcode_count": self._opcode_count.model_dump(),
839+
},
828840
}
829841

830842
# Add format-specific fields

0 commit comments

Comments
 (0)