Skip to content

Commit e0da1d9

Browse files
committed
Test some jump scenarios + baseline; log resource utilization[
1 parent fce88dd commit e0da1d9

File tree

2 files changed

+212
-0
lines changed

2 files changed

+212
-0
lines changed

tests/zkevm/test_worst_compute.py

+204
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,207 @@ def test_worst_keccak(
104104
post={},
105105
blocks=[Block(txs=[tx])],
106106
)
107+
108+
109+
@pytest.mark.zkevm
110+
@pytest.mark.valid_from("Cancun")
111+
@pytest.mark.parametrize(
112+
"gas_limit",
113+
[36_000_000],
114+
)
115+
def run_jump_analysis_test(
116+
blockchain_test: BlockchainTestFiller,
117+
pre: Alloc,
118+
fork: Fork,
119+
gas_limit: int,
120+
opcodes_pattern,
121+
iteration_gas_cost: int,
122+
description: str = "Opcode test",
123+
):
124+
"""
125+
Generate test for worst-case compute scenario for a given opcode pattern.
126+
"""
127+
env = Environment(gas_limit=gas_limit)
128+
129+
# Intrinsic gas cost is paid once
130+
intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
131+
available_gas = gas_limit - intrinsic_gas_calculator()
132+
133+
# Each iteration adds the bytes of the opcode pattern
134+
bytes_per_iter = len(opcodes_pattern)
135+
136+
# Calculate max iterations based on code size and gas constraints
137+
max_iters_by_size = MAX_CODE_SIZE // bytes_per_iter
138+
max_iters_by_gas = (
139+
available_gas // iteration_gas_cost if iteration_gas_cost > 0 else float("inf")
140+
)
141+
num_iters = min(max_iters_by_size, max_iters_by_gas)
142+
143+
# Create code with repeated pattern by concatenating the pattern num_iters times
144+
code = opcodes_pattern * num_iters
145+
146+
# Calculate utilization percentages
147+
code_size_pct = (len(code) / MAX_CODE_SIZE) * 100
148+
gas_used = num_iters * iteration_gas_cost
149+
gas_pct = (gas_used / available_gas) * 100 if available_gas > 0 else None
150+
151+
# Only write to the summary file if this is the first time we've seen this description
152+
# This prevents duplication because tests are run for multiple blockchain test implementations
153+
if description not in LOGGED_DESCRIPTIONS:
154+
with open(SUMMARY_FILE, "a") as f:
155+
# Format bytecode and gas info with aligned parentheses
156+
bytecode_values = f"{len(code)}/{MAX_CODE_SIZE}"
157+
gas_values = f"{gas_used}/{available_gas}"
158+
159+
# Align the parentheses by padding the values section
160+
f.write(f"{description:<30} {bytecode_values:>15} ({code_size_pct:6.2f}%) {gas_values:>20} ({gas_pct:6.2f}%) {bytes_per_iter:>4} {num_iters:>10}\n")
161+
162+
LOGGED_DESCRIPTIONS.add(description)
163+
164+
if len(code) > MAX_CODE_SIZE:
165+
# Must never happen, but keep it as a sanity check.
166+
raise ValueError(f"Code size {len(code)} exceeds maximum code size {MAX_CODE_SIZE}")
167+
168+
code_address = pre.deploy_contract(code=code)
169+
170+
tx = Transaction(
171+
to=code_address,
172+
gas_limit=gas_limit,
173+
gas_price=10,
174+
sender=pre.fund_eoa(),
175+
data=[],
176+
value=0,
177+
)
178+
179+
return blockchain_test(
180+
env=env,
181+
pre=pre,
182+
post={},
183+
blocks=[Block(txs=[tx])],
184+
)
185+
186+
187+
@pytest.mark.zkevm
188+
@pytest.mark.valid_from("Cancun")
189+
@pytest.mark.parametrize(
190+
"gas_limit",
191+
[
192+
36_000_000,
193+
],
194+
)
195+
def test_jumpdest_only(
196+
blockchain_test: BlockchainTestFiller,
197+
pre: Alloc,
198+
fork: Fork,
199+
gas_limit: int,
200+
):
201+
"""Test with only JUMPDEST opcodes"""
202+
gsc = fork.gas_costs()
203+
iteration_gas_cost = gsc.G_JUMPDEST
204+
205+
# Use Op.JUMPDEST instead of raw bytes
206+
opcodes_pattern = Op.JUMPDEST
207+
208+
run_jump_analysis_test(
209+
blockchain_test=blockchain_test,
210+
pre=pre,
211+
fork=fork,
212+
gas_limit=gas_limit,
213+
opcodes_pattern=opcodes_pattern,
214+
iteration_gas_cost=iteration_gas_cost,
215+
description="JUMPDEST only",
216+
)
217+
218+
219+
@pytest.mark.zkevm
220+
@pytest.mark.valid_from("Cancun")
221+
@pytest.mark.parametrize(
222+
"gas_limit",
223+
[
224+
36_000_000,
225+
],
226+
)
227+
def test_jumpdest_jump(
228+
blockchain_test: BlockchainTestFiller,
229+
pre: Alloc,
230+
fork: Fork,
231+
gas_limit: int,
232+
):
233+
"""Test with JUMPDEST and JUMP opcodes"""
234+
gsc = fork.gas_costs()
235+
iteration_gas_cost = gsc.G_JUMPDEST + gsc.G_MID
236+
237+
# Use Op.JUMPDEST + Op.JUMP instead of raw bytes
238+
opcodes_pattern = Op.JUMPDEST + Op.JUMP
239+
240+
run_jump_analysis_test(
241+
blockchain_test=blockchain_test,
242+
pre=pre,
243+
fork=fork,
244+
gas_limit=gas_limit,
245+
opcodes_pattern=opcodes_pattern,
246+
iteration_gas_cost=iteration_gas_cost,
247+
description="JUMPDEST + JUMP",
248+
)
249+
250+
251+
@pytest.mark.zkevm
252+
@pytest.mark.valid_from("Cancun")
253+
@pytest.mark.parametrize(
254+
"gas_limit",
255+
[
256+
36_000_000,
257+
],
258+
)
259+
def test_jumpdest_push0_pop_jump(
260+
blockchain_test: BlockchainTestFiller,
261+
pre: Alloc,
262+
fork: Fork,
263+
gas_limit: int,
264+
):
265+
"""Test with JUMPDEST, PUSH0, POP, and JUMP opcodes"""
266+
gsc = fork.gas_costs()
267+
iteration_gas_cost = gsc.G_JUMPDEST + gsc.G_VERY_LOW + gsc.G_BASE + gsc.G_MID
268+
269+
# Use Op.JUMPDEST + Op.PUSH0 + Op.POP + Op.JUMP instead of raw bytes
270+
opcodes_pattern = Op.JUMPDEST + Op.PUSH0 + Op.POP + Op.JUMP
271+
272+
run_jump_analysis_test(
273+
blockchain_test=blockchain_test,
274+
pre=pre,
275+
fork=fork,
276+
gas_limit=gas_limit,
277+
opcodes_pattern=opcodes_pattern,
278+
iteration_gas_cost=iteration_gas_cost,
279+
description="JUMPDEST + PUSH0 + POP + JUMP",
280+
)
281+
282+
283+
@pytest.mark.zkevm
284+
@pytest.mark.valid_from("Cancun")
285+
@pytest.mark.parametrize(
286+
"gas_limit",
287+
[36_000_000],
288+
)
289+
def test_push0_pop_no_jump(
290+
blockchain_test: BlockchainTestFiller,
291+
pre: Alloc,
292+
fork: Fork,
293+
gas_limit: int,
294+
):
295+
"""Test with just PUSH0 and POP opcodes"""
296+
gsc = fork.gas_costs()
297+
iteration_gas_cost = gsc.G_VERY_LOW + gsc.G_BASE
298+
299+
# Use Op.PUSH0 + Op.POP instead of raw bytes
300+
opcodes_pattern = Op.PUSH0 + Op.POP
301+
302+
run_jump_analysis_test(
303+
blockchain_test=blockchain_test,
304+
pre=pre,
305+
fork=fork,
306+
gas_limit=gas_limit,
307+
opcodes_pattern=opcodes_pattern,
308+
iteration_gas_cost=iteration_gas_cost,
309+
description="PUSH0 + POP",
310+
)

tests/zkevm/utilization_summary.log

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
===== UTILIZATION SUMMARY =====
2+
3+
TEST PATTERN BYTECODE GAS SIZE ITERATIONS
4+
----------------------------------------------------------------------------------------------------
5+
JUMPDEST only 24576/24576 (100.00%) 24576/35979000 ( 0.07%) 1 24576
6+
JUMPDEST + JUMP 24576/24576 (100.00%) 110592/35979000 ( 0.31%) 2 12288
7+
JUMPDEST + PUSH0 + POP + JUMP 24576/24576 (100.00%) 86016/35979000 ( 0.24%) 4 6144
8+
PUSH0 + POP 24576/24576 (100.00%) 61440/35979000 ( 0.17%) 2 12288

0 commit comments

Comments
 (0)