Skip to content

Commit cc2a29f

Browse files
refactor: compute scenario folder
1 parent 6f6e190 commit cc2a29f

File tree

2 files changed

+594
-0
lines changed

2 files changed

+594
-0
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""Benchmark mixed operations."""
2+
3+
import pytest
4+
from ethereum_test_forks import Fork
5+
from ethereum_test_tools import (
6+
Account,
7+
Address,
8+
BenchmarkTestFiller,
9+
Bytecode,
10+
JumpLoopGenerator,
11+
)
12+
from ethereum_test_types import Alloc
13+
from ethereum_test_vm import Opcodes as Op
14+
15+
16+
@pytest.mark.parametrize(
17+
"pattern",
18+
[
19+
Op.STOP,
20+
Op.JUMPDEST,
21+
Op.PUSH1[bytes(Op.JUMPDEST)],
22+
Op.PUSH2[bytes(Op.JUMPDEST + Op.JUMPDEST)],
23+
Op.PUSH1[bytes(Op.JUMPDEST)] + Op.JUMPDEST,
24+
Op.PUSH2[bytes(Op.JUMPDEST + Op.JUMPDEST)] + Op.JUMPDEST,
25+
],
26+
ids=lambda x: x.hex(),
27+
)
28+
def test_jumpdest_analysis(
29+
benchmark_test: BenchmarkTestFiller,
30+
fork: Fork,
31+
pattern: Bytecode,
32+
) -> None:
33+
"""
34+
Test the jumpdest analysis performance of the initcode.
35+
36+
This benchmark places a very long initcode in the memory and then invoke
37+
CREATE instructions with this initcode up to the block gas limit. The
38+
initcode itself has minimal execution time but forces the EVM to perform
39+
the full jumpdest analysis on the parametrized byte pattern. The initicode
40+
is modified by mixing-in the returned create address between CREATE
41+
invocations to prevent caching.
42+
"""
43+
initcode_size = fork.max_initcode_size()
44+
45+
# Expand the initcode pattern to the transaction data so it can be used in
46+
# CALLDATACOPY in the main contract. TODO: tune the tx_data_len param.
47+
tx_data_len = 1024
48+
tx_data = pattern * (tx_data_len // len(pattern))
49+
tx_data += (tx_data_len - len(tx_data)) * bytes(Op.JUMPDEST)
50+
assert len(tx_data) == tx_data_len
51+
assert initcode_size % len(tx_data) == 0
52+
53+
# Prepare the initcode in memory.
54+
code_prepare_initcode = sum(
55+
(
56+
Op.CALLDATACOPY(
57+
dest_offset=i * len(tx_data), offset=0, size=Op.CALLDATASIZE
58+
)
59+
for i in range(initcode_size // len(tx_data))
60+
),
61+
Bytecode(),
62+
)
63+
64+
# At the start of the initcode execution, jump to the last opcode.
65+
# This forces EVM to do the full jumpdest analysis.
66+
initcode_prefix = Op.JUMP(initcode_size - 1)
67+
code_prepare_initcode += Op.MSTORE(
68+
0, Op.PUSH32[bytes(initcode_prefix).ljust(32, bytes(Op.JUMPDEST))]
69+
)
70+
71+
# Make sure the last opcode in the initcode is JUMPDEST.
72+
code_prepare_initcode += Op.MSTORE(
73+
initcode_size - 32, Op.PUSH32[bytes(Op.JUMPDEST) * 32]
74+
)
75+
76+
attack_block = (
77+
Op.PUSH1[len(initcode_prefix)]
78+
+ Op.MSTORE
79+
+ Op.CREATE(value=Op.PUSH0, offset=Op.PUSH0, size=Op.MSIZE)
80+
)
81+
82+
setup = code_prepare_initcode + Op.PUSH0
83+
84+
benchmark_test(
85+
code_generator=JumpLoopGenerator(
86+
setup=setup,
87+
attack_block=attack_block,
88+
tx_kwargs={"data": tx_data},
89+
),
90+
)
91+
92+
93+
@pytest.mark.parametrize(
94+
"opcode",
95+
[
96+
Op.BALANCE,
97+
Op.EXTCODESIZE,
98+
Op.EXTCODEHASH,
99+
Op.CALL,
100+
Op.CALLCODE,
101+
Op.DELEGATECALL,
102+
Op.STATICCALL,
103+
],
104+
)
105+
@pytest.mark.parametrize(
106+
"absent_target",
107+
[
108+
True,
109+
False,
110+
],
111+
)
112+
def test_worst_address_state_warm(
113+
benchmark_test: BenchmarkTestFiller,
114+
pre: Alloc,
115+
opcode: Op,
116+
absent_target: bool,
117+
) -> None:
118+
"""
119+
Test running a block with as many stateful opcodes doing warm access
120+
for an account.
121+
"""
122+
# Setup
123+
target_addr = Address(100_000)
124+
post = {}
125+
if not absent_target:
126+
code = Op.STOP + Op.JUMPDEST * 100
127+
target_addr = pre.deploy_contract(balance=100, code=code)
128+
post[target_addr] = Account(balance=100, code=code)
129+
130+
# Execution
131+
setup = Op.MSTORE(0, target_addr)
132+
attack_block = Op.POP(opcode(address=Op.MLOAD(0)))
133+
benchmark_test(
134+
post=post,
135+
code_generator=JumpLoopGenerator(
136+
setup=setup, attack_block=attack_block
137+
),
138+
)

0 commit comments

Comments
 (0)