Skip to content

Commit 58b4bb0

Browse files
authored
refactor(test-tests): Port create collision tests (#2031)
1 parent c3813a5 commit 58b4bb0

3 files changed

Lines changed: 168 additions & 209 deletions

File tree

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
"""
2+
Test CREATE/CREATE2 collision scenarios with pre-existing storage per EIP-7610.
3+
"""
4+
5+
import pytest
6+
from execution_testing import (
7+
Account,
8+
Alloc,
9+
Bytecode,
10+
Initcode,
11+
Op,
12+
StateTestFiller,
13+
Transaction,
14+
compute_create2_address,
15+
)
16+
17+
REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7610.md"
18+
REFERENCE_SPEC_VERSION = "80ef48d0bbb5a4939ade51caaaac57b5df6acd4e"
19+
20+
pytestmark = [
21+
pytest.mark.valid_from("Paris"),
22+
# We need to modify the pre-alloc to include the collision
23+
pytest.mark.pre_alloc_modify,
24+
]
25+
26+
27+
@pytest.mark.ported_from(
28+
[
29+
"https://github.com/ethereum/tests/tree/v13.3/src/GeneralStateTestsFiller/stCreate2/RevertInCreateInInitCreate2ParisFiller.json", # noqa: E501
30+
],
31+
pr=["https://github.com/ethereum/execution-specs/pull/2031"],
32+
)
33+
def test_collision_with_create2_revert_in_initcode(
34+
state_test: StateTestFiller,
35+
pre: Alloc,
36+
) -> None:
37+
"""
38+
Test that a CREATE transaction collision with pre-existing storage causes
39+
the transaction to fail, even when the initcode would perform CREATE2 with
40+
reverting inner initcode.
41+
42+
The initcode (if it were to run) would:
43+
1. Execute CREATE2 with inner initcode that reverts with 32 bytes of data
44+
2. Store RETURNDATASIZE to storage slot 0
45+
3. Copy returndata to memory and store to slot 1
46+
47+
Since there's a collision (pre-existing storage), the CREATE TX should fail
48+
and the pre-existing account should remain unchanged.
49+
"""
50+
inner_initcode = Op.MSTORE(0, 0x112233) + Op.REVERT(0, 32)
51+
52+
initcode = (
53+
Op.MSTORE(0, Op.PUSH32(bytes(inner_initcode).ljust(32, b"\0")))
54+
+ Op.CREATE2(value=0, offset=0, size=len(inner_initcode), salt=0)
55+
+ Op.SSTORE(0, Op.RETURNDATASIZE)
56+
+ Op.RETURNDATACOPY(0, 0, 32)
57+
+ Op.SSTORE(1, Op.MLOAD(0))
58+
+ Op.STOP
59+
)
60+
61+
sender = pre.fund_eoa()
62+
tx = Transaction(
63+
sender=sender,
64+
to=None,
65+
data=initcode,
66+
gas_limit=10_000_000,
67+
)
68+
69+
# Pre-existing account with storage - this causes collision per EIP-7610.
70+
pre[tx.created_contract] = Account(
71+
balance=10,
72+
storage={0x00: 0x01},
73+
)
74+
75+
state_test(
76+
pre=pre,
77+
post={
78+
(tx.created_contract): Account(
79+
balance=10,
80+
nonce=0,
81+
storage={0x00: 0x01},
82+
),
83+
},
84+
tx=tx,
85+
)
86+
87+
88+
@pytest.mark.ported_from(
89+
[
90+
"https://github.com/ethereum/tests/tree/v13.3/src/GeneralStateTestsFiller/stCreate2/create2collisionStorageParisFiller.json", # noqa: E501
91+
],
92+
pr=["https://github.com/ethereum/execution-specs/pull/2031"],
93+
)
94+
@pytest.mark.parametrize(
95+
"create2_initcode",
96+
[
97+
pytest.param(b"", id="empty-initcode"),
98+
pytest.param(Op.SSTORE(1, 1), id="sstore-initcode"),
99+
pytest.param(
100+
Initcode(deploy_code=Op.SSTORE(1, 1)),
101+
id="initcode-with-deploy",
102+
),
103+
],
104+
)
105+
def test_create2_collision_storage(
106+
state_test: StateTestFiller,
107+
pre: Alloc,
108+
create2_initcode: Bytecode,
109+
) -> None:
110+
"""
111+
Test that CREATE2 fails when targeting an address with pre-existing
112+
storage.
113+
114+
A CREATE transaction deploys a contract that executes CREATE2. The CREATE2
115+
target address has pre-existing storage, which should cause the CREATE2 to
116+
fail per EIP-7610. The deployer stores the CREATE2 result to slot 0 (0 on
117+
failure).
118+
"""
119+
deployer_code = (
120+
Op.MSTORE(0, Op.PUSH32(bytes(create2_initcode).ljust(32, b"\0")))
121+
+ Op.SSTORE(
122+
0,
123+
Op.CREATE2(value=0, offset=0, size=len(create2_initcode), salt=0),
124+
)
125+
+ Op.STOP
126+
)
127+
128+
sender = pre.fund_eoa()
129+
tx = Transaction(
130+
sender=sender,
131+
to=None,
132+
data=deployer_code,
133+
value=1,
134+
gas_limit=400_000,
135+
)
136+
137+
deployer_address = tx.created_contract
138+
139+
create2_address = compute_create2_address(
140+
address=deployer_address,
141+
salt=0,
142+
initcode=create2_initcode,
143+
)
144+
145+
pre[create2_address] = Account(
146+
balance=10,
147+
storage={0x00: 0x01},
148+
)
149+
150+
state_test(
151+
pre=pre,
152+
post={
153+
# CREATE2 target unchanged due to collision
154+
create2_address: Account(
155+
balance=10,
156+
nonce=0,
157+
storage={0x00: 0x01},
158+
),
159+
# Deployer: nonce=2 (1 for creation + 1 for failed CREATE2 attempt)
160+
# storage[0]=0 indicates CREATE2 returned 0 (failure)
161+
deployer_address: Account(
162+
balance=1,
163+
nonce=2,
164+
storage={0x00: 0x00},
165+
),
166+
},
167+
tx=tx,
168+
)

tests/static/state_tests/stCreate2/RevertInCreateInInitCreate2ParisFiller.json

Lines changed: 0 additions & 60 deletions
This file was deleted.

tests/static/state_tests/stCreate2/create2collisionStorageParisFiller.json

Lines changed: 0 additions & 149 deletions
This file was deleted.

0 commit comments

Comments
 (0)