@@ -204,3 +204,93 @@ def test_worst_bytecode_single_opcode(
204
204
],
205
205
exclude_full_post_state_in_output = True ,
206
206
)
207
+
208
+
209
+ @pytest .mark .valid_from ("Paris" )
210
+ @pytest .mark .parametrize ("initcode_size" , [0xC000 ])
211
+ @pytest .mark .parametrize (
212
+ "pattern" ,
213
+ [
214
+ Op .STOP ,
215
+ Op .JUMPDEST ,
216
+ Op .PUSH1 [Op .JUMPDEST ],
217
+ Op .PUSH2 [Op .JUMPDEST + Op .JUMPDEST ],
218
+ Op .PUSH1 [Op .JUMPDEST ] + Op .JUMPDEST ,
219
+ Op .PUSH2 [Op .JUMPDEST + Op .JUMPDEST ] + Op .JUMPDEST ,
220
+ ],
221
+ ids = lambda x : x .hex (),
222
+ )
223
+ def test_worst_initcode_jumpdest_analysis (
224
+ blockchain_test : BlockchainTestFiller ,
225
+ fork : Fork ,
226
+ pre : Alloc ,
227
+ initcode_size : int ,
228
+ pattern : Bytecode ,
229
+ ):
230
+ """Test the jumpdest analysis performance of the initcode."""
231
+ # Expand the initcode pattern to the transaction data so it can be used in CALLDATACOPY
232
+ # in the main contract. TODO: tune the tx_data_len param.
233
+ tx_data_len = 1024
234
+ tx_data = pattern * (tx_data_len // len (pattern ))
235
+ tx_data += (tx_data_len - len (tx_data )) * bytes (Op .JUMPDEST )
236
+ assert len (tx_data ) == tx_data_len
237
+ assert initcode_size % len (tx_data ) == 0
238
+
239
+ # Prepare the initcode in memory.
240
+ code_prepare_initcode = sum (
241
+ (
242
+ Op .CALLDATACOPY (dest_offset = i * len (tx_data ), offset = 0 , size = Op .CALLDATASIZE )
243
+ for i in range (initcode_size // len (tx_data ))
244
+ ),
245
+ Bytecode (),
246
+ )
247
+
248
+ # At the start of the initcode execution, jump to the last opcode.
249
+ # This forces EVM to do the full jumpdest analysis.
250
+ initcode_prefix = Op .JUMP (initcode_size - 1 )
251
+ assert len (initcode_prefix ) <= 4
252
+ code_prepare_initcode += Op .MSTORE (
253
+ 0 , Op .PUSH32 [initcode_prefix + Op .STOP * (32 - len (initcode_prefix ))]
254
+ )
255
+
256
+ # Make sure the last opcode in the initcode is JUMPDEST.
257
+ code_prepare_initcode += Op .MSTORE (initcode_size - 32 , Op .PUSH1 [Op .JUMPDEST ])
258
+
259
+ # We are using the Paris fork where there is no initcode size limit.
260
+ # This allows us to test the increased initcode limits.
261
+ # However, Paris doesn't have PUSH0 so simulate it with PUSH1.
262
+ push0 = Op .PUSH0 if Op .PUSH0 in fork .valid_opcodes () else Op .PUSH1 [0 ]
263
+
264
+ code_invoke_create = (
265
+ Op .PUSH1 [len (initcode_prefix )]
266
+ + Op .MSTORE
267
+ + Op .CREATE (value = push0 , offset = push0 , size = Op .MSIZE )
268
+ )
269
+
270
+ initial_random = push0
271
+ code_prefix = code_prepare_initcode + initial_random
272
+ code_loop_header = Op .JUMPDEST
273
+ code_loop_footer = Op .JUMP (len (code_prefix ))
274
+ code_loop_body_len = (
275
+ MAX_CONTRACT_SIZE - len (code_prefix ) - len (code_loop_header ) - len (code_loop_footer )
276
+ )
277
+
278
+ code_loop_body = (code_loop_body_len // len (code_invoke_create )) * bytes (code_invoke_create )
279
+ code = code_prefix + code_loop_header + code_loop_body + code_loop_footer
280
+ assert (MAX_CONTRACT_SIZE - len (code_invoke_create )) < len (code ) <= MAX_CONTRACT_SIZE
281
+
282
+ env = Environment ()
283
+
284
+ tx = Transaction (
285
+ to = pre .deploy_contract (code = code ),
286
+ data = tx_data ,
287
+ gas_limit = env .gas_limit ,
288
+ sender = pre .fund_eoa (),
289
+ )
290
+
291
+ blockchain_test (
292
+ env = env ,
293
+ pre = pre ,
294
+ post = {},
295
+ blocks = [Block (txs = [tx ])],
296
+ )
0 commit comments