55Tests for zkEVMs worst-cases scenarios.
66"""
77
8+ import math
9+
810import pytest
911
1012from ethereum_test_forks import Fork
1719 Hash ,
1820 Transaction ,
1921 While ,
20- compute_create_address ,
22+ compute_create2_address ,
2123)
2224from ethereum_test_tools .vm .opcode import Opcodes as Op
2325
3335XOR_TABLE = [Hash (i ).sha256 () for i in range (XOR_TABLE_SIZE )]
3436
3537
36- # TODO: Parametrize for EOF
3738@pytest .mark .zkevm
3839@pytest .mark .parametrize (
3940 "opcode" ,
@@ -95,12 +96,14 @@ def test_worst_bytecode_single_opcode(
9596 )
9697 + Op .MSTORE (
9798 0 ,
98- Op .CREATE (
99+ Op .CREATE2 (
99100 value = 0 ,
100101 offset = 0 ,
101102 size = Op .MSIZE ,
103+ salt = Op .SLOAD (0 ),
102104 ),
103105 )
106+ + Op .SSTORE (0 , Op .ADD (Op .SLOAD (0 ), 1 ))
104107 + Op .RETURN (0 , 32 )
105108 )
106109 factory_address = pre .deploy_contract (code = factory_code )
@@ -115,9 +118,18 @@ def test_worst_bytecode_single_opcode(
115118
116119 gas_costs = fork .gas_costs ()
117120 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
120127 )
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+
121133 total_contracts_to_deploy = max_number_of_contract_calls
122134 approximate_gas_per_deployment = 4_970_000 # Obtained from evm tracing
123135 contracts_deployed_per_tx = BLOCK_GAS_LIMIT // approximate_gas_per_deployment
@@ -144,16 +156,27 @@ def generate_deploy_tx(contracts_to_deploy: int):
144156 post = {}
145157 deployed_contract_addresses = []
146158 for i in range (total_contracts_to_deploy ):
147- deployed_contract_address = compute_create_address (
159+ deployed_contract_address = compute_create2_address (
148160 address = factory_address ,
149- nonce = i + 1 ,
161+ salt = i ,
162+ initcode = initcode ,
150163 )
151164 post [deployed_contract_address ] = Account (nonce = 1 )
152165 deployed_contract_addresses .append (deployed_contract_address )
153166
154167 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+ )
156178 )
179+
157180 if len (opcode_code ) > MAX_CONTRACT_SIZE :
158181 # TODO: A workaround could be to split the opcode code into multiple contracts
159182 # and call them in sequence.
0 commit comments