From 58b4bb0674d50b31a805ea078afdc2f465cfcd59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 26 Jan 2026 15:04:03 +0100 Subject: [PATCH] refactor(test-tests): Port create collision tests (#2031) --- .../test_revert_in_create.py | 168 ++++++++++++++++++ ...evertInCreateInInitCreate2ParisFiller.json | 60 ------- .../create2collisionStorageParisFiller.json | 149 ---------------- 3 files changed, 168 insertions(+), 209 deletions(-) create mode 100644 tests/paris/eip7610_create_collision/test_revert_in_create.py delete mode 100644 tests/static/state_tests/stCreate2/RevertInCreateInInitCreate2ParisFiller.json delete mode 100644 tests/static/state_tests/stCreate2/create2collisionStorageParisFiller.json diff --git a/tests/paris/eip7610_create_collision/test_revert_in_create.py b/tests/paris/eip7610_create_collision/test_revert_in_create.py new file mode 100644 index 0000000000..f8834ea81e --- /dev/null +++ b/tests/paris/eip7610_create_collision/test_revert_in_create.py @@ -0,0 +1,168 @@ +""" +Test CREATE/CREATE2 collision scenarios with pre-existing storage per EIP-7610. +""" + +import pytest +from execution_testing import ( + Account, + Alloc, + Bytecode, + Initcode, + Op, + StateTestFiller, + Transaction, + compute_create2_address, +) + +REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7610.md" +REFERENCE_SPEC_VERSION = "80ef48d0bbb5a4939ade51caaaac57b5df6acd4e" + +pytestmark = [ + pytest.mark.valid_from("Paris"), + # We need to modify the pre-alloc to include the collision + pytest.mark.pre_alloc_modify, +] + + +@pytest.mark.ported_from( + [ + "https://github.com/ethereum/tests/tree/v13.3/src/GeneralStateTestsFiller/stCreate2/RevertInCreateInInitCreate2ParisFiller.json", # noqa: E501 + ], + pr=["https://github.com/ethereum/execution-specs/pull/2031"], +) +def test_collision_with_create2_revert_in_initcode( + state_test: StateTestFiller, + pre: Alloc, +) -> None: + """ + Test that a CREATE transaction collision with pre-existing storage causes + the transaction to fail, even when the initcode would perform CREATE2 with + reverting inner initcode. + + The initcode (if it were to run) would: + 1. Execute CREATE2 with inner initcode that reverts with 32 bytes of data + 2. Store RETURNDATASIZE to storage slot 0 + 3. Copy returndata to memory and store to slot 1 + + Since there's a collision (pre-existing storage), the CREATE TX should fail + and the pre-existing account should remain unchanged. + """ + inner_initcode = Op.MSTORE(0, 0x112233) + Op.REVERT(0, 32) + + initcode = ( + Op.MSTORE(0, Op.PUSH32(bytes(inner_initcode).ljust(32, b"\0"))) + + Op.CREATE2(value=0, offset=0, size=len(inner_initcode), salt=0) + + Op.SSTORE(0, Op.RETURNDATASIZE) + + Op.RETURNDATACOPY(0, 0, 32) + + Op.SSTORE(1, Op.MLOAD(0)) + + Op.STOP + ) + + sender = pre.fund_eoa() + tx = Transaction( + sender=sender, + to=None, + data=initcode, + gas_limit=10_000_000, + ) + + # Pre-existing account with storage - this causes collision per EIP-7610. + pre[tx.created_contract] = Account( + balance=10, + storage={0x00: 0x01}, + ) + + state_test( + pre=pre, + post={ + (tx.created_contract): Account( + balance=10, + nonce=0, + storage={0x00: 0x01}, + ), + }, + tx=tx, + ) + + +@pytest.mark.ported_from( + [ + "https://github.com/ethereum/tests/tree/v13.3/src/GeneralStateTestsFiller/stCreate2/create2collisionStorageParisFiller.json", # noqa: E501 + ], + pr=["https://github.com/ethereum/execution-specs/pull/2031"], +) +@pytest.mark.parametrize( + "create2_initcode", + [ + pytest.param(b"", id="empty-initcode"), + pytest.param(Op.SSTORE(1, 1), id="sstore-initcode"), + pytest.param( + Initcode(deploy_code=Op.SSTORE(1, 1)), + id="initcode-with-deploy", + ), + ], +) +def test_create2_collision_storage( + state_test: StateTestFiller, + pre: Alloc, + create2_initcode: Bytecode, +) -> None: + """ + Test that CREATE2 fails when targeting an address with pre-existing + storage. + + A CREATE transaction deploys a contract that executes CREATE2. The CREATE2 + target address has pre-existing storage, which should cause the CREATE2 to + fail per EIP-7610. The deployer stores the CREATE2 result to slot 0 (0 on + failure). + """ + deployer_code = ( + Op.MSTORE(0, Op.PUSH32(bytes(create2_initcode).ljust(32, b"\0"))) + + Op.SSTORE( + 0, + Op.CREATE2(value=0, offset=0, size=len(create2_initcode), salt=0), + ) + + Op.STOP + ) + + sender = pre.fund_eoa() + tx = Transaction( + sender=sender, + to=None, + data=deployer_code, + value=1, + gas_limit=400_000, + ) + + deployer_address = tx.created_contract + + create2_address = compute_create2_address( + address=deployer_address, + salt=0, + initcode=create2_initcode, + ) + + pre[create2_address] = Account( + balance=10, + storage={0x00: 0x01}, + ) + + state_test( + pre=pre, + post={ + # CREATE2 target unchanged due to collision + create2_address: Account( + balance=10, + nonce=0, + storage={0x00: 0x01}, + ), + # Deployer: nonce=2 (1 for creation + 1 for failed CREATE2 attempt) + # storage[0]=0 indicates CREATE2 returned 0 (failure) + deployer_address: Account( + balance=1, + nonce=2, + storage={0x00: 0x00}, + ), + }, + tx=tx, + ) diff --git a/tests/static/state_tests/stCreate2/RevertInCreateInInitCreate2ParisFiller.json b/tests/static/state_tests/stCreate2/RevertInCreateInInitCreate2ParisFiller.json deleted file mode 100644 index b83a17e9de..0000000000 --- a/tests/static/state_tests/stCreate2/RevertInCreateInInitCreate2ParisFiller.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "RevertInCreateInInitCreate2Paris" : { - "_info" : { - "comment" : "RevertInCreateInInit for CREATE2" - }, - "env" : { - "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty" : "0x20000", - "currentGasLimit" : "0x0b00000000", - "currentNumber" : "0x01", - "currentTimestamp" : "0x03e8" - }, - "expect" : [ - { - "network" : [">=Cancun=Cancun"], - "result" : { - "e2b35478fdd26477cc576dd906e6277761246a3c" : { - "balance" : "10", - "nonce" : "0", - "storage" : { - "0x00" : "0x01" - } - }, - "6295ee1b4f6dd65047762f924ecd367c17eabf8f" : { - "balance" : "1", - "nonce" : "2", - "storage" : { - "0x00" : "0x00" - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "nonce" : "1" - } - } - }, - { - "indexes" : { - "data" : 1, - "gas" : -1, - "value" : -1 - }, - "network" : [">=Cancun"], - "result" : { - "af3ecba2fe09a4f6c19f16a9d119e44e08c2da01" : { - "balance" : "10", - "nonce" : "0", - "code" : "0x", - "storage" : { - "0x00" : "0x01" - } - }, - "6295ee1b4f6dd65047762f924ecd367c17eabf8f" : { - "balance" : "1", - "nonce" : "2", - "storage" : { - "0x00" : "0x00" - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "nonce" : "1" - } - } - }, - { - "indexes" : { - "data" : 2, - "gas" : -1, - "value" : -1 - }, - "network" : [">=Cancun"], - "result" : { - "ec2c6832d00680ece8ff9254f81fdab0a5a2ac50" : { - "balance" : "10", - "nonce" : "0", - "code" : "0x", - "storage" : { - "0x00" : "0x01" - } - }, - "6295ee1b4f6dd65047762f924ecd367c17eabf8f" : { - "balance" : "1", - "nonce" : "2", - "storage" : { - "0x00" : "0x00" - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "nonce" : "1" - } - } - } - ], - "pre" : { - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "1000000000000000000", - "code" : "", - "nonce" : "0", - "storage" : { - } - }, - "e2b35478fdd26477cc576dd906e6277761246a3c" : { - "balance" : "10", - "code" : "0x", - "nonce" : "0", - "storage" : { - "0x00" : "0x01" - } - }, - "af3ecba2fe09a4f6c19f16a9d119e44e08c2da01" : { - "balance" : "10", - "code" : "0x", - "nonce" : "0", - "storage" : { - "0x00" : "0x01" - } - }, - "ec2c6832d00680ece8ff9254f81fdab0a5a2ac50" : { - "balance" : "10", - "code" : "0x", - "nonce" : "0", - "storage" : { - "0x00" : "0x01" - } - } - }, - "transaction" : { - "data" : [ - "{ (CREATE2 0 0 0 0) }", - "{ (MSTORE 0 0x6001600155) (CREATE2 0 27 5 0) }", - "{ (MSTORE 0 0x6460016001556000526005601bf3) (CREATE2 0 18 14 0) }" - ], - "gasLimit" : [ - "400000" - ], - "gasPrice" : "10", - "nonce" : "0", - "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to" : "", - "value" : [ - "1" - ] - } - } -}