Skip to content

Commit 01e4d6b

Browse files
refactor: optimize benchmark cases
1 parent b5c2255 commit 01e4d6b

File tree

9 files changed

+139
-117
lines changed

9 files changed

+139
-117
lines changed

tests/benchmark/compute/instruction/test_account_query.py

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""
55

66
import math
7+
from typing import Dict
78

89
import pytest
910
from execution_testing import (
@@ -24,6 +25,7 @@
2425
While,
2526
compute_create2_address,
2627
)
28+
from execution_testing.base_types.conversions import NumberConvertible
2729

2830
from tests.benchmark.compute.helpers import (
2931
XOR_TABLE,
@@ -72,7 +74,6 @@ def test_codesize(
7274
)
7375
def test_codecopy(
7476
benchmark_test: BenchmarkTestFiller,
75-
pre: Alloc,
7677
fork: Fork,
7778
max_code_size_ratio: float,
7879
fixed_src_dst: bool,
@@ -86,26 +87,12 @@ def test_codecopy(
8687
src_dst = 0 if fixed_src_dst else Op.MOD(Op.GAS, 7)
8788
attack_block = Op.CODECOPY(src_dst, src_dst, Op.DUP1) # DUP1 copies size.
8889

89-
code = JumpLoopGenerator(
90-
setup=setup, attack_block=attack_block
91-
).generate_repeated_code(
92-
repeated_code=attack_block, setup=setup, fork=fork
93-
)
94-
95-
# Pad the generated code to ensure the contract size matches the maximum
96-
# The content of the padding bytes is arbitrary.
97-
code += Op.INVALID * (max_code_size - len(code))
98-
assert len(code) == max_code_size, (
99-
f"Code size {len(code)} is not equal to max code size {max_code_size}."
100-
)
101-
102-
tx = Transaction(
103-
to=pre.deploy_contract(code=code),
104-
sender=pre.fund_eoa(),
90+
benchmark_test(
91+
code_generator=JumpLoopGenerator(
92+
setup=setup, attack_block=attack_block
93+
)
10594
)
10695

107-
benchmark_test(tx=tx)
108-
10996

11097
@pytest.mark.parametrize(
11198
"opcode",
@@ -361,7 +348,21 @@ def test_extcodecopy_warm(
361348
],
362349
)
363350
@pytest.mark.parametrize(
364-
"absent_target",
351+
"empty_account",
352+
[
353+
True,
354+
False,
355+
],
356+
)
357+
@pytest.mark.parametrize(
358+
"initial_balance",
359+
[
360+
True,
361+
False,
362+
],
363+
)
364+
@pytest.mark.parametrize(
365+
"initial_storage",
365366
[
366367
True,
367368
False,
@@ -371,27 +372,40 @@ def test_ext_account_query_warm(
371372
benchmark_test: BenchmarkTestFiller,
372373
pre: Alloc,
373374
opcode: Op,
374-
absent_target: bool,
375+
empty_account: bool,
376+
initial_balance: bool,
377+
initial_storage: bool,
375378
) -> None:
376379
"""
377380
Test running a block with as many stateful opcodes doing warm access
378381
for an account.
379382
"""
380383
# Setup
381-
target_addr = pre.empty_account()
384+
target_addr = pre.empty_account() if initial_balance else pre.fund_eoa()
382385
post = {}
383-
if not absent_target:
386+
if not empty_account:
384387
code = Op.STOP + Op.JUMPDEST * 100
385-
target_addr = pre.deploy_contract(balance=100, code=code)
386-
post[target_addr] = Account(balance=100, code=code)
387388

388-
# Execution
389-
setup = Op.MSTORE(0, target_addr)
390-
attack_block = Op.POP(opcode(address=Op.MLOAD(0)))
389+
storage: Dict[NumberConvertible, NumberConvertible] = (
390+
{0: 0x1337} if initial_storage else {0: 0}
391+
)
392+
target_addr = pre.deploy_contract(
393+
balance=initial_balance,
394+
code=code,
395+
storage=storage,
396+
)
397+
398+
post[target_addr] = Account(
399+
balance=initial_balance,
400+
code=code,
401+
storage=storage,
402+
)
403+
391404
benchmark_test(
392405
post=post,
393406
code_generator=JumpLoopGenerator(
394-
setup=setup, attack_block=attack_block
407+
setup=Op.MSTORE(0, target_addr),
408+
attack_block=Op.POP(opcode(address=Op.MLOAD(0))),
395409
),
396410
)
397411

tests/benchmark/compute/instruction/test_block_context.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,29 @@ def test_block_context_ops(
3636
)
3737

3838

39+
@pytest.mark.parametrize(
40+
"index",
41+
[
42+
0,
43+
1,
44+
256,
45+
257,
46+
None,
47+
],
48+
)
3949
def test_blockhash(
4050
benchmark_test: BenchmarkTestFiller,
51+
index: int | None,
4152
) -> None:
4253
"""Benchmark BLOCKHASH instruction accessing oldest allowed block."""
4354
# Create 256 dummy blocks to fill the blockhash window.
4455
blocks = [Block()] * 256
4556

57+
block_number = Op.AND(Op.GAS, 0xFF) if index is None else index
58+
4659
benchmark_test(
4760
setup_blocks=blocks,
48-
code_generator=ExtCallGenerator(attack_block=Op.BLOCKHASH(1)),
61+
code_generator=ExtCallGenerator(
62+
attack_block=Op.BLOCKHASH(block_number)
63+
),
4964
)

tests/benchmark/compute/instruction/test_call_context.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def test_calldatacopy(
149149
size: int,
150150
fixed_src_dst: bool,
151151
non_zero_data: bool,
152-
gas_benchmark_value: int,
152+
tx_gas_limit: int,
153153
) -> None:
154154
"""Benchmark CALLDATACOPY instruction."""
155155
if size == 0 and non_zero_data:
@@ -162,7 +162,7 @@ def test_calldatacopy(
162162

163163
intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
164164
min_gas = intrinsic_gas_calculator(calldata=data)
165-
if min_gas > gas_benchmark_value:
165+
if min_gas > tx_gas_limit:
166166
pytest.skip(
167167
"Minimum gas required for calldata ({min_gas}) is greater "
168168
"than the gas limit"
@@ -210,7 +210,6 @@ def test_calldatacopy(
210210

211211
tx = Transaction(
212212
to=tx_target,
213-
gas_limit=gas_benchmark_value,
214213
data=data,
215214
sender=pre.fund_eoa(),
216215
)
@@ -311,11 +310,6 @@ def test_returndatacopy(
311310
)
312311
dst = 0 if fixed_dst else Op.MOD(Op.GAS, 7)
313312

314-
# We create the contract that will be doing the RETURNDATACOPY multiple
315-
# times.
316-
returndata_gen = (
317-
Op.STATICCALL(address=helper_contract) if size > 0 else Bytecode()
318-
)
319313
attack_block = Op.RETURNDATACOPY(dst, Op.PUSH0, Op.RETURNDATASIZE)
320314

321315
benchmark_test(

tests/benchmark/compute/instruction/test_control_flow.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ def test_gas_op(
2222
)
2323

2424

25+
def test_pc_op(
26+
benchmark_test: BenchmarkTestFiller,
27+
) -> None:
28+
"""Benchmark PC instruction."""
29+
benchmark_test(
30+
code_generator=ExtCallGenerator(attack_block=Op.PC),
31+
)
32+
33+
2534
def test_jumps(
2635
benchmark_test: BenchmarkTestFiller,
2736
pre: Alloc,

tests/benchmark/compute/instruction/test_keccak.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import math
44

5+
import pytest
56
from execution_testing import (
67
BenchmarkTestFiller,
78
Fork,
@@ -15,15 +16,15 @@
1516
KECCAK_RATE = 136
1617

1718

18-
def test_keccak(
19+
def test_keccak_max_permutations(
1920
benchmark_test: BenchmarkTestFiller,
2021
fork: Fork,
21-
gas_benchmark_value: int,
22+
tx_gas_limit: int,
2223
) -> None:
23-
"""Benchmark KECCAK256 instruction."""
24+
"""Benchmark KECCAK256 instruction to maximize permutations per block."""
2425
# Intrinsic gas cost is paid once.
2526
intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
26-
available_gas = gas_benchmark_value - intrinsic_gas_calculator()
27+
available_gas = tx_gas_limit - intrinsic_gas_calculator()
2728

2829
gsc = fork.gas_costs()
2930
mem_exp_gas_calculator = fork.memory_expansion_gas_calculator()
@@ -60,7 +61,24 @@ def test_keccak(
6061

6162
benchmark_test(
6263
code_generator=JumpLoopGenerator(
63-
setup=Op.PUSH20[optimal_input_length],
64+
setup=Op.PUSH20(optimal_input_length),
6465
attack_block=Op.POP(Op.SHA3(Op.PUSH0, Op.DUP1)),
6566
),
6667
)
68+
69+
70+
@pytest.mark.parametrize("mem_alloc", [b"", b"ff", b"ff" * 32])
71+
@pytest.mark.parametrize("offset", [0, 31, 1024])
72+
def test_keccak(
73+
benchmark_test: BenchmarkTestFiller,
74+
offset: int,
75+
mem_alloc: bytes,
76+
) -> None:
77+
"""Benchmark KECCAK256 instruction with diff input data and offsets."""
78+
benchmark_test(
79+
code_generator=JumpLoopGenerator(
80+
setup=Op.CALLDATACOPY(offset, Op.PUSH0, Op.CALLDATASIZE),
81+
attack_block=Op.POP(Op.SHA3(offset, Op.CALLDATASIZE)),
82+
tx_kwargs={"data": mem_alloc},
83+
),
84+
)

tests/benchmark/compute/instruction/test_memory.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,10 @@ def test_msize(
1818
benchmark_test: BenchmarkTestFiller,
1919
mem_size: int,
2020
) -> None:
21-
"""
22-
Benchmark MSIZE instruction.
23-
24-
- mem_size: by how much the memory is expanded.
25-
"""
21+
"""Benchmark MSIZE instruction."""
2622
benchmark_test(
2723
code_generator=ExtCallGenerator(
28-
setup=Op.MLOAD(Op.SELFBALANCE) + Op.POP,
24+
setup=Op.POP(Op.MLOAD(Op.SELFBALANCE)),
2925
attack_block=Op.MSIZE,
3026
contract_balance=mem_size,
3127
),
@@ -99,6 +95,6 @@ def test_mcopy(
9995
)
10096
benchmark_test(
10197
code_generator=JumpLoopGenerator(
102-
setup=mem_touch, attack_block=attack_block, cleanup=mem_touch
98+
attack_block=attack_block, cleanup=mem_touch
10399
),
104100
)

tests/benchmark/compute/instruction/test_storage.py

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Block,
88
Bytecode,
99
Environment,
10+
ExtCallGenerator,
1011
Fork,
1112
JumpLoopGenerator,
1213
Op,
@@ -33,36 +34,25 @@ def test_tload(
3334
val_mut: bool,
3435
) -> None:
3536
"""Benchmark TLOAD instruction."""
36-
start_key = 41
37-
code_key_mut = Bytecode()
38-
code_val_mut = Bytecode()
3937
setup = Bytecode()
4038
if key_mut and val_mut:
41-
setup = Op.PUSH1(start_key)
42-
attack_block = Op.POP(Op.TLOAD(Op.DUP1))
43-
code_key_mut = Op.POP + Op.GAS
44-
code_val_mut = Op.TSTORE(Op.DUP2, Op.GAS)
39+
setup = Op.GAS + Op.TSTORE(Op.DUP2, Op.GAS)
40+
attack_block = Op.TLOAD(Op.DUP1)
4541
if key_mut and not val_mut:
46-
attack_block = Op.POP(Op.TLOAD(Op.GAS))
42+
attack_block = Op.TLOAD(Op.GAS)
4743
if not key_mut and val_mut:
48-
attack_block = Op.POP(Op.TLOAD(Op.CALLVALUE))
49-
code_val_mut = Op.TSTORE(
50-
Op.CALLVALUE, Op.GAS
51-
) # CALLVALUE configured in the tx
44+
setup = Op.TSTORE(Op.CALLVALUE, Op.GAS)
45+
attack_block = Op.TLOAD(Op.CALLVALUE)
5246
if not key_mut and not val_mut:
53-
attack_block = Op.POP(Op.TLOAD(Op.CALLVALUE))
47+
attack_block = Op.TLOAD(Op.CALLVALUE)
5448

55-
cleanup = code_key_mut + code_val_mut
56-
tx_value = start_key if not key_mut and val_mut else 0
49+
tx_value = 42 if not key_mut and val_mut else 0
5750

5851
benchmark_test(
59-
code_generator=JumpLoopGenerator(
52+
code_generator=ExtCallGenerator(
6053
setup=setup,
6154
attack_block=attack_block,
62-
cleanup=cleanup,
63-
tx_kwargs={
64-
"value": tx_value,
65-
},
55+
contract_balance=tx_value,
6656
),
6757
)
6858

0 commit comments

Comments
 (0)