diff --git a/converted-ethereum-tests.txt b/converted-ethereum-tests.txt index 195b3f9219..fa95fc194b 100644 --- a/converted-ethereum-tests.txt +++ b/converted-ethereum-tests.txt @@ -9,10 +9,14 @@ GeneralStateTests/stCreate2/call_then_create2_successful_then_returndatasize.jso ([#598](https://github.com/ethereum/execution-spec-tests/pull/598)) EOFTests/EIP3540/validInvalid.json +EOFTests/EIP3670/validInvalid.json EOFTests/efValidation/EOF1_embedded_container_.json EOFTests/efValidation/EOF1_eofcreate_valid_.json EOFTests/efValidation/EOF1_section_order_.json EOFTests/efValidation/EOF1_truncated_section_.json +EOFTests/efValidation/EOF1_undefined_opcodes_.json +EOFTests/efValidation/EOF1_truncated_push_.json +EOFTests/efValidation/deprecated_instructions_.json EOFTests/efValidation/unreachable_code_sections_.json ([#647](https://github.com/ethereum/execution-spec-tests/pull/647)) diff --git a/tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py b/tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py index 14149e069b..4c9bd554a6 100644 --- a/tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py +++ b/tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py @@ -58,6 +58,8 @@ Op.JUMPF, } +data_portion_opcodes = {op for op in all_opcodes if op.has_data_portion()} + # NOTE: `sorted` is used to ensure that the tests are collected in a deterministic order. @@ -118,6 +120,24 @@ def test_all_opcodes_in_container( ) +@pytest.mark.parametrize( + "opcode", + sorted(invalid_eof_opcodes | undefined_opcodes), +) +def test_invalid_opcodes_after_stop( + eof_test: EOFTestFiller, + opcode: Opcode, +): + """ + Test that an invalid opcode placed after STOP (terminating instruction) invalidates EOF. + """ + eof_code = Container(sections=[Section.Code(code=Op.STOP + opcode), Section.Data("00" * 32)]) + eof_test( + data=eof_code, + expect_exception=EOFException.UNDEFINED_INSTRUCTION, + ) + + @pytest.mark.parametrize( "opcode", sorted( @@ -382,3 +402,55 @@ def test_all_opcodes_stack_overflow( data=eof_code, expect_exception=exception, ) + + +@pytest.mark.parametrize( + "opcode", + sorted(data_portion_opcodes), +) +@pytest.mark.parametrize( + "truncate_all", + [False, True], +) +@pytest.mark.parametrize( + "compute_max_stack_height", + [False, True], +) +def test_truncated_data_portion_opcodes( + eof_test: EOFTestFiller, + opcode: Opcode, + truncate_all, + compute_max_stack_height: bool, +): + """ + Test that an instruction with data portion and truncated immediate bytes + (therefore a terminating instruction is also missing) invalidates EOF. + """ + opcode_with_data_portion: bytes = bytes(opcode[1]) + + if len(opcode_with_data_portion) == 2 and truncate_all: + pytest.skip("can only be truncated one-way") + + # Compose instruction bytes with empty imm bytes (truncate_all) or 1 byte shorter imm bytes. + opcode_bytes = opcode_with_data_portion[0:1] if truncate_all else opcode_with_data_portion[:-1] + + if opcode.min_stack_height > 0: + opcode_bytes = bytes(Op.PUSH0 * opcode.min_stack_height) + opcode_bytes + + max_stack_height = ( + max(opcode.min_stack_height, opcode.pushed_stack_items) if compute_max_stack_height else 0 + ) + if compute_max_stack_height and max_stack_height == 0: + pytest.skip("max_stack_height is 0") + + eof_code = Container( + sections=[ + Section.Code(opcode_bytes, max_stack_height=max_stack_height), + # Provide data section potentially confused with missing imm bytes. + Section.Data(b"\0" * 64), + ] + ) + eof_test( + data=eof_code, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, + ) diff --git a/tests/osaka/eip7692_eof_v1/eof_tracker.md b/tests/osaka/eip7692_eof_v1/eof_tracker.md index a8593dfa07..efacca1499 100644 --- a/tests/osaka/eip7692_eof_v1/eof_tracker.md +++ b/tests/osaka/eip7692_eof_v1/eof_tracker.md @@ -96,12 +96,12 @@ ### Validation -- [ ] Code section with invalid opcodes is rejected (ethereum/tests: ./src/EOFTestsFiller/efExample/validInvalidFiller.yml src/EOFTestsFiller/efValidation/EOF1_undefined_opcodes_Copier.json src/EOFTestsFiller/EIP3670/validInvalidFiller.yml) -- [ ] INVALID opcode is valid (ethereum/tests: ./src/EOFTestsFiller/efExample/validInvalidFiller.yml) -- [ ] Truncated PUSH data (ethereum/tests: ./src/EOFTestsFiller/efExample/validInvalidFiller.yml src/EOFTestsFiller/efValidation/EOF1_truncated_push_Copier.json src/EOFTestsFiller/EIP3670/validInvalidFiller.yml) -- [ ] Opcodes deprecated in EOF are rejected (ethereum/tests: src/EOFTestsFiller/efValidation/deprecated_instructions_Copier.json ethereum/tests: src/EOFTestsFiller/EIP3670/validInvalidFiller.yml) -- [ ] Codes with each valid opcodes (ethereum/tests: src/EOFTestsFiller/EIP3670/validInvalidFiller.yml) -- [ ] Undefined instruction after terminating instruction (ethereum/tests: src/EOFTestsFiller/EIP3670/validInvalidFiller.yml) +- [x] Code section with invalid opcodes is rejected ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_all_opcodes_in_container`](./eip3540_eof_v1/test_all_opcodes_in_container/test_all_opcodes_in_container.md)) +- [x] INVALID opcode is valid ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_all_opcodes_in_container`](./eip3540_eof_v1/test_all_opcodes_in_container/test_all_opcodes_in_container.md)) +- [x] Truncated PUSH data ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_truncated_data_portion_opcodes`](./eip3540_eof_v1/test_all_opcodes_in_container/test_truncated_data_portion_opcodes.md)) +- [x] Opcodes deprecated in EOF are rejected ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_all_opcodes_in_container`](./eip3540_eof_v1/test_all_opcodes_in_container/test_all_opcodes_in_container.md)) +- [x] Codes with each valid opcodes ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_all_opcodes_in_container`](./eip3540_eof_v1/test_all_opcodes_in_container/test_all_opcodes_in_container.md)) +- [x] Undefined instruction after terminating instruction ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_all_opcodes_in_container.py::test_invalid_opcodes_after_stop`](./eip3540_eof_v1/test_all_opcodes_in_container/test_invalid_opcodes_after_stop.md)) ## EIP-4200: EOF - Static relative jumps