From a60adc22191f18fed9b6cf6dd81b9496ca216d1a Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 11 Jul 2024 19:52:15 +0200 Subject: [PATCH] new(tests): EOF - EIP-7620: Add more tests for validating EOF subcontainer kinds (#676) * fix(tests): Fix invalid EOF container combos test Make sure the case is invalid only for one reason. * new(tests): Add more cases for invalid subcontainer kind combos * new(tests): Add test for valid subcontainer kinds on a deeper nesting level * new(tests): Add test for valid subcontainer kinds in a non-first code section * Update changelog --- docs/CHANGELOG.md | 1 + .../test_subcontainer_validation.py | 218 +++++++++++++++++- 2 files changed, 215 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4b559ed76e..40dcb16889 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -25,6 +25,7 @@ Test fixtures for use by clients are available for each release on the [Github r - 🔀 Update EIP-7002 tests to match spec changes in [ethereum/execution-apis#549](https://github.com/ethereum/execution-apis/pull/549) ([#600](https://github.com/ethereum/execution-spec-tests/pull/600)) - ✨ Convert a few eip1153 tests from ethereum/tests repo into .py ([#440](https://github.com/ethereum/execution-spec-tests/pull/440)). - ✨ Add tests for [EIP-7480: EOF - Data section access instructions](https://eips.ethereum.org/EIPS/eip-7480) ([#518](https://github.com/ethereum/execution-spec-tests/pull/518), [#664](https://github.com/ethereum/execution-spec-tests/pull/664)). +- ✨ Add tests for subcontainer kind validation from [EIP-7620: EOF Contract Creation](https://eips.ethereum.org/EIPS/eip-7620) for the cases with deeply nested containers and non-first code sections ([#676](https://github.com/ethereum/execution-spec-tests/pull/676)). ### 🛠️ Framework diff --git a/tests/prague/eip7692_eof_v1/eip7620_eof_create/test_subcontainer_validation.py b/tests/prague/eip7692_eof_v1/eip7620_eof_create/test_subcontainer_validation.py index 0979dfd7db..ff64804b55 100644 --- a/tests/prague/eip7692_eof_v1/eip7620_eof_create/test_subcontainer_validation.py +++ b/tests/prague/eip7692_eof_v1/eip7620_eof_create/test_subcontainer_validation.py @@ -1,10 +1,12 @@ """ EOF Subcontainer tests covering simple cases. """ + import pytest from ethereum_test_tools import Account, EOFException, EOFStateTestFiller, EOFTestFiller from ethereum_test_tools.eof.v1 import Container, ContainerKind, Section +from ethereum_test_tools.eof.v1.constants import MAX_BYTECODE_SIZE from ethereum_test_tools.vm.opcode import Opcodes as Op from .. import EOF_FORK_NAME @@ -19,6 +21,9 @@ code=Op.EOFCREATE[0](0, 0, 0, 0) + Op.SSTORE(slot_code_worked, value_code_worked) + Op.STOP, max_stack_height=4, ) +eofcreate_revert_code_section = Section.Code( + code=Op.EOFCREATE[0](0, 0, 0, 0) + Op.REVERT(0, 0), +) returncontract_code_section = Section.Code( code=Op.SSTORE(slot_code_worked, value_code_worked) + Op.RETURNCONTRACT[0](0, 0), max_stack_height=2, @@ -90,9 +95,11 @@ def test_reverting_container( zero_section, revert_sub_container, ], - kind=ContainerKind.INITCODE - if zero_section == returncontract_code_section - else ContainerKind.RUNTIME, + kind=( + ContainerKind.INITCODE + if zero_section == returncontract_code_section + else ContainerKind.RUNTIME + ), ), container_post=Account(storage={slot_code_worked: value_code_worked}), ) @@ -187,21 +194,24 @@ def test_container_combos_valid( @pytest.mark.parametrize( - "code_section,first_sub_container", + "code_section,first_sub_container,container_kind", [ pytest.param( eofcreate_code_section, stop_sub_container, + ContainerKind.RUNTIME, id="EOFCREATE/STOP", ), pytest.param( eofcreate_code_section, return_sub_container, + ContainerKind.RUNTIME, id="EOFCREATE/RETURN", ), pytest.param( returncontract_code_section, returncontract_sub_container, + ContainerKind.INITCODE, id="RETURNCONTRACT/RETURNCONTRACT", ), ], @@ -210,6 +220,7 @@ def test_container_combos_invalid( eof_test: EOFTestFiller, code_section: Section, first_sub_container: Container, + container_kind: ContainerKind, ): """Test invalid subcontainer reference / opcode combos""" eof_test( @@ -218,6 +229,205 @@ def test_container_combos_invalid( code_section, first_sub_container, ], + kind=container_kind, + ), + expect_exception=EOFException.INCOMPATIBLE_CONTAINER_KIND, + ) + + +@pytest.mark.parametrize( + "code_section,first_sub_container", + [ + pytest.param( + eofcreate_revert_code_section, + returncontract_sub_container, + id="EOFCREATE/RETURNCONTRACT", + ), + pytest.param( + returncontract_code_section, + stop_sub_container, + id="RETURNCONTRACT/STOP", + ), + pytest.param( + returncontract_code_section, + return_sub_container, + id="RETURNCONTRACT/RETURN", + ), + pytest.param( + eofcreate_revert_code_section, + revert_sub_container, + id="EOFCREATE/REVERT", + ), + pytest.param( + returncontract_code_section, + revert_sub_container, + id="RETURNCONTRACT/REVERT", + ), + ], +) +def test_container_combos_deeply_nested_valid( + eof_test: EOFTestFiller, + code_section: Section, + first_sub_container: Container, +): + """Test valid subcontainer reference / opcode combos on a deep container nesting level""" + valid_container = Container( + sections=[ + code_section, + first_sub_container, + ], + kind=ContainerKind.INITCODE, + ) + + container = valid_container + while len(container) < MAX_BYTECODE_SIZE: + container = Container( + sections=[ + eofcreate_revert_code_section, + Section.Container(container=container.copy()), + ], + kind=ContainerKind.INITCODE, + ) + + eof_test(data=container) + + +@pytest.mark.parametrize( + "code_section,first_sub_container", + [ + pytest.param( + eofcreate_revert_code_section, + stop_sub_container, + id="EOFCREATE/STOP", + ), + pytest.param( + eofcreate_revert_code_section, + return_sub_container, + id="EOFCREATE/RETURN", + ), + pytest.param( + returncontract_code_section, + returncontract_sub_container, + id="RETURNCONTRACT/RETURNCONTRACT", + ), + ], +) +def test_container_combos_deeply_nested_invalid( + eof_test: EOFTestFiller, + code_section: Section, + first_sub_container: Container, +): + """Test invalid subcontainer reference / opcode combos on a deep container nesting level""" + invalid_container = Container( + sections=[ + code_section, + first_sub_container, + ], + kind=ContainerKind.INITCODE, + ) + + container = invalid_container + while len(container) < MAX_BYTECODE_SIZE: + container = Container( + sections=[ + eofcreate_revert_code_section, + Section.Container(container=container.copy()), + ], + kind=ContainerKind.INITCODE, + ) + + eof_test( + data=container, + expect_exception=EOFException.INCOMPATIBLE_CONTAINER_KIND, + ) + + +@pytest.mark.parametrize( + "code_section,first_sub_container,container_kind", + [ + pytest.param( + eofcreate_code_section, + returncontract_sub_container, + ContainerKind.RUNTIME, + id="EOFCREATE/RETURNCONTRACT", + ), + pytest.param( + returncontract_code_section, + stop_sub_container, + ContainerKind.INITCODE, + id="RETURNCONTRACT/STOP", + ), + pytest.param( + returncontract_code_section, + return_sub_container, + ContainerKind.INITCODE, + id="RETURNCONTRACT/RETURN", + ), + pytest.param( + eofcreate_code_section, + revert_sub_container, + ContainerKind.RUNTIME, + id="EOFCREATE/REVERT", + ), + pytest.param( + returncontract_code_section, + revert_sub_container, + ContainerKind.INITCODE, + id="RETURNCONTRACT/REVERT", + ), + ], +) +def test_container_combos_non_first_code_sections_valid( + eof_test: EOFTestFiller, + code_section: Section, + first_sub_container: Container, + container_kind: ContainerKind, +): + """Test valid subcontainer reference / opcode combos in a non-first code section""" + eof_test( + data=Container( + sections=[Section.Code(Op.JUMPF[i]) for i in range(1, 1024)] + + [code_section, first_sub_container], + kind=container_kind, + ), + ) + + +@pytest.mark.parametrize( + "code_section,first_sub_container,container_kind", + [ + pytest.param( + eofcreate_code_section, + stop_sub_container, + ContainerKind.RUNTIME, + id="EOFCREATE/STOP", + ), + pytest.param( + eofcreate_code_section, + return_sub_container, + ContainerKind.RUNTIME, + id="EOFCREATE/RETURN", + ), + pytest.param( + returncontract_code_section, + returncontract_sub_container, + ContainerKind.INITCODE, + id="RETURNCONTRACT/RETURNCONTRACT", + ), + ], +) +def test_container_combos_non_first_code_sections_invalid( + eof_test: EOFTestFiller, + code_section: Section, + first_sub_container: Container, + container_kind: ContainerKind, +): + """Test invalid subcontainer reference / opcode combos in a non-first code section""" + eof_test( + data=Container( + sections=[Section.Code(Op.JUMPF[i]) for i in range(1, 1024)] + + [code_section, first_sub_container], + kind=container_kind, ), expect_exception=EOFException.INCOMPATIBLE_CONTAINER_KIND, )