diff --git a/converted-ethereum-tests.txt b/converted-ethereum-tests.txt index e69de29bb2..672b29c661 100644 --- a/converted-ethereum-tests.txt +++ b/converted-ethereum-tests.txt @@ -0,0 +1,4 @@ +([#636](https://github.com/ethereum/execution-spec-tests/pull/636)) +GeneralStateTests/stSStoreTest/InitCollision.json +GeneralStateTests/stSStoreTest/InitCollisionNonZeroNonce.json +GeneralStateTests/stSStoreTest/InitCollisionParis.json \ No newline at end of file diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8309108c72..4b7bb27884 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -22,6 +22,7 @@ Test fixtures for use by clients are available for each release on the [Github r - ✨ Add tests for [EIP-7069: EOF - Revamped CALL instructions](https://eips.ethereum.org/EIPS/eip-7069) ([#595](https://github.com/ethereum/execution-spec-tests/pull/595)). - 🐞 Fix typos in self-destruct collision test from erroneous pytest parametrization ([#608](https://github.com/ethereum/execution-spec-tests/pull/608)). - ✨ Add tests for [EIP-3540: EOF - EVM Object Format v1](https://eips.ethereum.org/EIPS/eip-3540) ([#634](https://github.com/ethereum/execution-spec-tests/pull/634)). +- ✨ Add tests for [EIP-7610: Revert creation in case of non-empty storage](https://eips.ethereum.org/EIPS/eip-7610) ([#636](https://github.com/ethereum/execution-spec-tests/pull/636)). ### 🛠️ Framework diff --git a/docs/writing_tests/test_markers.md b/docs/writing_tests/test_markers.md index ad2a0fb884..c8c6c267fe 100644 --- a/docs/writing_tests/test_markers.md +++ b/docs/writing_tests/test_markers.md @@ -86,6 +86,31 @@ In this example, the test will be parameterized for parameter `precompile` with This marker is used to mark tests that are slow to run. These tests are not run during tox testing, and are only run when a release is being prepared. +### pytest.mark.pre_alloc_modifier + +This marker is used to mark tests that access and modify the internal accounts of `pre` , instead of using the default `pre.deploy_contract` or `pre.fund_eoa` functions. + +This is sometimes necessary when the test requires a specific account state that is not possible to achieve using the default functions, but limits the environment in which the test can be run. + +```python +import pytest + +@pytest.mark.pre_alloc_modifier +def test_something_with_modified_pre(pre): + pre[0x0000000000000000000000000000000000000000] = Account(storage={0: 1}) + ... +``` + +```python +import pytest + +@pytest.mark.pre_alloc_modifier +def test_something_with_modified_pre(): + # Test defines its own pre-alloc + pre = Alloc() + ... +``` + ### pytest.mark.skip("reason") This marker is used to skip a test with a reason. diff --git a/pytest.ini b/pytest.ini index 078ab40add..b1a8756db0 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,6 +5,7 @@ python_files = *.py testpaths = tests/ markers = slow + pre_alloc_modifier addopts = -p pytest_plugins.test_filler.test_filler -p pytest_plugins.forks.forks diff --git a/tests/paris/eip7610_create_collision/__init__.py b/tests/paris/eip7610_create_collision/__init__.py new file mode 100644 index 0000000000..1c7193e995 --- /dev/null +++ b/tests/paris/eip7610_create_collision/__init__.py @@ -0,0 +1,3 @@ +""" +Cross-client Create Collision Tests +""" diff --git a/tests/paris/eip7610_create_collision/test_initcollision.py b/tests/paris/eip7610_create_collision/test_initcollision.py new file mode 100644 index 0000000000..4ac3e70f55 --- /dev/null +++ b/tests/paris/eip7610_create_collision/test_initcollision.py @@ -0,0 +1,212 @@ +""" +Test collision in CREATE/CREATE2 account creation, where the existing account only has a non-zero +storage slot set. +""" + +import pytest + +from ethereum_test_tools import Account, Alloc, Environment, Initcode +from ethereum_test_tools import Opcodes as Op +from ethereum_test_tools import ( + StateTestFiller, + Transaction, + compute_create2_address, + compute_create_address, + compute_eofcreate_address, +) +from ethereum_test_tools.eof.v1 import Container, Section + +from ...prague.eip7692_eof_v1 import EOF_FORK_NAME + +REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7610.md" +REFERENCE_SPEC_VERSION = "80ef48d0bbb5a4939ade51caaaac57b5df6acd4e" + + +@pytest.mark.parametrize("nonce", [0, 1]) +@pytest.mark.pre_alloc_modifier # We need to modify the pre-alloc to include the collision +@pytest.mark.with_all_contract_creating_tx_types +@pytest.mark.valid_from("Paris") +def test_init_collision_create_tx( + state_test: StateTestFiller, + pre: Alloc, + nonce: int, + tx_type: int, +): + """ + Test that a contract creation transaction exceptionally aborts when the target address has a + non-empty storage. + """ + env = Environment() + + sender = pre.fund_eoa() + + initcode = Initcode( + deploy_code=Op.STOP, + initcode_prefix=Op.SSTORE(0, 1) + Op.SSTORE(1, 0), + ) + + tx = Transaction( + sender=sender, + type=tx_type, + to=None, + data=initcode, + gas_limit=200_000, + ) + + created_contract_address = tx.created_contract + + pre[created_contract_address] = Account( + storage={0x01: 0x01}, + nonce=nonce, + ) + + state_test( + env=env, + pre=pre, + post={ + created_contract_address: Account( + storage={0x01: 0x01}, + ), + }, + tx=tx, + ) + + +@pytest.mark.parametrize("opcode", [Op.CREATE, Op.CREATE2]) +@pytest.mark.parametrize("nonce", [0, 1]) +@pytest.mark.pre_alloc_modifier # We need to modify the pre-alloc to include the collision +@pytest.mark.valid_from("Paris") +def test_init_collision_create_opcode( + state_test: StateTestFiller, + pre: Alloc, + nonce: int, + opcode: Op, +): + """ + Test that a contract creation opcode exceptionally aborts when the target address has a + non-empty storage. + """ + env = Environment() + + sender = pre.fund_eoa() + + deploy_code = Op.STOP + + initcode = Initcode( + deploy_code=deploy_code, + initcode_prefix=Op.SSTORE(0, 1) + Op.SSTORE(1, 0), + ) + + salt = 0x0 + salt_param = [salt] if opcode == Op.CREATE2 else [] + assert len(initcode) <= 32 + contract_creator_code = ( + Op.MSTORE(0, Op.PUSH32(bytes(initcode).ljust(32, b"\0"))) + + Op.SSTORE(0x01, opcode(0, 0, len(initcode), *salt_param)) + + Op.STOP + ) + contract_creator_address = pre.deploy_contract( + contract_creator_code, + storage={0x01: 0x01}, + ) + if opcode == Op.CREATE2: + created_contract_address = compute_create2_address( + contract_creator_address, salt, initcode + ) + elif opcode == Op.CREATE: + created_contract_address = compute_create_address(contract_creator_address, 1) + else: + raise ValueError(f"Unknown opcode: {opcode}") + + tx = Transaction( + sender=sender, + to=contract_creator_address, + gas_limit=2_000_000, + ) + + pre[created_contract_address] = Account( + storage={0x01: 0x01}, + nonce=nonce, + ) + + state_test( + env=env, + pre=pre, + post={ + created_contract_address: Account( + storage={0x01: 0x01}, + ), + contract_creator_address: Account(storage={0x01: 0x00}), + }, + tx=tx, + ) + + +@pytest.mark.parametrize("nonce", [0, 1]) +@pytest.mark.pre_alloc_modifier # We need to modify the pre-alloc to include the collision +@pytest.mark.valid_from(EOF_FORK_NAME) +def test_init_collision_eofcreate_opcode( + state_test: StateTestFiller, + pre: Alloc, + nonce: int, +): + """ + Test that a EOF contract creation opcode exceptionally aborts when the target address has a + non-empty storage. + """ + env = Environment() + + sender = pre.fund_eoa() + + deploy_container = Container(sections=[Section.Code(Op.STOP)]) + + init_container = Container( + sections=[ + Section.Code( + code=Op.RETURNCONTRACT[0](0, 0), + max_stack_height=2, + ), + Section.Container(container=deploy_container), + ] + ) + + salt = 0x00 + contract_creator_container = Container( + sections=[ + Section.Code( + code=Op.SSTORE(0x01, Op.EOFCREATE[0](0, salt, 0, 0)) + Op.STOP, + max_stack_height=4, + ), + Section.Container(init_container), + ] + ) + contract_creator_address = pre.deploy_contract( + contract_creator_container, + storage={0x01: 0x01}, + ) + created_contract_address = compute_eofcreate_address( + contract_creator_address, salt, init_container + ) + + tx = Transaction( + sender=sender, + to=contract_creator_address, + gas_limit=2_000_000, + ) + + pre[created_contract_address] = Account( + storage={0x01: 0x01}, + nonce=nonce, + ) + + state_test( + env=env, + pre=pre, + post={ + created_contract_address: Account( + storage={0x01: 0x01}, + ), + contract_creator_address: Account(storage={0x01: 0x00}), + }, + tx=tx, + ) diff --git a/whitelist.txt b/whitelist.txt index aee48455bd..dca762bb46 100644 --- a/whitelist.txt +++ b/whitelist.txt @@ -105,6 +105,7 @@ eips EIPs eip6110 eip7002 +eip7692 el endianness EngineAPI