Skip to content

Commit 5fa5938

Browse files
feat(test-benchmark): support per block opcode count record (#3183)
* feat: support per block opcode count record * fix(test-clis): Fix eels opcode count --------- Co-authored-by: Mario Vega <marioevz@gmail.com>
1 parent 213f0e8 commit 5fa5938

7 files changed

Lines changed: 71 additions & 1 deletion

File tree

packages/testing/src/execution_testing/cli/pytest_commands/plugins/filler/filler.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,11 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
17871787
fill_metadata["opcode_count"] = (
17881788
t8n.opcode_count.model_dump()
17891789
)
1790+
if t8n.opcode_count_per_block:
1791+
fill_metadata["opcode_count_per_block"] = [
1792+
block_opcode_count.model_dump()
1793+
for block_opcode_count in t8n.opcode_count_per_block
1794+
]
17901795
if fill_result.metadata:
17911796
fill_metadata.update(fill_result.metadata)
17921797

packages/testing/src/execution_testing/client_clis/cli_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ def print(self) -> None:
329329

330330
_opcode_synonyms = {
331331
"KECCAK256": "SHA3",
332+
"KECCAK": "SHA3",
332333
"DIFFICULTY": "PREVRANDAO",
333334
}
334335

packages/testing/src/execution_testing/client_clis/client_backend.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ class ClientBackend:
167167

168168
# t8n-compatibility stubs — fill's filler reads these on the backend.
169169
opcode_count: OpcodeCount | None = None
170+
opcode_count_per_block: List[OpcodeCount] | None = None
170171
output_cache: Any = None
171172
debug_dump_dir: Path | None = None
172173
call_counter: int = 0

packages/testing/src/execution_testing/client_clis/clis/execution_specs.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ def _evaluate(
128128
# TODO: This should be optimized by the t8n tool instead.
129129
t8n_args.append("--input.blobParams=stdin")
130130

131+
if self.supports_opcode_count:
132+
t8n_args.append("--opcode.count=stdout")
133+
131134
if self.trace:
132135
t8n_args.extend(
133136
[
@@ -148,6 +151,12 @@ def _evaluate(
148151
t8n.run()
149152

150153
output_dict = json.loads(out_stream.getvalue())
154+
155+
if "opcodeCount" in output_dict and "result" in output_dict:
156+
output_dict["result"]["opcodeCount"] = output_dict.pop(
157+
"opcodeCount"
158+
)
159+
151160
output: TransitionToolOutput = TransitionToolOutput.model_validate(
152161
output_dict, context={"exception_mapper": self.exception_mapper}
153162
)

packages/testing/src/execution_testing/client_clis/tests/test_execution_specs.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ def test_evm_t8n(
184184
),
185185
)
186186
assert to_json(t8n_output.alloc.get()) == expected.get("alloc")
187+
t8n_result = to_json(t8n_output.result)
187188
if isinstance(default_t8n, ExecutionSpecsTransitionTool):
188189
# The expected output was generated with geth, instead of deleting
189190
# any info from this expected output, the fields not returned by
@@ -198,9 +199,11 @@ def test_evm_t8n(
198199
for i, _ in enumerate(expected.get("result")["receipts"]):
199200
del expected.get("result")["receipts"][i][key]
200201

201-
t8n_result = to_json(t8n_output.result)
202202
for i, _ in enumerate(expected.get("result")["rejected"]):
203203
del expected.get("result")["rejected"][i]["error"]
204204
del t8n_result["rejected"][i]["error"]
205205

206+
if "opcodeCount" in t8n_result:
207+
t8n_result.pop("opcodeCount")
208+
206209
assert t8n_result == expected.get("result")

packages/testing/src/execution_testing/client_clis/tests/test_transition_tool.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
LazyAllocFile,
2323
LazyAllocJson,
2424
LazyAllocStr,
25+
OpcodeCount,
2526
Result,
2627
TransitionToolInput,
2728
TransitionToolOutput,
@@ -436,3 +437,49 @@ def test_lazy_alloc_file_empty_object_yields_empty_alloc(
436437
lazy = LazyAllocFile(raw=alloc_path, _state_root=TEST_ALLOC_STATE_ROOT)
437438

438439
assert lazy.get() == Alloc.model_validate({})
440+
441+
442+
def _output_with_opcode_count(counts: dict) -> TransitionToolOutput:
443+
"""Build a minimal t8n output carrying the given opcode counts."""
444+
result = Result.model_validate(
445+
{
446+
"stateRoot": "0x" + "00" * 32,
447+
"txRoot": "0x" + "00" * 32,
448+
"receiptsRoot": "0x" + "00" * 32,
449+
"logsHash": "0x" + "00" * 32,
450+
"logsBloom": "0x" + "00" * 256,
451+
"receipts": [],
452+
"gasUsed": "0x0",
453+
}
454+
)
455+
result.opcode_count = OpcodeCount.model_validate(counts)
456+
return TransitionToolOutput(
457+
alloc=LazyAllocJson(
458+
raw=TEST_ALLOC.model_dump(), _state_root=TEST_ALLOC_STATE_ROOT
459+
),
460+
result=result,
461+
)
462+
463+
464+
def test_opcode_count_accumulation() -> None:
465+
"""
466+
`process_result` accumulates the per-test opcode count total and also
467+
records each call's (per-block) count separately.
468+
"""
469+
tool = ExecutionSpecsTransitionTool()
470+
tool.reset_opcode_count()
471+
472+
tool.process_result(_output_with_opcode_count({"PUSH1": 5, "SSTORE": 2}))
473+
tool.process_result(_output_with_opcode_count({"PUSH1": 3}))
474+
475+
assert tool.opcode_count == OpcodeCount.model_validate(
476+
{"PUSH1": 8, "SSTORE": 2}
477+
)
478+
assert tool.opcode_count_per_block == [
479+
OpcodeCount.model_validate({"PUSH1": 5, "SSTORE": 2}),
480+
OpcodeCount.model_validate({"PUSH1": 3}),
481+
]
482+
483+
tool.reset_opcode_count()
484+
assert tool.opcode_count == OpcodeCount({})
485+
assert tool.opcode_count_per_block == []

packages/testing/src/execution_testing/client_clis/transition_tool.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ class TransitionTool(EthereumCLI):
202202
debug_dump_dir: Path | None = None
203203
call_counter: int = 0
204204
opcode_count: OpcodeCount | None = None
205+
opcode_count_per_block: List[OpcodeCount] | None = None
205206

206207
supports_opcode_count: ClassVar[bool] = False
207208
supports_xdist: ClassVar[bool] = True
@@ -314,6 +315,7 @@ def reset_opcode_count(self) -> None:
314315
Reset the opcode count to zero.
315316
"""
316317
self.opcode_count = OpcodeCount({})
318+
self.opcode_count_per_block = []
317319

318320
@dataclass
319321
class TransitionToolData:
@@ -987,6 +989,8 @@ def process_result(
987989
and self.opcode_count is not None
988990
):
989991
self.opcode_count += result.result.opcode_count
992+
if self.opcode_count_per_block is not None:
993+
self.opcode_count_per_block.append(result.result.opcode_count)
990994
return result
991995

992996
def evaluate(

0 commit comments

Comments
 (0)