From b6f5a85a8a573eea8bf6b677f46f8961f8443ec5 Mon Sep 17 00:00:00 2001 From: Guruprasad Kamath Date: Tue, 24 Sep 2024 10:22:05 +0200 Subject: [PATCH] update to latest test releases + fixes This commit updates to the latest test releases and fixes some minor bugs exposed by the increased coverage. In particular, the bugs related to faulty return flag in the type section are fixed. Any code section that has RETF instruction or has a JUMPF to a returning section is considered a returning section. Otherwise, the code section is considered non-returning. The output in the type section should reflect this behaviour. If not, the container is invalid --- src/ethereum/prague/vm/eof/__init__.py | 1 + .../prague/vm/eof/instructions_check.py | 47 ++++++++++++++++++- src/ethereum/prague/vm/eof/validation.py | 11 +++++ tests/helpers/__init__.py | 2 +- tests/prague/test_evm_tools.py | 8 ++-- tests/prague/test_state_transition.py | 3 +- 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/ethereum/prague/vm/eof/__init__.py b/src/ethereum/prague/vm/eof/__init__.py index be0299ad10..e01c4178d2 100644 --- a/src/ethereum/prague/vm/eof/__init__.py +++ b/src/ethereum/prague/vm/eof/__init__.py @@ -132,6 +132,7 @@ class Validator: current_index: Uint current_code: bytes current_pc: Uint + is_current_section_returning: bool has_return_contract: bool has_stop: bool has_return: bool diff --git a/src/ethereum/prague/vm/eof/instructions_check.py b/src/ethereum/prague/vm/eof/instructions_check.py index 250313f8e4..f514a69229 100644 --- a/src/ethereum/prague/vm/eof/instructions_check.py +++ b/src/ethereum/prague/vm/eof/instructions_check.py @@ -249,8 +249,11 @@ def validate_jumpf(validator: Validator) -> None: current_outputs = current_section_type[1] target_outputs = target_section_type[1] - if target_outputs != 0x80 and target_outputs > current_outputs: - raise InvalidEof("Invalid stack height") + if target_outputs != 0x80: + if target_outputs > current_outputs: + raise InvalidEof("Invalid stack height") + validator.is_current_section_returning = True + reached_sections = validator.reached_code_sections[validator.current_index] reached_sections.add(target_index) @@ -562,6 +565,45 @@ def validate_return(validator: Validator) -> None: ) +def validate_retf(validator: Validator) -> None: + """ + Validate the RETF instructions. + + Parameters + ---------- + validator : `Validator` + The current validator instance. + """ + code = validator.current_code + position = Uint(validator.current_pc) + counter = validator.current_pc + 1 + current_metadata = validator.sections.get(validator.current_index, {}) + opcode = map_int_to_op(code[position], EofVersion.EOF1) + index = validator.current_index + eof_meta = validator.eof.metadata + + section_type = eof_meta.type_section_contents[index] + outputs = section_type[1] + if outputs == 0x80: + raise InvalidEof("RETF in non-returning section") + + validator.is_current_section_returning = True + + # Successor instruction positions + relative_offsets: List[int] = [] + + # Update Instruction Metadata + validator.current_pc = counter + current_metadata[position] = InstructionMetadata( + opcode=opcode, + pc_post_instruction=validator.current_pc, + relative_offsets=relative_offsets, + target_index=None, + container_index=None, + stack_height=None, + ) + + def validate_other_terminating_instructions(validator: Validator) -> None: """ Validate other terminating instructions. @@ -668,6 +710,7 @@ def validate_other_instructions(validator: Validator) -> None: Ops.RETURNCONTRACT: validate_returncontract, Ops.STOP: validate_stop, Ops.RETURN: validate_return, + Ops.RETF: validate_retf, } diff --git a/src/ethereum/prague/vm/eof/validation.py b/src/ethereum/prague/vm/eof/validation.py index eac1b03bc7..dc5b6921ca 100644 --- a/src/ethereum/prague/vm/eof/validation.py +++ b/src/ethereum/prague/vm/eof/validation.py @@ -159,8 +159,16 @@ def validate_code_section(validator: Validator) -> None: code_index = validator.current_index section_type = validator.eof.metadata.type_section_contents[code_index] section_inputs = section_type[0] + section_outputs = section_type[1] section_max_stack_height = Uint.from_be_bytes(section_type[2:]) + # If the return flag from the section type does not agree with + # the instructions in the code. + if ( + section_outputs != 0x80 and not validator.is_current_section_returning + ) or (section_outputs == 0x80 and validator.is_current_section_returning): + raise InvalidEof(f"Invalid return flag in section {code_index}") + computed_maximum_stack_height = 0 for index, position in enumerate(valid_opcode_positions): validator.current_pc = position @@ -249,6 +257,7 @@ def validate_eof_code(validator: Validator) -> None: validator.current_index = Uint(code_index) validator.current_code = code validator.current_pc = Uint(0) + validator.is_current_section_returning = False validator.sections[validator.current_index] = {} validate_code_section(validator) @@ -340,6 +349,7 @@ def validate_eof_container( current_index=Uint(0), current_code=metadata.code_section_contents[0], current_pc=Uint(0), + is_current_section_returning=False, sections={}, has_return_contract=False, has_stop=False, @@ -396,6 +406,7 @@ def parse_create_tx_call_data(data: bytes) -> Tuple[Eof, bytes]: current_index=Uint(0), current_code=eof.metadata.code_section_contents[0], current_pc=Uint(0), + is_current_section_returning=False, has_return_contract=False, has_stop=False, has_return=False, diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py index b858784439..9b9fb1b898 100644 --- a/tests/helpers/__init__.py +++ b/tests/helpers/__init__.py @@ -17,7 +17,7 @@ }, "latest_fork_tests": { "url": "https://github.com/gurukamath/latest_fork_tests.git", - "commit_hash": "1a9b58e", + "commit_hash": "b5c73d5", "fixture_path": "tests/fixtures/latest_fork_tests", }, } diff --git a/tests/prague/test_evm_tools.py b/tests/prague/test_evm_tools.py index ad6a9fccd5..3276e03a6b 100644 --- a/tests/prague/test_evm_tools.py +++ b/tests/prague/test_evm_tools.py @@ -38,6 +38,11 @@ "tests/fixtures/latest_fork_tests/state_tests/prague/eip2537_bls_12_381_precompiles", "tests/fixtures/latest_fork_tests/state_tests/prague/eip7702_set_code_tx", "tests/fixtures/latest_fork_tests/state_tests/prague/eip7692_eof_v1", + "tests/fixtures/ethereum_tests/EIPTests/StateTests/stEOF", + # TODO: The following evmone tests for EOF are currently disabled since + # some of the fixtures are malformed. They will be enabled once the + # new fixtures are available. + # "tests/fixtures/latest_fork_tests/evmone_tests/state_tests", ) IGNORE_TESTS = ( @@ -49,9 +54,6 @@ "tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_invalid_tx_invalid_auth_signature[fork_Prague-state_test-v_0,r_1,s_2**256-1]", "tests/fixtures/latest_fork_tests/state_tests/prague/eip7692_eof_v1", "tests/fixtures/ethereum_tests/EIPTests/StateTests/stEOF", - # # TODO: Run evmone tests after they have been fixed - # # There are currently some malformed fixtures in the evmone tests - # "tests/fixtures/latest_fork_tests/evmone_tests/state_tests", ) diff --git a/tests/prague/test_state_transition.py b/tests/prague/test_state_transition.py index 21789de7e8..57a0ee1278 100644 --- a/tests/prague/test_state_transition.py +++ b/tests/prague/test_state_transition.py @@ -107,8 +107,7 @@ "tests/fixtures/latest_fork_tests/blockchain_tests/prague/eip2537_bls_12_381_precompiles", "tests/fixtures/latest_fork_tests/blockchain_tests/prague/eip2935_historical_block_hashes_from_state", "tests/fixtures/latest_fork_tests/blockchain_tests/prague/eip7702_set_code_tx", - # TODO: Current test fixtures don't support EOF along with other - # EIPs. This will be fixed in the future. + "tests/fixtures/latest_fork_tests/blockchain_tests/prague/eip7692_eof_v1", )