Skip to content

fix(tests): EIP-7620 - make CREATE/CREATE2 restrictions specific #1475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/osaka/eip7692_eof_v1/eip7620_eof_create/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
slot_call_or_create = next(_slot)
slot_counter = next(_slot)
slot_data_load = next(_slot)
slot_all_subcall_gas_gone = next(_slot)

slot_last_slot = next(_slot)

Expand Down
128 changes: 110 additions & 18 deletions tests/osaka/eip7692_eof_v1/eip7620_eof_create/test_legacy_eof_creates.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest

from ethereum_test_base_types.base_types import Address, Bytes
from ethereum_test_tools import (
Account,
Alloc,
Expand All @@ -13,9 +14,12 @@
from ethereum_test_tools.vm.opcode import Opcodes
from ethereum_test_tools.vm.opcode import Opcodes as Op
from ethereum_test_types.eof.v1 import Container
from ethereum_test_types.helpers import compute_create_address
from tests.prague.eip7702_set_code_tx.spec import Spec

from .. import EOF_FORK_NAME
from .helpers import (
slot_all_subcall_gas_gone,
slot_code_worked,
slot_create_address,
smallest_initcode_subcontainer,
Expand All @@ -38,46 +42,128 @@
],
)
@pytest.mark.parametrize(
"deploy_code",
"initcode",
[
Bytes("0xEF00"),
Bytes("0xEF0001"),
pytest.param(smallest_initcode_subcontainer, id="deploy_eof_initcontainer"),
pytest.param(smallest_runtime_subcontainer, id="deploy_eof_container"),
],
)
def test_cross_version_creates_fail(
def test_cross_version_creates_fail_light(
state_test: StateTestFiller,
pre: Alloc,
legacy_create_opcode: Opcodes,
deploy_code: Container,
initcode: Bytes | Container,
):
"""Verifies that CREATE and CREATE2 cannot create EOF contracts."""
"""Verifies that CREATE and CREATE2 cannot run EOF initcodes and fail early on attempt."""
env = Environment()
salt_param = [0] if legacy_create_opcode == Op.CREATE2 else []

sender = pre.fund_eoa()

tx_gas_limit = 10_000_000

contract_address = pre.deploy_contract(
code=Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE)
+ Op.SSTORE(slot_create_address, legacy_create_opcode(0, 0, Op.CALLDATASIZE, *salt_param))
+ Op.SSTORE(slot_create_address, legacy_create_opcode(size=Op.CALLDATASIZE))
# Aproximates whether code until here consumed the 63/64th gas given to subcall
+ Op.SSTORE(slot_all_subcall_gas_gone, Op.LT(Op.GAS, tx_gas_limit // 64))
+ Op.SSTORE(slot_code_worked, value_code_worked)
+ Op.STOP
)

# Storage in 0 should be empty as the create/create2 should fail,
# and 1 in 1 to show execution continued and did not halt
post = {
contract_address: Account(
storage={
slot_create_address: EOFCREATE_FAILURE,
slot_code_worked: value_code_worked,
}
)
slot_all_subcall_gas_gone: 0,
},
nonce=1,
),
# Double check no accounts were created
compute_create_address(address=contract_address, nonce=1): Account.NONEXISTENT,
compute_create_address(
address=contract_address, initcode=initcode, salt=0, opcode=Op.CREATE2
): Account.NONEXISTENT,
}
tx = Transaction(
to=contract_address,
gas_limit=10_000_000,
gas_price=10,
protected=False,
gas_limit=tx_gas_limit,
sender=sender,
data=deploy_code,
data=initcode,
)

state_test(
env=env,
pre=pre,
post=post,
tx=tx,
)


@pytest.mark.parametrize(
"legacy_create_opcode",
[
pytest.param(Op.CREATE, id="CREATE"),
pytest.param(Op.CREATE2, id="CREATE2"),
],
)
@pytest.mark.parametrize(
"initcode",
[
Bytes("0xEF"),
Bytes("0xEF01"),
Bytes("0xEF0101"),
Spec.delegation_designation(Address(0xAA)),
Bytes("0xEF02"),
],
)
def test_cross_version_creates_fail_hard(
state_test: StateTestFiller,
pre: Alloc,
legacy_create_opcode: Opcodes,
initcode: Bytes,
):
"""
Verifies that CREATE and CREATE2 fail hard on attempt to run initcode starting with `EF` but
not `EF00`.
"""
env = Environment()

sender = pre.fund_eoa()

tx_gas_limit = 10_000_000

contract_address = pre.deploy_contract(
code=Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE)
+ Op.SSTORE(slot_create_address, legacy_create_opcode(size=Op.CALLDATASIZE))
# Aproximates whether code until here consumed the 63/64th gas given to subcall
+ Op.SSTORE(slot_all_subcall_gas_gone, Op.LT(Op.GAS, tx_gas_limit // 64))
+ Op.SSTORE(slot_code_worked, value_code_worked)
+ Op.STOP
)

post = {
contract_address: Account(
storage={
slot_create_address: EOFCREATE_FAILURE,
slot_code_worked: value_code_worked,
slot_all_subcall_gas_gone: 1,
},
nonce=2,
),
# Double check no accounts were created
compute_create_address(address=contract_address, nonce=1): Account.NONEXISTENT,
compute_create_address(
address=contract_address, initcode=initcode, salt=0, opcode=Op.CREATE2
): Account.NONEXISTENT,
}
tx = Transaction(
to=contract_address,
gas_limit=tx_gas_limit,
sender=sender,
data=initcode,
)

state_test(
Expand All @@ -98,6 +184,10 @@ def test_cross_version_creates_fail(
@pytest.mark.parametrize(
"deploy_code",
[
Bytes("0xEF"),
Bytes("0xEF00"),
Bytes("0xEF0001"),
Bytes("0xEF01"),
pytest.param(smallest_initcode_subcontainer, id="deploy_eof_initcontainer"),
pytest.param(smallest_runtime_subcontainer, id="deploy_eof_container"),
],
Expand All @@ -106,9 +196,13 @@ def test_legacy_initcode_eof_contract_fails(
state_test: StateTestFiller,
pre: Alloc,
legacy_create_opcode: Opcodes,
deploy_code: Container,
deploy_code: Bytes | Container,
):
"""Verifies that legacy initcode cannot create EOF."""
"""
Verifies that legacy initcode cannot create EOF.

This tests only ensures EIP-3541 behavior is kept, not altered by EIP-7620.
"""
env = Environment()
init_code = LegacyInitcode(deploy_code=deploy_code)
salt_param = [0] if legacy_create_opcode == Op.CREATE2 else []
Expand All @@ -131,8 +225,6 @@ def test_legacy_initcode_eof_contract_fails(
tx = Transaction(
to=contract_address,
gas_limit=10_000_000,
gas_price=10,
protected=False,
data=init_code,
sender=sender,
)
Expand Down
Loading