5
5
Tests for zkEVMs worst-cases scenarios.
6
6
"""
7
7
8
+ import math
9
+
8
10
import pytest
9
11
10
12
from ethereum_test_forks import Fork
17
19
Hash ,
18
20
Transaction ,
19
21
While ,
20
- compute_create_address ,
22
+ compute_create2_address ,
21
23
)
22
24
from ethereum_test_tools .vm .opcode import Opcodes as Op
23
25
33
35
XOR_TABLE = [Hash (i ).sha256 () for i in range (XOR_TABLE_SIZE )]
34
36
35
37
36
- # TODO: Parametrize for EOF
37
38
@pytest .mark .zkevm
38
39
@pytest .mark .parametrize (
39
40
"opcode" ,
@@ -95,12 +96,14 @@ def test_worst_bytecode_single_opcode(
95
96
)
96
97
+ Op .MSTORE (
97
98
0 ,
98
- Op .CREATE (
99
+ Op .CREATE2 (
99
100
value = 0 ,
100
101
offset = 0 ,
101
102
size = Op .MSIZE ,
103
+ salt = Op .SLOAD (0 ),
102
104
),
103
105
)
106
+ + Op .SSTORE (0 , Op .ADD (Op .SLOAD (0 ), 1 ))
104
107
+ Op .RETURN (0 , 32 )
105
108
)
106
109
factory_address = pre .deploy_contract (code = factory_code )
@@ -115,9 +118,18 @@ def test_worst_bytecode_single_opcode(
115
118
116
119
gas_costs = fork .gas_costs ()
117
120
intrinsic_gas_cost_calc = fork .transaction_intrinsic_cost_calculator ()
118
- max_number_of_contract_calls = (OPCODE_GAS_LIMIT - intrinsic_gas_cost_calc ()) // (
119
- gas_costs .G_VERY_LOW + gas_costs .G_BASE + gas_costs .G_COLD_ACCOUNT_ACCESS
121
+ loop_cost = (
122
+ gas_costs .G_KECCAK_256 # KECCAK static cost
123
+ + math .ceil (85 / 32 ) * gas_costs .G_KECCAK_256_WORD # KECCAK dynamic cost for CREATE2
124
+ + gas_costs .G_VERY_LOW * 3 # ~MSTOREs+ADDs
125
+ + gas_costs .G_COLD_ACCOUNT_ACCESS # EXTCODESIZE
126
+ + 30 # ~Gluing opcodes
120
127
)
128
+ max_number_of_contract_calls = (
129
+ # Base available gas = GAS_LIMIT - intrinsic - (out of loop MSTOREs)
130
+ OPCODE_GAS_LIMIT - intrinsic_gas_cost_calc () - gas_costs .G_VERY_LOW * 4
131
+ ) // loop_cost
132
+
121
133
total_contracts_to_deploy = max_number_of_contract_calls
122
134
approximate_gas_per_deployment = 4_970_000 # Obtained from evm tracing
123
135
contracts_deployed_per_tx = BLOCK_GAS_LIMIT // approximate_gas_per_deployment
@@ -144,16 +156,27 @@ def generate_deploy_tx(contracts_to_deploy: int):
144
156
post = {}
145
157
deployed_contract_addresses = []
146
158
for i in range (total_contracts_to_deploy ):
147
- deployed_contract_address = compute_create_address (
159
+ deployed_contract_address = compute_create2_address (
148
160
address = factory_address ,
149
- nonce = i + 1 ,
161
+ salt = i ,
162
+ initcode = initcode ,
150
163
)
151
164
post [deployed_contract_address ] = Account (nonce = 1 )
152
165
deployed_contract_addresses .append (deployed_contract_address )
153
166
154
167
opcode_code = (
155
- sum (Op .POP (opcode (address = address )) for address in deployed_contract_addresses ) + Op .STOP
168
+ # Setup memory for later CREATE2 address generation loop.
169
+ Op .MSTORE (0 , factory_address )
170
+ + Op .MSTORE8 (32 - 20 - 1 , 0xFF ) # 0xFF prefix byte
171
+ + Op .MSTORE (32 , 0 )
172
+ + Op .MSTORE (64 , initcode .keccak256 ())
173
+ # Main loop
174
+ + While (
175
+ body = Op .POP (Op .EXTCODESIZE (Op .SHA3 (32 - 20 - 1 , 85 )))
176
+ + Op .MSTORE (32 , Op .ADD (Op .MLOAD (32 ), 1 )),
177
+ )
156
178
)
179
+
157
180
if len (opcode_code ) > MAX_CONTRACT_SIZE :
158
181
# TODO: A workaround could be to split the opcode code into multiple contracts
159
182
# and call them in sequence.
0 commit comments