|
18 | 18 | Bytecode,
|
19 | 19 | Environment,
|
20 | 20 | Hash,
|
| 21 | + StateTestFiller, |
21 | 22 | Transaction,
|
22 | 23 | While,
|
23 | 24 | compute_create2_address,
|
@@ -221,3 +222,96 @@ def test_worst_bytecode_single_opcode(
|
221 | 222 | ],
|
222 | 223 | exclude_full_post_state_in_output=True,
|
223 | 224 | )
|
| 225 | + |
| 226 | + |
| 227 | +@pytest.mark.valid_from("Cancun") |
| 228 | +@pytest.mark.parametrize( |
| 229 | + "pattern", |
| 230 | + [ |
| 231 | + Op.STOP, |
| 232 | + Op.JUMPDEST, |
| 233 | + Op.PUSH1[bytes(Op.JUMPDEST)], |
| 234 | + Op.PUSH2[bytes(Op.JUMPDEST + Op.JUMPDEST)], |
| 235 | + Op.PUSH1[bytes(Op.JUMPDEST)] + Op.JUMPDEST, |
| 236 | + Op.PUSH2[bytes(Op.JUMPDEST + Op.JUMPDEST)] + Op.JUMPDEST, |
| 237 | + ], |
| 238 | + ids=lambda x: x.hex(), |
| 239 | +) |
| 240 | +def test_worst_initcode_jumpdest_analysis( |
| 241 | + state_test: StateTestFiller, |
| 242 | + pre: Alloc, |
| 243 | + fork: Fork, |
| 244 | + pattern: Bytecode, |
| 245 | +): |
| 246 | + """ |
| 247 | + Test the jumpdest analysis performance of the initcode. |
| 248 | +
|
| 249 | + This benchmark places a very long initcode in the memory and then invoke CREATE instructions |
| 250 | + with this initcode up to the block gas limit. The initcode itself has minimal execution time |
| 251 | + but forces the EVM to perform the full jumpdest analysis on the parametrized byte pattern. |
| 252 | + The initicode is modified by mixing-in the returned create address between CREATE invocations |
| 253 | + to prevent caching. |
| 254 | + """ |
| 255 | + max_code_size = fork.max_code_size() |
| 256 | + initcode_size = fork.max_initcode_size() |
| 257 | + |
| 258 | + # Expand the initcode pattern to the transaction data so it can be used in CALLDATACOPY |
| 259 | + # in the main contract. TODO: tune the tx_data_len param. |
| 260 | + tx_data_len = 1024 |
| 261 | + tx_data = pattern * (tx_data_len // len(pattern)) |
| 262 | + tx_data += (tx_data_len - len(tx_data)) * bytes(Op.JUMPDEST) |
| 263 | + assert len(tx_data) == tx_data_len |
| 264 | + assert initcode_size % len(tx_data) == 0 |
| 265 | + |
| 266 | + # Prepare the initcode in memory. |
| 267 | + code_prepare_initcode = sum( |
| 268 | + ( |
| 269 | + Op.CALLDATACOPY(dest_offset=i * len(tx_data), offset=0, size=Op.CALLDATASIZE) |
| 270 | + for i in range(initcode_size // len(tx_data)) |
| 271 | + ), |
| 272 | + Bytecode(), |
| 273 | + ) |
| 274 | + |
| 275 | + # At the start of the initcode execution, jump to the last opcode. |
| 276 | + # This forces EVM to do the full jumpdest analysis. |
| 277 | + initcode_prefix = Op.JUMP(initcode_size - 1) |
| 278 | + code_prepare_initcode += Op.MSTORE( |
| 279 | + 0, Op.PUSH32[bytes(initcode_prefix).ljust(32, bytes(Op.JUMPDEST))] |
| 280 | + ) |
| 281 | + |
| 282 | + # Make sure the last opcode in the initcode is JUMPDEST. |
| 283 | + code_prepare_initcode += Op.MSTORE(initcode_size - 32, Op.PUSH32[bytes(Op.JUMPDEST) * 32]) |
| 284 | + |
| 285 | + code_invoke_create = ( |
| 286 | + Op.PUSH1[len(initcode_prefix)] |
| 287 | + + Op.MSTORE |
| 288 | + + Op.CREATE(value=Op.PUSH0, offset=Op.PUSH0, size=Op.MSIZE) |
| 289 | + ) |
| 290 | + |
| 291 | + initial_random = Op.PUSH0 |
| 292 | + code_prefix = code_prepare_initcode + initial_random |
| 293 | + code_loop_header = Op.JUMPDEST |
| 294 | + code_loop_footer = Op.JUMP(len(code_prefix)) |
| 295 | + code_loop_body_len = ( |
| 296 | + max_code_size - len(code_prefix) - len(code_loop_header) - len(code_loop_footer) |
| 297 | + ) |
| 298 | + |
| 299 | + code_loop_body = (code_loop_body_len // len(code_invoke_create)) * bytes(code_invoke_create) |
| 300 | + code = code_prefix + code_loop_header + code_loop_body + code_loop_footer |
| 301 | + assert (max_code_size - len(code_invoke_create)) < len(code) <= max_code_size |
| 302 | + |
| 303 | + env = Environment() |
| 304 | + |
| 305 | + tx = Transaction( |
| 306 | + to=pre.deploy_contract(code=code), |
| 307 | + data=tx_data, |
| 308 | + gas_limit=env.gas_limit, |
| 309 | + sender=pre.fund_eoa(), |
| 310 | + ) |
| 311 | + |
| 312 | + state_test( |
| 313 | + env=env, |
| 314 | + pre=pre, |
| 315 | + post={}, |
| 316 | + tx=tx, |
| 317 | + ) |
0 commit comments