Skip to content

Commit fbbabc6

Browse files
feat(zkevm): add log opcode worst case (#1745)
* feat(zkevm): add log opcode worst case * fix(zkevm): logic issue * refactor(zkevm): update test parameters for log opcodes * refactor(zkevm): move test path for worst log opcodes * refactor(zkevm): enhance test with additional parameters * refactor(zkevm): update test parametrization * Apply suggestions from code review --------- Co-authored-by: Mario Vega <marioevz@gmail.com>
1 parent df014bf commit fbbabc6

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed

tests/zkevm/test_worst_opcode.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""
2+
abstract: Tests zkEVMs worst-case opcode scenarios.
3+
Tests zkEVMs worst-case opcode scenarios.
4+
5+
Tests running worst-case opcodes scenarios for zkEVMs.
6+
"""
7+
8+
import pytest
9+
10+
from ethereum_test_forks import Fork
11+
from ethereum_test_tools import (
12+
Alloc,
13+
Bytecode,
14+
Environment,
15+
StateTestFiller,
16+
Transaction,
17+
)
18+
from ethereum_test_tools.vm.opcode import Opcodes as Op
19+
from ethereum_test_vm.opcode import Opcode
20+
21+
from .helpers import code_loop_precompile_call
22+
23+
24+
@pytest.mark.parametrize(
25+
"opcode",
26+
[
27+
pytest.param(Op.LOG0, id="log0"),
28+
pytest.param(Op.LOG1, id="log1"),
29+
pytest.param(Op.LOG2, id="log2"),
30+
pytest.param(Op.LOG3, id="log3"),
31+
pytest.param(Op.LOG4, id="log4"),
32+
],
33+
)
34+
@pytest.mark.parametrize(
35+
"size,non_zero_data",
36+
[
37+
pytest.param(0, False, id="0_bytes_data"),
38+
pytest.param(1024 * 1024, False, id="1_MiB_zeros_data"), # 1 MiB
39+
pytest.param(1024 * 1024, True, id="1_MiB_non_zero_data"), # 1 MiB
40+
],
41+
)
42+
@pytest.mark.parametrize(
43+
"zeros_topic", [pytest.param(True, id="zeros_topic"), pytest.param(False, id="non_zero_topic")]
44+
)
45+
@pytest.mark.parametrize("fixed_offset", [True, False])
46+
def test_worst_log_opcodes(
47+
state_test: StateTestFiller,
48+
pre: Alloc,
49+
fork: Fork,
50+
opcode: Opcode,
51+
zeros_topic: bool,
52+
size: int,
53+
fixed_offset: bool,
54+
non_zero_data: bool,
55+
):
56+
"""Test running a block with as many LOG opcodes as possible."""
57+
env = Environment()
58+
max_code_size = fork.max_code_size()
59+
60+
calldata = Bytecode()
61+
62+
# For non-zero data, load into memory.
63+
if non_zero_data:
64+
calldata += Op.CODECOPY(dest_offset=0, offset=0, size=Op.CODESIZE)
65+
66+
# Push the size value onto the stack and access it using the DUP opcode.
67+
calldata += Op.PUSH3(size)
68+
69+
# For non-zeros topic, push a non-zero value for topic.
70+
calldata += Op.PUSH0 if zeros_topic else Op.PUSH32(2**256 - 1)
71+
72+
topic_count = len(opcode.kwargs or []) - 2
73+
offset = Op.PUSH0 if fixed_offset else Op.MOD(Op.GAS, 7)
74+
75+
# Calculate the appropriate DUP opcode based on topic count
76+
# 0 topics -> DUP1, 1 topic -> DUP2, N topics -> DUP(N+1)
77+
size_op = getattr(Op, f"DUP{topic_count + 2}")
78+
79+
code_sequence = Op.DUP1 * topic_count + size_op + offset + opcode
80+
81+
code = code_loop_precompile_call(calldata, code_sequence, fork)
82+
assert len(code) <= max_code_size
83+
84+
code_address = pre.deploy_contract(code=code)
85+
86+
tx = Transaction(
87+
to=code_address,
88+
gas_limit=env.gas_limit,
89+
sender=pre.fund_eoa(),
90+
)
91+
92+
state_test(
93+
env=env,
94+
pre=pre,
95+
post={},
96+
tx=tx,
97+
)

0 commit comments

Comments
 (0)