From 513d519c440fbbd6363bd476db28c2eb98d26e5a Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 30 May 2024 19:38:09 -0600 Subject: [PATCH 1/7] new(tests) EIP-4200 relative jumps Port over ethereum/tests EIP-4200 tests, both validation and execution. Some duplicate tests were dropped, and some new immediate tests were added. Signed-off-by: Danno Ferrin --- src/ethereum_test_tools/vm/opcode.py | 14 +- .../eip4200_relative_jumps/__init__.py | 3 + .../eip4200_relative_jumps/helpers.py | 16 + .../eip4200_relative_jumps/spec.py | 5 + .../eip4200_relative_jumps/test_rjump.py | 533 ++++++++++++ .../eip4200_relative_jumps/test_rjumpi.py | 701 ++++++++++++++++ .../eip4200_relative_jumps/test_rjumpv.py | 792 ++++++++++++++++++ 7 files changed, 2056 insertions(+), 8 deletions(-) create mode 100644 tests/prague/eip7692_eof_v1/eip4200_relative_jumps/__init__.py create mode 100644 tests/prague/eip7692_eof_v1/eip4200_relative_jumps/helpers.py create mode 100644 tests/prague/eip7692_eof_v1/eip4200_relative_jumps/spec.py create mode 100644 tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py create mode 100644 tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py create mode 100644 tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py diff --git a/src/ethereum_test_tools/vm/opcode.py b/src/ethereum_test_tools/vm/opcode.py index bb3209babf..b8698b7ba0 100644 --- a/src/ethereum_test_tools/vm/opcode.py +++ b/src/ethereum_test_tools/vm/opcode.py @@ -5511,28 +5511,26 @@ class Opcodes(Opcode, Enum): Source: [EIP-7069](https://eips.ethereum.org/EIPS/eip-7069) """ - CREATE4 = Opcode(0xF7, popped_stack_items=5, pushed_stack_items=1) + RETURNDATALOAD = Opcode(0xF7, popped_stack_items=1) """ - !!! Note: This opcode is under development - - CREATE4() + RETURNDATALOAD(offset) ---- Description ---- + Copy 32 bytes from returndata at offset onto the stack Inputs ---- - - Outputs - ---- + - offset: byte offset in the return data from the last executed sub context to copy Fork ---- + EOF Gas ---- - + 3 """ REVERT = Opcode(0xFD, popped_stack_items=2) diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/__init__.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/__init__.py new file mode 100644 index 0000000000..2f3b343750 --- /dev/null +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/__init__.py @@ -0,0 +1,3 @@ +""" +EOF tests for EIP-4200 relative jumps +""" diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/helpers.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/helpers.py new file mode 100644 index 0000000000..fb0862dabb --- /dev/null +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/helpers.py @@ -0,0 +1,16 @@ +""" +EOF RJump tests helpers +""" +import itertools + +"""Storage addresses for common testing fields""" +_slot = itertools.count() +next(_slot) # don't use slot 0 +slot_code_worked = next(_slot) +slot_conditional_result = next(_slot) +slot_last_slot = next(_slot) + +"""Storage values for common testing fields""" +value_code_worked = 0x2015 +value_calldata_true = 10 +value_calldata_false = 11 diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/spec.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/spec.py new file mode 100644 index 0000000000..7bf760554f --- /dev/null +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/spec.py @@ -0,0 +1,5 @@ +""" +EOF V1 Constants used throughout all tests +""" + +EOF_FORK_NAME = "Prague" diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py new file mode 100644 index 0000000000..71418eb071 --- /dev/null +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py @@ -0,0 +1,533 @@ +""" +EOF JUMPF tests covering stack and code validation rules. +""" + +import pytest + +from ethereum_test_tools import Account, EOFException, EOFStateTestFiller, EOFTestFiller +from ethereum_test_tools.eof.v1 import Container, Section +from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION +from ethereum_test_tools.vm.opcode import Opcodes as Op + +from .helpers import slot_code_worked, value_code_worked +from .spec import EOF_FORK_NAME + +REFERENCE_SPEC_GIT_PATH = "EIPS/eip-4200.md" +REFERENCE_SPEC_VERSION = "17d4a8d12d2b5e0f2985c866376c16c8c6df7cba" + +pytestmark = pytest.mark.valid_from(EOF_FORK_NAME) + + +def test_rjump_positive_negative( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0001 (Valid) EOF code containing RJUMP (Positive, Negative)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH0 + + Op.RJUMPI[3] + + Op.RJUMP[7] + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP + + Op.RJUMP[-10], + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjump_positive_negative_with_data( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0001 (Valid) EOF code containing RJUMP (Positive, Negative)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH0 + + Op.RJUMPI[3] + + Op.RJUMP[7] + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP + + Op.RJUMP[-10], + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ), + Section.Data(data=b"\xde\xad\xbe\xef"), + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjump_zero( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0002 (Valid) EOF code containing RJUMP (Zero)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[0] + Op.SSTORE(slot_code_worked, value_code_worked) + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjump_maxes( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0003 EOF with RJUMP containing the maximum positive and negative offset (32767)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH0 + + Op.RJUMPI[3] # The push/jumpi is to allow the NOOPs to be forward referenced + + Op.RJUMP[0x7FFF] + + Op.NOOP * (0x7FFF - 7) + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP + + Op.RJUMP[0x8000], + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjump_truncated_rjump( + eof_test: EOFTestFiller, +): + """EOF1I4200_0001 (Invalid) EOF code containing truncated RJUMP""" + eof_test( + data=Container( + sections=[Section.Code(code=Op.RJUMP, code_outputs=NON_RETURNING_SECTION)], + ), + expect_exception=EOFException.UNDEFINED_EXCEPTION, + ) + + +def test_rjump_truncated_rjump_2( + eof_test: EOFTestFiller, +): + """EOF1I4200_0002 (Invalid) EOF code containing truncated RJUMP""" + eof_test( + data=Container( + sections=[Section.Code(code=Op.RJUMP + Op.STOP, code_outputs=NON_RETURNING_SECTION)], + ), + expect_exception=EOFException.UNDEFINED_EXCEPTION, + ) + + +def test_rjump_into_header( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0003 (Invalid) EOF code containing RJUMP with target outside code bounds + (Jumping into header) + """ + eof_test( + data=Container( + sections=[ + Section.Code(code=Op.RJUMP[-5], code_outputs=NON_RETURNING_SECTION), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_before_header( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0004 (Invalid) EOF code containing RJUMP with target outside code bounds + (Jumping before code begin) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[-23], + code_outputs=NON_RETURNING_SECTION, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_data( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0005 (Invalid) EOF code containing RJUMP with target outside code bounds + (Jumping into data section) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[2], + code_outputs=NON_RETURNING_SECTION, + ), + Section.Data(data=b"\xaa\xbb\xcc"), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_outside_other_section_before( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target outside code bounds (prior code section)""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.JUMPF[1], + code_outputs=NON_RETURNING_SECTION, + ), + Section.Code( + code=Op.RJUMP[-6], + code_outputs=NON_RETURNING_SECTION, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_outside_other_section_after( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target outside code bounds (Subsequent code section)""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.JUMPF[1], + code_outputs=NON_RETURNING_SECTION, + ), + Section.Code( + code=Op.RJUMP[3] + Op.JUMPF[2], + code_outputs=NON_RETURNING_SECTION, + ), + Section.Code( + code=Op.STOP, + code_outputs=NON_RETURNING_SECTION, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_after_container( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0006 (Invalid) EOF code containing RJUMP with target outside code bounds + (Jumping after code end) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[2], + code_outputs=NON_RETURNING_SECTION, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_to_code_end( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0007 (Invalid) EOF code containing RJUMP with target outside code bounds + (Jumping to code end) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[1] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_self( + eof_test: EOFTestFiller, +): + """EOF1I4200_0008 (Invalid) EOF code containing RJUMP with target self RJUMP immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[-1], + code_outputs=NON_RETURNING_SECTION, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_rjump( + eof_test: EOFTestFiller, +): + """EOF1I4200_0009 (Invalid) EOF code containing RJUMP with target other RJUMP immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[1] + Op.RJUMP[0], + code_outputs=NON_RETURNING_SECTION, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_rjumpi( + eof_test: EOFTestFiller, +): + """EOF1I4200_0010 (Invalid) EOF code containing RJUMP with target RJUMPI immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[5] + Op.STOP + Op.PUSH1(1) + Op.RJUMPI[-6] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_push( + eof_test: EOFTestFiller, +): + """EOF1I4200_0011 (Invalid) EOF code containing RJUMP with target PUSH immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMP[-4], + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_rjumpv( + eof_test: EOFTestFiller, +): + """EOF1I4200_0012 (Invalid) EOF code containing RJUMP with target RJUMPV immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[5] + Op.STOP + Op.PUSH1(1) + Op.RJUMPV[0] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_callf( + eof_test: EOFTestFiller, +): + """EOF1I4200_0013 (Invalid) EOF code containing RJUMP with target CALLF immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[2] + Op.CALLF(1) + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + ), + Section.Code( + code=Op.SSTORE(1, 1) + Op.RETF, + code_outputs=0, + max_stack_height=2, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_dupn( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target DUPN immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.PUSH1(1) + + Op.RJUMP[1] + + Op.DUPN[1] + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_swapn( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target SWAPN immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.PUSH1(1) + + Op.RJUMP[1] + + Op.SWAPN[1] + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_exchange( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target EXCHANGE immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.PUSH1(2) + + Op.PUSH1(3) + + Op.RJUMP[1] + + Op.EXCHANGE[0x00] + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=3, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_eofcreate( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target EOFCREATE immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.RJUMP[1] + Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=4, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.RETURNCONTRACT[0](0, 0), + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.STOP, + code_outputs=NON_RETURNING_SECTION, + ) + ] + ) + ), + ] + ) + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_returncontract( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target RETURNCONTRACT immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=4, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.RJUMP[5] + Op.RETURNCONTRACT[0](0, 0), + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.STOP, + code_outputs=NON_RETURNING_SECTION, + ) + ] + ) + ), + ] + ) + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py new file mode 100644 index 0000000000..1792b4b352 --- /dev/null +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py @@ -0,0 +1,701 @@ +""" +EOF JUMPF tests covering stack and code validation rules. +""" + +import pytest + +from ethereum_test_tools import ( + Account, + Environment, + EOFException, + EOFStateTestFiller, + EOFTestFiller, + StateTestFiller, + TestAddress, + Transaction, +) +from ethereum_test_tools.eof.v1 import Container, Section +from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION +from ethereum_test_tools.vm.opcode import Opcodes as Op + +from .helpers import ( + slot_code_worked, + slot_conditional_result, + value_calldata_false, + value_calldata_true, + value_code_worked, +) +from .spec import EOF_FORK_NAME + +REFERENCE_SPEC_GIT_PATH = "EIPS/eip-4200.md" +REFERENCE_SPEC_VERSION = "17d4a8d12d2b5e0f2985c866376c16c8c6df7cba" + +pytestmark = pytest.mark.valid_from(EOF_FORK_NAME) + + +@pytest.mark.parametrize( + "calldata", + [pytest.param(b"\x00", id="False"), pytest.param(b"\x01", id="True")], +) +def test_rjumpi_condition_forwards( + state_test: StateTestFiller, + calldata: bytes, +): + """Test RJUMPI contract switching based on external input""" + env = Environment() + tx = Transaction( + nonce=1, + gas_limit=10_000_000, + data=calldata, + ) + pre = { + TestAddress: Account(balance=10**18, nonce=tx.nonce), + tx.to: Account( + code=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + + Op.CALLDATALOAD + + Op.RJUMPI[6] + + Op.SSTORE(slot_conditional_result, value_calldata_false) + + Op.STOP + + Op.SSTORE(slot_conditional_result, value_calldata_true) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ] + ), + nonce=1, + ), + } + post = { + tx.to: Account( + storage={ + slot_conditional_result: value_calldata_false + if calldata == b"\0" + else value_calldata_true + } + ) + } + state_test(env=env, tx=tx, pre=pre, post=post) + + +@pytest.mark.parametrize( + "calldata", + [pytest.param(b"\x00", id="False"), pytest.param(b"\x01", id="True")], +) +def test_rjumpi_condition_backwards( + state_test: StateTestFiller, + calldata: bytes, +): + """Test RJUMPI contract switching based on external input""" + env = Environment() + tx = Transaction( + nonce=1, + gas_limit=10_000_000, + data=calldata, + ) + pre = { + TestAddress: Account(balance=10**18, nonce=tx.nonce), + tx.to: Account( + code=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPI[6] + + Op.SSTORE(slot_conditional_result, value_calldata_true) + + Op.STOP + + Op.PUSH0 + + Op.CALLDATALOAD + + Op.RJUMPI[-11] + + Op.SSTORE(slot_conditional_result, value_calldata_false) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ] + ), + nonce=1, + ), + } + post = { + tx.to: Account( + storage={ + slot_conditional_result: value_calldata_false + if calldata == b"\0" + else value_calldata_true + } + ) + } + state_test(env=env, tx=tx, pre=pre, post=post) + + +@pytest.mark.parametrize( + "calldata", + [pytest.param(b"\x00", id="False"), pytest.param(b"\x01", id="True")], +) +def test_rjumpi_condition_zero( + state_test: StateTestFiller, + calldata: bytes, +): + """Test RJUMPI contract switching based on external input""" + env = Environment() + tx = Transaction( + nonce=1, + gas_limit=10_000_000, + data=calldata, + ) + pre = { + TestAddress: Account(balance=10**18, nonce=tx.nonce), + tx.to: Account( + code=Container( + sections=[ + Section.Code( + code=Op.PUSH0 + + Op.CALLDATALOAD + + Op.RJUMPI[0] + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ] + ), + nonce=1, + ), + } + post = {tx.to: Account(storage={slot_code_worked: value_code_worked})} + state_test(env=env, tx=tx, pre=pre, post=post) + + +def test_rjumpi_forwards( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0004 (Valid) EOF code containing RJUMPI (Positive)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPI[3] + + Op.NOOP + + Op.NOOP + + Op.STOP + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpi_backwards( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0005 (Valid) EOF code containing RJUMPI (Negative)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPI[7] + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP + + Op.PUSH1(1) + + Op.RJUMPI[-12] + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpi_zero( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0006 (Valid) EOF code containing RJUMPI (Zero)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPI[0] + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpi_max_forward( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0007 (Valid) EOF with RJUMPI containing the maximum offset (32767)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPI[32767] + + Op.NOOP * 32768 + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpi_max_backward( + eof_state_test: EOFStateTestFiller, +): + """EOF with RJUMPI containing the maximum negative offset (-32768)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH0 + + Op.RJUMPI[0x7FFF] + + Op.NOOP * (0x7FFF - 7) + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP + + Op.PUSH0 + + Op.RJUMPI[0x8000] + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ), + + +def test_rjump_truncated( + eof_test: EOFTestFiller, +): + """EOF1I4200_0014 (Invalid) EOF code containing truncated RJUMPI""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + Op.RJUMPI, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.UNDEFINED_EXCEPTION, + ) + + +def test_rjump_truncated_2( + eof_test: EOFTestFiller, +): + """EOF1I4200_0015 (Invalid) EOF code containing truncated RJUMPI""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + Op.RJUMPI + b"\0", + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.UNDEFINED_EXCEPTION, + ) + + +def test_rjumpi_into_header( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0016 (Invalid) EOF code containing RJUMPI with target outside code bounds + (Jumping into header) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPI[-7] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_jump_before_header( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0017 (Invalid) EOF code containing RJUMPI with target outside code bounds + (Jumping to before code begin) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPI[-25] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_data( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0018 (Invalid) EOF code containing RJUMPI with target outside code bounds + (Jumping into data section) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPI[2] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ), + Section.Data(data=b"\xaa\xbb\xcc"), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_after_container( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0019 (Invalid) EOF code containing RJUMPI with target outside code bounds + (Jumping to after code end) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPI[2] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_to_code_end( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0020 (Invalid) EOF code containing RJUMPI with target outside code bounds + (Jumping to code end) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPI[1] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_self( + eof_test: EOFTestFiller, +): + """EOF1I4200_0021 (Invalid) EOF code containing RJUMPI with target same RJUMPI immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPI[-1] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_rjump( + eof_test: EOFTestFiller, +): + """EOF1I4200_0023 (Invalid) EOF code containing RJUMPI with target RJUMP immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPI[3] + Op.STOP + Op.RJUMP[-9], + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_rjumpi( + eof_test: EOFTestFiller, +): + """EOF1I4200_0022 (Invalid) EOF code containing RJUMPI with target other RJUMPI immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPI[5] + + Op.STOP + + Op.PUSH1(1) + + Op.RJUMPI[-11] + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_push( + eof_test: EOFTestFiller, +): + """EOF1I4200_0024 (Invalid) EOF code containing RJUMPI with target PUSH immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPI[-4] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_rjumpv( + eof_test: EOFTestFiller, +): + """EOF1I4200_0025 (Invalid) EOF code containing RJUMPI with target RJUMPV immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPI[5] + + Op.STOP + + Op.PUSH1(1) + + Op.RJUMPV[0] + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_callf( + eof_test: EOFTestFiller, +): + """EOF1I4200_0026 (Invalid) EOF code containing RJUMPI with target CALLF immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPI[2] + Op.CALLF(1) + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ), + Section.Code( + code=Op.SSTORE(1, 1) + Op.RETF, + code_outputs=0, + max_stack_height=2, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_dupn( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target DUPN immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.PUSH1(1) + + Op.PUSH1(1) + + Op.RJUMPI[1] + + Op.DUPN[1] + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=3, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_swapn( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target SWAPN immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.PUSH1(1) + + Op.PUSH1(1) + + Op.RJUMPI[1] + + Op.SWAPN[1] + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=3, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_exchange( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target EXCHANGE immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.PUSH1(2) + + Op.PUSH1(3) + + Op.PUSH1(1) + + Op.RJUMPI[1] + + Op.EXCHANGE[0x00] + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=4, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_eofcreate( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target EOFCREATE immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH0 + Op.RJUMPI[9] + Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=4, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.RETURNCONTRACT[0](0, 0), + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.STOP, + code_outputs=NON_RETURNING_SECTION, + ) + ] + ) + ), + ] + ) + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpi_into_returncontract( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target RETURNCONTRACT immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=4, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.PUSH0 + Op.RJUMPI[5] + Op.RETURNCONTRACT[0](0, 0), + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.STOP, + code_outputs=NON_RETURNING_SECTION, + ) + ] + ) + ), + ] + ) + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py new file mode 100644 index 0000000000..691f0cc23f --- /dev/null +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py @@ -0,0 +1,792 @@ +""" +EOF JUMPF tests covering stack and code validation rules. +""" + +import pytest + +from ethereum_test_tools import ( + Account, + Environment, + EOFException, + EOFStateTestFiller, + EOFTestFiller, + StateTestFiller, + TestAddress, + Transaction, +) +from ethereum_test_tools.eof.v1 import Container, Section +from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION +from ethereum_test_tools.vm.opcode import Opcodes as Op + +from .helpers import slot_code_worked, slot_conditional_result, value_code_worked +from .spec import EOF_FORK_NAME + +REFERENCE_SPEC_GIT_PATH = "EIPS/eip-4200.md" +REFERENCE_SPEC_VERSION = "17d4a8d12d2b5e0f2985c866376c16c8c6df7cba" + +pytestmark = pytest.mark.valid_from(EOF_FORK_NAME) + + +@pytest.mark.parametrize( + "calldata", + [ + pytest.param(b"\0" * 32, id="c0"), + pytest.param(b"\0" * 31 + b"\x01", id="c1"), + pytest.param(b"\0" * 31 + b"\x03", id="c3"), + pytest.param(b"\0" * 30 + b"\x10\x00", id="c256"), + pytest.param(b"\xff" * 32, id="cmax"), + ], +) +@pytest.mark.parametrize( + "table_size", + [ + pytest.param(1, id="t1"), + pytest.param(3, id="t3"), + pytest.param(256, id="t256"), + ], +) +def test_rjumpv_condition( + state_test: StateTestFiller, + calldata: bytes, + table_size: int, +): + """Test RJUMPV contract switching based on external input""" + value_fall_through = 0xFFFF + value_base = 0x1000 + + jump_table = b"" + for i in range(table_size): + jump_table += int.to_bytes((i + 1) * 7, 2, "big") + + jump_targets = b"" + for i in range(table_size): + jump_targets += Op.SSTORE(slot_conditional_result, i + value_base) + Op.STOP + + env = Environment() + tx = Transaction( + nonce=1, + gas_limit=10_000_000, + data=calldata, + ) + pre = { + TestAddress: Account(balance=10**18, nonce=tx.nonce), + tx.to: Account( + code=Container( + sections=[ + Section.Code( + code=Op.PUSH0 + + Op.CALLDATALOAD + + Op.RJUMPV + + int.to_bytes(table_size - 1, 1, "big") + + jump_table + + Op.SSTORE(slot_conditional_result, value_fall_through) + + Op.STOP + + jump_targets, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ] + ), + nonce=1, + ), + } + calldata_int = int.from_bytes(calldata, "big") + post = { + tx.to: Account( + storage={ + slot_conditional_result: calldata_int + 0x1000 + if calldata_int < table_size + else value_fall_through, + } + ) + } + state_test( + env=env, + tx=tx, + pre=pre, + post=post, + ) + + +def test_rjumpv_forwards( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0008 (Valid) EOF with RJUMPV table size 1 (Positive)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + + Op.RJUMPV[3] + + Op.NOOP + + Op.NOOP + + Op.STOP + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpv_backwards( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0009 (Valid) EOF with RJUMPV table size 1 (Negative)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + + Op.RJUMPI[7] + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP + + Op.PUSH1(0) + + Op.RJUMPV[-13] + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpv_zero( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0010 (Valid) EOF with RJUMPV table size 1 (Zero)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + + Op.RJUMPV[0] + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpv_size_3( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0011 (Valid) EOF with RJUMPV table size 3""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + + Op.RJUMPV[3, 0, -10] + + Op.NOOP + + Op.NOOP + + Op.STOP + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpv_full_table( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0012 (Valid) EOF with RJUMPV table size 256 (Target 0)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + + Op.RJUMPV[range(256)] + + Op.NOOP * 256 + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpv_full_table_mid( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0013 (Valid) EOF with RJUMPV table size 256 (Target 100)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(100) + + Op.RJUMPV[range(256)] + + Op.NOOP * 256 + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpv_full_table_end( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0014 (Valid) EOF with RJUMPV table size 256 (Target 254)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(254) + + Op.RJUMPV[range(256)] + + Op.NOOP * 256 + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpv_full_table_last( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0015 (Valid) EOF with RJUMPV table size 256 (Target 256)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH2(256) + + Op.RJUMPV[range(256)] + + Op.NOOP * 256 + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpv_max_forwards( + eof_state_test: EOFStateTestFiller, +): + """EOF1V4200_0016 (Valid) EOF with RJUMPV containing the maximum offset (32767)""" + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPV[32767] + + Op.NOOP * 32768 + + Op.SSTORE(slot_code_worked, value_code_worked) + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ], + ), + container_post=Account(storage={slot_code_worked: value_code_worked}), + ) + + +def test_rjumpv_truncated( + eof_test: EOFTestFiller, +): + """EOF1I4200_0027 (Invalid) EOF code containing RJUMPV with max_index 0 but no immediates""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV + b"\0", + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.UNDEFINED_EXCEPTION, + ) + + +def test_rjumpv_truncated_1( + eof_test: EOFTestFiller, +): + """EOF1I4200_0028 (Invalid) EOF code containing truncated RJUMPV""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.UNDEFINED_EXCEPTION, + ) + # - data: | + + +def test_rjumpv_truncated_2( + eof_test: EOFTestFiller, +): + """EOF1I4200_0029 (Invalid) EOF code containing truncated RJUMPV""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV + b"\0\0", + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.UNDEFINED_EXCEPTION, + ) + + +def test_rjumpv_truncated_3( + eof_test: EOFTestFiller, +): + """EOF1I4200_0030 (Invalid) EOF code containing truncated RJUMPV""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV + b"\0\0", + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.UNDEFINED_EXCEPTION, + ) + + +def test_rjumpv_truncated_4( + eof_test: EOFTestFiller, +): + """EOF code containing truncated RJUMPV table""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV + b"\2\0\0\0\0", + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.UNDEFINED_EXCEPTION, + ) + + +def test_rjumpv_into_header( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0031 (Invalid) EOF code containing RJUMPV with target outside code bounds + (Jumping into header) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV[-7] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_before_container( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0032 (Invalid) EOF code containing RJUMPV with target outside code bounds + (Jumping to before code begin) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV[-15] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_data( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0033 (Invalid) EOF code containing RJUMPV with target outside code bounds + (Jumping into data section) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV[2] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_after_container( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0034 (Invalid) EOF code containing RJUMPV with target outside code bounds + (Jumping to after code end) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV[2] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_at_end( + eof_test: EOFTestFiller, +): + """ + EOF1I4200_0035 (Invalid) EOF code containing RJUMPV with target outside code bounds + (Jumping to code end) + """ + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV[1] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_self( + eof_test: EOFTestFiller, +): + """EOF1I4200_0036 (Invalid) EOF code containing RJUMPV with target same RJUMPV immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + Op.RJUMPV[-1] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_rjump( + eof_test: EOFTestFiller, +): + """EOF1I4200_0037 (Invalid) EOF code containing RJUMPV with target RJUMP immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPV[5] + + Op.STOP + + Op.PUSH1(1) + + Op.RJUMP[-9] + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_rjumpi( + eof_test: EOFTestFiller, +): + """EOF1I4200_0038 (Invalid) EOF code containing RJUMPV with target RJUMPI immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPV[5] + + Op.STOP + + Op.PUSH1(1) + + Op.RJUMPI[-9] + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_push( + eof_test: EOFTestFiller, +): + """EOF1I4200_0039 (Invalid) EOF code containing RJUMPV with target PUSH immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPV[2] + + Op.STOP + + Op.PUSH1(1) + + Op.PUSH1(1) + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_rjumpv( + eof_test: EOFTestFiller, +): + """EOF1I4200_0040 (Invalid) EOF code containing RJUMPV with target other RJUMPV immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.RJUMPV[5] + + Op.STOP + + Op.PUSH1(1) + + Op.RJUMPV[0] + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_callf( + eof_test: EOFTestFiller, +): + """EOF1I4200_0041 (Invalid) EOF code containing RJUMPV with target CALLF immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + Op.RJUMPV[2] + Op.CALLF[1] + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ), + Section.Code( + code=Op.SSTORE(1, 1) + Op.RETF, + code_outputs=0, + max_stack_height=2, + ), + ] + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_dupn( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target DUPN immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.PUSH1(1) + + Op.PUSH1(0) + + Op.RJUMPV[1] + + Op.DUPN[1] + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=3, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_swapn( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target SWAPN immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.PUSH1(1) + + Op.PUSH1(0) + + Op.RJUMPV[1] + + Op.SWAPN[1] + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=3, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjump_into_exchange( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target EXCHANGE immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(1) + + Op.PUSH1(2) + + Op.PUSH1(3) + + Op.PUSH1(0) + + Op.RJUMPV[1] + + Op.EXCHANGE[0x00] + + Op.SSTORE + + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=4, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_eofcreate( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target EOFCREATE immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + Op.RJUMPV[9] + Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=4, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.RETURNCONTRACT[0](0, 0), + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.STOP, + code_outputs=NON_RETURNING_SECTION, + ) + ] + ) + ), + ] + ) + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +def test_rjumpv_into_returncontract( + eof_test: EOFTestFiller, +): + """EOF code containing RJUMP with target RETURNCONTRACT immediate""" + eof_test( + data=Container( + sections=[ + Section.Code( + code=Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=4, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.PUSH1(0) + Op.RJUMPV[5] + Op.RETURNCONTRACT[0](0, 0), + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ), + Section.Container( + container=Container( + sections=[ + Section.Code( + code=Op.STOP, + code_outputs=NON_RETURNING_SECTION, + ) + ] + ) + ), + ] + ) + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) From aba3c14a9979109defd3985c5091cd6bfddc2284 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 30 May 2024 19:40:27 -0600 Subject: [PATCH 2/7] accidental commit Signed-off-by: Danno Ferrin --- src/ethereum_test_tools/vm/opcode.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ethereum_test_tools/vm/opcode.py b/src/ethereum_test_tools/vm/opcode.py index b8698b7ba0..bb3209babf 100644 --- a/src/ethereum_test_tools/vm/opcode.py +++ b/src/ethereum_test_tools/vm/opcode.py @@ -5511,26 +5511,28 @@ class Opcodes(Opcode, Enum): Source: [EIP-7069](https://eips.ethereum.org/EIPS/eip-7069) """ - RETURNDATALOAD = Opcode(0xF7, popped_stack_items=1) + CREATE4 = Opcode(0xF7, popped_stack_items=5, pushed_stack_items=1) """ - RETURNDATALOAD(offset) + !!! Note: This opcode is under development + + CREATE4() ---- Description ---- - Copy 32 bytes from returndata at offset onto the stack Inputs ---- - - offset: byte offset in the return data from the last executed sub context to copy + + Outputs + ---- Fork ---- - EOF Gas ---- - 3 + """ REVERT = Opcode(0xFD, popped_stack_items=2) From 06dc6e62fc816a32a2c2caf8d198c95822de9305 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Mon, 3 Jun 2024 23:48:46 +0000 Subject: [PATCH 3/7] fix(tests): EIP-4200: rebase fixes --- tests/prague/eip7692_eof_v1/eip4200_relative_jumps/spec.py | 5 ----- .../eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py | 2 +- .../eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py | 1 + .../eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py | 2 +- 4 files changed, 3 insertions(+), 7 deletions(-) delete mode 100644 tests/prague/eip7692_eof_v1/eip4200_relative_jumps/spec.py diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/spec.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/spec.py deleted file mode 100644 index 7bf760554f..0000000000 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/spec.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -EOF V1 Constants used throughout all tests -""" - -EOF_FORK_NAME = "Prague" diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py index 71418eb071..40cdddf791 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py @@ -9,7 +9,7 @@ from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION from ethereum_test_tools.vm.opcode import Opcodes as Op -from .helpers import slot_code_worked, value_code_worked +from .. import EOF_FORK_NAME from .spec import EOF_FORK_NAME REFERENCE_SPEC_GIT_PATH = "EIPS/eip-4200.md" diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py index 1792b4b352..9720f1d1a7 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py @@ -18,6 +18,7 @@ from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION from ethereum_test_tools.vm.opcode import Opcodes as Op +from .. import EOF_FORK_NAME from .helpers import ( slot_code_worked, slot_conditional_result, diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py index 691f0cc23f..ae9670453f 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py @@ -18,7 +18,7 @@ from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION from ethereum_test_tools.vm.opcode import Opcodes as Op -from .helpers import slot_code_worked, slot_conditional_result, value_code_worked +from .. import EOF_FORK_NAME from .spec import EOF_FORK_NAME REFERENCE_SPEC_GIT_PATH = "EIPS/eip-4200.md" From a5d8897c6146acd0f29f5b170448038d5931b08a Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Mon, 3 Jun 2024 23:49:11 +0000 Subject: [PATCH 4/7] fix(fw): Add missing TRUNCATED_INSTRUCTION EOF exception --- src/ethereum_test_tools/exceptions/evmone_exceptions.py | 1 + src/ethereum_test_tools/exceptions/exceptions.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/ethereum_test_tools/exceptions/evmone_exceptions.py b/src/ethereum_test_tools/exceptions/evmone_exceptions.py index a51d65c995..efe6b3dd71 100644 --- a/src/ethereum_test_tools/exceptions/evmone_exceptions.py +++ b/src/ethereum_test_tools/exceptions/evmone_exceptions.py @@ -69,6 +69,7 @@ class EvmoneExceptionMapper: ), ExceptionMessage(EOFException.INVALID_MAX_STACK_HEIGHT, "err: invalid_max_stack_height"), ExceptionMessage(EOFException.INVALID_DATALOADN_INDEX, "err: invalid_dataloadn_index"), + ExceptionMessage(EOFException.TRUNCATED_INSTRUCTION, "err: truncated_instruction"), ) def __init__(self) -> None: diff --git a/src/ethereum_test_tools/exceptions/exceptions.py b/src/ethereum_test_tools/exceptions/exceptions.py index b9261a8f49..261a1bad5b 100644 --- a/src/ethereum_test_tools/exceptions/exceptions.py +++ b/src/ethereum_test_tools/exceptions/exceptions.py @@ -680,6 +680,10 @@ class EOFException(ExceptionBase): """ A DATALOADN instruction has out-of-bounds index for the data section. """ + TRUNCATED_INSTRUCTION = auto() + """ + EOF container's code section has truncated instruction. + """ """ From c9b0ac40b458c393526c61dec14131ec2662ca35 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 4 Jun 2024 00:01:52 +0000 Subject: [PATCH 5/7] new(tests): EOF: EIP-4200: more tests --- .../eip4200_relative_jumps/helpers.py | 10 + .../eip4200_relative_jumps/test_rjump.py | 109 +++- .../eip4200_relative_jumps/test_rjumpi.py | 112 +++- .../eip4200_relative_jumps/test_rjumpv.py | 528 ++++++++++++++---- 4 files changed, 643 insertions(+), 116 deletions(-) diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/helpers.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/helpers.py index fb0862dabb..a1f3a161cb 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/helpers.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/helpers.py @@ -2,6 +2,7 @@ EOF RJump tests helpers """ import itertools +from enum import Enum """Storage addresses for common testing fields""" _slot = itertools.count() @@ -14,3 +15,12 @@ value_code_worked = 0x2015 value_calldata_true = 10 value_calldata_false = 11 + + +class JumpDirection(Enum): + """ + Enum for the direction of the jump + """ + + FORWARD = 1 + BACKWARD = -1 diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py index 40cdddf791..cd007b1cfb 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py @@ -10,7 +10,7 @@ from ethereum_test_tools.vm.opcode import Opcodes as Op from .. import EOF_FORK_NAME -from .spec import EOF_FORK_NAME +from .helpers import JumpDirection, slot_code_worked, value_code_worked REFERENCE_SPEC_GIT_PATH = "EIPS/eip-4200.md" REFERENCE_SPEC_VERSION = "17d4a8d12d2b5e0f2985c866376c16c8c6df7cba" @@ -115,7 +115,7 @@ def test_rjump_truncated_rjump( data=Container( sections=[Section.Code(code=Op.RJUMP, code_outputs=NON_RETURNING_SECTION)], ), - expect_exception=EOFException.UNDEFINED_EXCEPTION, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, ) @@ -127,7 +127,7 @@ def test_rjump_truncated_rjump_2( data=Container( sections=[Section.Code(code=Op.RJUMP + Op.STOP, code_outputs=NON_RETURNING_SECTION)], ), - expect_exception=EOFException.UNDEFINED_EXCEPTION, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, ) @@ -327,15 +327,87 @@ def test_rjump_into_rjumpi( ) -def test_rjump_into_push( +@pytest.mark.parametrize("jump", [JumpDirection.FORWARD, JumpDirection.BACKWARD]) +def test_rjump_into_push_1(eof_test: EOFTestFiller, jump: JumpDirection): + """EOF1I4200_0011 (Invalid) EOF code containing RJUMP with target PUSH1 immediate""" + code = ( + Op.PUSH1[1] + Op.RJUMP[-4] if jump == JumpDirection.BACKWARD else Op.RJUMP[1] + Op.PUSH1[1] + ) + eof_test( + data=Container( + sections=[ + Section.Code( + code=code, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ), + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +@pytest.mark.parametrize( + "opcode", + [ + Op.PUSH2, + Op.PUSH3, + Op.PUSH4, + Op.PUSH5, + Op.PUSH6, + Op.PUSH7, + Op.PUSH8, + Op.PUSH9, + Op.PUSH10, + Op.PUSH11, + Op.PUSH12, + Op.PUSH13, + Op.PUSH14, + Op.PUSH15, + Op.PUSH16, + Op.PUSH17, + Op.PUSH18, + Op.PUSH19, + Op.PUSH20, + Op.PUSH21, + Op.PUSH22, + Op.PUSH23, + Op.PUSH24, + Op.PUSH25, + Op.PUSH26, + Op.PUSH27, + Op.PUSH28, + Op.PUSH29, + Op.PUSH30, + Op.PUSH31, + Op.PUSH32, + ], +) +@pytest.mark.parametrize("jump", [JumpDirection.FORWARD, JumpDirection.BACKWARD]) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) +def test_rjump_into_push_n( eof_test: EOFTestFiller, + opcode: Op, + jump: JumpDirection, + data_portion_end: bool, ): - """EOF1I4200_0011 (Invalid) EOF code containing RJUMP with target PUSH immediate""" + """EOF1I4200_0011 (Invalid) EOF code containing RJUMP with target PUSH2+ immediate""" + data_portion_length = int.from_bytes(opcode) - 0x5F + if jump == JumpDirection.FORWARD: + offset = data_portion_length if data_portion_end else 1 + code = Op.RJUMP[offset] + opcode[0] + else: + offset = -4 if data_portion_end else -4 - data_portion_length + 1 + code = opcode[0] + Op.RJUMP[offset] eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) + Op.RJUMP[-4], + code=code, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ), @@ -345,15 +417,29 @@ def test_rjump_into_push( ) +@pytest.mark.parametrize("target_rjumpv_table_size", [1, 256]) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) def test_rjump_into_rjumpv( eof_test: EOFTestFiller, + target_rjumpv_table_size: int, + data_portion_end: bool, ): """EOF1I4200_0012 (Invalid) EOF code containing RJUMP with target RJUMPV immediate""" + invalid_destination = 4 + (2 * target_rjumpv_table_size) if data_portion_end else 4 + target_jump_table = [0 for _ in range(target_rjumpv_table_size)] eof_test( data=Container( sections=[ Section.Code( - code=Op.RJUMP[5] + Op.STOP + Op.PUSH1(1) + Op.RJUMPV[0] + Op.STOP, + code=Op.RJUMP[invalid_destination] + + Op.STOP + + Op.PUSH1(1) + + Op.RJUMPV[target_jump_table] + + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ) @@ -363,15 +449,22 @@ def test_rjump_into_rjumpv( ) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) def test_rjump_into_callf( eof_test: EOFTestFiller, + data_portion_end: bool, ): """EOF1I4200_0013 (Invalid) EOF code containing RJUMP with target CALLF immediate""" + invalid_destination = 2 if data_portion_end else 1 eof_test( data=Container( sections=[ Section.Code( - code=Op.RJUMP[2] + Op.CALLF(1) + Op.STOP, + code=Op.RJUMP[invalid_destination] + Op.CALLF[1] + Op.STOP, code_outputs=NON_RETURNING_SECTION, ), Section.Code( diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py index 9720f1d1a7..37891dfcb2 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py @@ -20,13 +20,13 @@ from .. import EOF_FORK_NAME from .helpers import ( + JumpDirection, slot_code_worked, slot_conditional_result, value_calldata_false, value_calldata_true, value_code_worked, ) -from .spec import EOF_FORK_NAME REFERENCE_SPEC_GIT_PATH = "EIPS/eip-4200.md" REFERENCE_SPEC_VERSION = "17d4a8d12d2b5e0f2985c866376c16c8c6df7cba" @@ -300,7 +300,7 @@ def test_rjump_truncated( ) ], ), - expect_exception=EOFException.UNDEFINED_EXCEPTION, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, ) @@ -318,7 +318,7 @@ def test_rjump_truncated_2( ) ], ), - expect_exception=EOFException.UNDEFINED_EXCEPTION, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, ) @@ -487,15 +487,22 @@ def test_rjumpi_into_rjumpi( ) -def test_rjumpi_into_push( +@pytest.mark.parametrize("jump", [JumpDirection.FORWARD, JumpDirection.BACKWARD]) +def test_rjumpi_into_push_1( eof_test: EOFTestFiller, + jump: JumpDirection, ): - """EOF1I4200_0024 (Invalid) EOF code containing RJUMPI with target PUSH immediate""" + """EOF1I4200_0024 (Invalid) EOF code containing RJUMPI with target PUSH1 immediate""" + code = ( + Op.PUSH1(1) + Op.RJUMPI[-4] + Op.STOP + if jump == JumpDirection.BACKWARD + else Op.PUSH1(1) + Op.RJUMPI[1] + Op.STOP + ) eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) + Op.RJUMPI[-4] + Op.STOP, + code=code, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ) @@ -505,19 +512,99 @@ def test_rjumpi_into_push( ) +@pytest.mark.parametrize( + "opcode", + [ + Op.PUSH2, + Op.PUSH3, + Op.PUSH4, + Op.PUSH5, + Op.PUSH6, + Op.PUSH7, + Op.PUSH8, + Op.PUSH9, + Op.PUSH10, + Op.PUSH11, + Op.PUSH12, + Op.PUSH13, + Op.PUSH14, + Op.PUSH15, + Op.PUSH16, + Op.PUSH17, + Op.PUSH18, + Op.PUSH19, + Op.PUSH20, + Op.PUSH21, + Op.PUSH22, + Op.PUSH23, + Op.PUSH24, + Op.PUSH25, + Op.PUSH26, + Op.PUSH27, + Op.PUSH28, + Op.PUSH29, + Op.PUSH30, + Op.PUSH31, + Op.PUSH32, + ], +) +@pytest.mark.parametrize("jump", [JumpDirection.FORWARD, JumpDirection.BACKWARD]) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) +def test_rjumpi_into_push_n( + eof_test: EOFTestFiller, + opcode: Op, + jump: JumpDirection, + data_portion_end: bool, +): + """EOF1I4200_0024 (Invalid) EOF code containing RJUMPI with target PUSH2+ immediate""" + data_portion_length = int.from_bytes(opcode) - 0x5F + if jump == JumpDirection.FORWARD: + offset = data_portion_length if data_portion_end else 1 + code = Op.PUSH1(1) + Op.RJUMPI[offset] + opcode[0] + else: + offset = -4 if data_portion_end else -4 - data_portion_length + 1 + code = opcode[0] + Op.RJUMPI[offset] + eof_test( + data=Container( + sections=[ + Section.Code( + code=code, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +@pytest.mark.parametrize("target_rjumpv_table_size", [1, 256]) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) def test_rjumpi_into_rjumpv( eof_test: EOFTestFiller, + target_rjumpv_table_size: int, + data_portion_end: bool, ): """EOF1I4200_0025 (Invalid) EOF code containing RJUMPI with target RJUMPV immediate""" + invalid_destination = 4 + (2 * target_rjumpv_table_size) if data_portion_end else 4 + target_jump_table = [0 for _ in range(target_rjumpv_table_size)] eof_test( data=Container( sections=[ Section.Code( code=Op.PUSH1(1) - + Op.RJUMPI[5] + + Op.RJUMPI[invalid_destination] + Op.STOP + Op.PUSH1(1) - + Op.RJUMPV[0] + + Op.RJUMPV[target_jump_table] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, @@ -528,15 +615,22 @@ def test_rjumpi_into_rjumpv( ) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) def test_rjumpi_into_callf( eof_test: EOFTestFiller, + data_portion_end: bool, ): """EOF1I4200_0026 (Invalid) EOF code containing RJUMPI with target CALLF immediate""" + invalid_destination = 2 if data_portion_end else 1 eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) + Op.RJUMPI[2] + Op.CALLF(1) + Op.STOP, + code=Op.PUSH1(1) + Op.RJUMPI[invalid_destination] + Op.CALLF[1] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ), diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py index ae9670453f..6d4f63cb69 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py @@ -4,22 +4,13 @@ import pytest -from ethereum_test_tools import ( - Account, - Environment, - EOFException, - EOFStateTestFiller, - EOFTestFiller, - StateTestFiller, - TestAddress, - Transaction, -) +from ethereum_test_tools import Account, EOFException, EOFStateTestFiller, EOFTestFiller from ethereum_test_tools.eof.v1 import Container, Section from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION from ethereum_test_tools.vm.opcode import Opcodes as Op from .. import EOF_FORK_NAME -from .spec import EOF_FORK_NAME +from .helpers import JumpDirection, slot_code_worked, slot_conditional_result, value_code_worked REFERENCE_SPEC_GIT_PATH = "EIPS/eip-4200.md" REFERENCE_SPEC_VERSION = "17d4a8d12d2b5e0f2985c866376c16c8c6df7cba" @@ -30,11 +21,12 @@ @pytest.mark.parametrize( "calldata", [ - pytest.param(b"\0" * 32, id="c0"), - pytest.param(b"\0" * 31 + b"\x01", id="c1"), - pytest.param(b"\0" * 31 + b"\x03", id="c3"), - pytest.param(b"\0" * 30 + b"\x10\x00", id="c256"), - pytest.param(b"\xff" * 32, id="cmax"), + pytest.param(0, id="c0"), + pytest.param(1, id="c1"), + pytest.param(3, id="c3"), + pytest.param(255, id="c255"), + pytest.param(256, id="c256"), + pytest.param(2**256 - 1, id="c2^256-1"), ], ) @pytest.mark.parametrize( @@ -46,65 +38,44 @@ ], ) def test_rjumpv_condition( - state_test: StateTestFiller, - calldata: bytes, + eof_state_test: EOFStateTestFiller, + calldata: int, table_size: int, ): """Test RJUMPV contract switching based on external input""" value_fall_through = 0xFFFF - value_base = 0x1000 - - jump_table = b"" - for i in range(table_size): - jump_table += int.to_bytes((i + 1) * 7, 2, "big") + value_base = 0x1000 # Force a `PUSH2` instruction to be used on all targets + target_length = 7 + jump_table = [(i + 1) * target_length for i in range(table_size)] jump_targets = b"" for i in range(table_size): jump_targets += Op.SSTORE(slot_conditional_result, i + value_base) + Op.STOP - env = Environment() - tx = Transaction( - nonce=1, - gas_limit=10_000_000, - data=calldata, - ) - pre = { - TestAddress: Account(balance=10**18, nonce=tx.nonce), - tx.to: Account( - code=Container( - sections=[ - Section.Code( - code=Op.PUSH0 - + Op.CALLDATALOAD - + Op.RJUMPV - + int.to_bytes(table_size - 1, 1, "big") - + jump_table - + Op.SSTORE(slot_conditional_result, value_fall_through) - + Op.STOP - + jump_targets, - code_outputs=NON_RETURNING_SECTION, - max_stack_height=2, - ) - ] - ), - nonce=1, - ), - } - calldata_int = int.from_bytes(calldata, "big") - post = { - tx.to: Account( + fall_through_case = Op.SSTORE(slot_conditional_result, value_fall_through) + Op.STOP + + eof_state_test( + data=Container( + sections=[ + Section.Code( + code=Op.PUSH0 + + Op.CALLDATALOAD + + Op.RJUMPV[jump_table] + + fall_through_case + + jump_targets, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=2, + ) + ] + ), + tx_data=calldata.to_bytes(32, "big"), + container_post=Account( storage={ - slot_conditional_result: calldata_int + 0x1000 - if calldata_int < table_size + slot_conditional_result: calldata + value_base + if calldata < table_size else value_fall_through, } - ) - } - state_test( - env=env, - tx=tx, - pre=pre, - post=post, + ), ) @@ -325,7 +296,7 @@ def test_rjumpv_truncated( ) ], ), - expect_exception=EOFException.UNDEFINED_EXCEPTION, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, ) @@ -343,7 +314,7 @@ def test_rjumpv_truncated_1( ) ], ), - expect_exception=EOFException.UNDEFINED_EXCEPTION, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, ) # - data: | @@ -362,7 +333,7 @@ def test_rjumpv_truncated_2( ) ], ), - expect_exception=EOFException.UNDEFINED_EXCEPTION, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, ) @@ -380,7 +351,7 @@ def test_rjumpv_truncated_3( ) ], ), - expect_exception=EOFException.UNDEFINED_EXCEPTION, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, ) @@ -398,22 +369,35 @@ def test_rjumpv_truncated_4( ) ], ), - expect_exception=EOFException.UNDEFINED_EXCEPTION, + expect_exception=EOFException.TRUNCATED_INSTRUCTION, ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjumpv_into_header( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """ EOF1I4200_0031 (Invalid) EOF code containing RJUMPV with target outside code bounds (Jumping into header) """ + invalid_destination = -5 - (2 * table_size) + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) + Op.RJUMPV[-7] + Op.STOP, + code=Op.PUSH1(1) + Op.RJUMPV[jump_table] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ) @@ -423,18 +407,31 @@ def test_rjumpv_into_header( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjumpv_before_container( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """ EOF1I4200_0032 (Invalid) EOF code containing RJUMPV with target outside code bounds (Jumping to before code begin) """ + invalid_destination = -13 - (2 * table_size) + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) + Op.RJUMPV[-15] + Op.STOP, + code=Op.PUSH1(1) + Op.RJUMPV[jump_table] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ) @@ -444,39 +441,66 @@ def test_rjumpv_before_container( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjumpv_into_data( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """ EOF1I4200_0033 (Invalid) EOF code containing RJUMPV with target outside code bounds (Jumping into data section) """ + invalid_destination = 2 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) + Op.RJUMPV[2] + Op.STOP, + code=Op.PUSH1(1) + Op.RJUMPV[jump_table] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, - ) + ), + Section.Data(data=b"\xaa\xbb\xcc"), ], ), expect_exception=EOFException.INVALID_RJUMP_DESTINATION, ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjumpv_after_container( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """ EOF1I4200_0034 (Invalid) EOF code containing RJUMPV with target outside code bounds (Jumping to after code end) """ + invalid_destination = 2 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) + Op.RJUMPV[2] + Op.STOP, + code=Op.PUSH1(1) + Op.RJUMPV[jump_table] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ) @@ -486,18 +510,31 @@ def test_rjumpv_after_container( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjumpv_at_end( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """ EOF1I4200_0035 (Invalid) EOF code containing RJUMPV with target outside code bounds (Jumping to code end) """ + invalid_destination = 1 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) + Op.RJUMPV[1] + Op.STOP, + code=Op.PUSH1(1) + Op.RJUMPV[jump_table] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ) @@ -507,15 +544,34 @@ def test_rjumpv_at_end( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) def test_rjumpv_into_self( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, + data_portion_end: bool, ): """EOF1I4200_0036 (Invalid) EOF code containing RJUMPV with target same RJUMPV immediate""" + invalid_destination = -1 if data_portion_end else -(2 * table_size) - 1 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) + Op.RJUMPV[-1] + Op.STOP, + code=Op.PUSH1(1) + Op.RJUMPV[jump_table] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ) @@ -525,20 +581,39 @@ def test_rjumpv_into_self( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) def test_rjumpv_into_rjump( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, + data_portion_end: bool, ): """EOF1I4200_0037 (Invalid) EOF code containing RJUMPV with target RJUMP immediate""" + invalid_destination = 3 if data_portion_end else 2 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination + if table_size > 1: + valid_index = 0 + if valid_index == invalid_index: + valid_index += 1 + jump_table[valid_index] = 1 eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) - + Op.RJUMPV[5] - + Op.STOP - + Op.PUSH1(1) - + Op.RJUMP[-9] - + Op.STOP, + code=Op.PUSH1(1) + Op.RJUMPV[jump_table] + Op.STOP + Op.RJUMP[0] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ) @@ -548,19 +623,43 @@ def test_rjumpv_into_rjump( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) def test_rjumpv_into_rjumpi( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, + data_portion_end: bool, ): """EOF1I4200_0038 (Invalid) EOF code containing RJUMPV with target RJUMPI immediate""" + invalid_destination = 5 if data_portion_end else 4 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination + if table_size > 1: + valid_index = 0 + if valid_index == invalid_index: + valid_index += 1 + jump_table[valid_index] = 1 eof_test( data=Container( sections=[ Section.Code( code=Op.PUSH1(1) - + Op.RJUMPV[5] + + Op.RJUMPV[jump_table] + Op.STOP + Op.PUSH1(1) - + Op.RJUMPI[-9] + + Op.RJUMPI[0] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, @@ -571,21 +670,141 @@ def test_rjumpv_into_rjumpi( ) -def test_rjumpv_into_push( +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) +@pytest.mark.parametrize("jump", [JumpDirection.FORWARD, JumpDirection.BACKWARD]) +def test_rjumpv_into_push_1( eof_test: EOFTestFiller, + jump: JumpDirection, + table_size: int, + invalid_index: int, ): - """EOF1I4200_0039 (Invalid) EOF code containing RJUMPV with target PUSH immediate""" + """EOF1I4200_0039 (Invalid) EOF code containing RJUMPV with target PUSH1 immediate""" + if jump == JumpDirection.FORWARD: + invalid_destination = 2 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination + code = ( + Op.PUSH1(1) + + Op.RJUMPV[jump_table] + + Op.STOP + + Op.PUSH1(1) + + Op.PUSH1(1) + + Op.SSTORE + + Op.STOP + ) + else: + invalid_destination = -(2 * table_size) - 3 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination + code = Op.PUSH1(1) + Op.RJUMPV[jump_table] + Op.STOP eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(1) - + Op.RJUMPV[2] - + Op.STOP - + Op.PUSH1(1) - + Op.PUSH1(1) - + Op.SSTORE - + Op.STOP, + code=code, + code_outputs=NON_RETURNING_SECTION, + max_stack_height=1, + ) + ], + ), + expect_exception=EOFException.INVALID_RJUMP_DESTINATION, + ) + + +@pytest.mark.parametrize( + "opcode", + [ + Op.PUSH2, + Op.PUSH3, + Op.PUSH4, + Op.PUSH5, + Op.PUSH6, + Op.PUSH7, + Op.PUSH8, + Op.PUSH9, + Op.PUSH10, + Op.PUSH11, + Op.PUSH12, + Op.PUSH13, + Op.PUSH14, + Op.PUSH15, + Op.PUSH16, + Op.PUSH17, + Op.PUSH18, + Op.PUSH19, + Op.PUSH20, + Op.PUSH21, + Op.PUSH22, + Op.PUSH23, + Op.PUSH24, + Op.PUSH25, + Op.PUSH26, + Op.PUSH27, + Op.PUSH28, + Op.PUSH29, + Op.PUSH30, + Op.PUSH31, + Op.PUSH32, + ], +) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) +@pytest.mark.parametrize("jump", [JumpDirection.FORWARD, JumpDirection.BACKWARD]) +def test_rjumpv_into_push_n( + eof_test: EOFTestFiller, + opcode: Op, + jump: JumpDirection, + table_size: int, + invalid_index: int, + data_portion_end: bool, +): + """EOF1I4200_0039 (Invalid) EOF code containing RJUMPV with target PUSH1 immediate""" + data_portion_length = int.from_bytes(opcode) - 0x5F + if jump == JumpDirection.FORWARD: + invalid_destination = data_portion_length + 1 if data_portion_end else 2 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination + code = ( + Op.PUSH1(1) + + Op.RJUMPV[jump_table] + + Op.STOP + + opcode[1] + + Op.PUSH1(1) + + Op.SSTORE + + Op.STOP + ) + else: + invalid_destination = ( + -(2 * table_size) - 3 + if data_portion_end + else -(2 * table_size) - 2 - data_portion_length + ) + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination + code = opcode[1] + Op.RJUMPV[jump_table] + Op.STOP + eof_test( + data=Container( + sections=[ + Section.Code( + code=code, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ) @@ -595,19 +814,41 @@ def test_rjumpv_into_push( ) +@pytest.mark.parametrize( + "source_table_size,invalid_index", + [ + pytest.param(1, 0, id="s1i0"), + pytest.param(256, 0, id="s256i0"), + pytest.param(256, 255, id="s256i255"), + ], +) +@pytest.mark.parametrize("target_table_size", [1, 256], ids=["t1", "t256"]) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) def test_rjumpv_into_rjumpv( eof_test: EOFTestFiller, + source_table_size: int, + target_table_size: int, + invalid_index: int, + data_portion_end: bool, ): """EOF1I4200_0040 (Invalid) EOF code containing RJUMPV with target other RJUMPV immediate""" + invalid_destination = 4 + (2 * target_table_size) if data_portion_end else 4 + source_jump_table = [0 for _ in range(source_table_size)] + source_jump_table[invalid_index] = invalid_destination + target_jump_table = [0 for _ in range(target_table_size)] eof_test( data=Container( sections=[ Section.Code( code=Op.PUSH1(1) - + Op.RJUMPV[5] + + Op.RJUMPV[source_jump_table] + Op.STOP + Op.PUSH1(1) - + Op.RJUMPV[0] + + Op.RJUMPV[target_jump_table] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, @@ -618,15 +859,34 @@ def test_rjumpv_into_rjumpv( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) +@pytest.mark.parametrize( + "data_portion_end", + [True, False], + ids=["data_portion_end", "data_portion_start"], +) def test_rjumpv_into_callf( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, + data_portion_end: bool, ): """EOF1I4200_0041 (Invalid) EOF code containing RJUMPV with target CALLF immediate""" + invalid_destination = 2 if data_portion_end else 1 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(0) + Op.RJUMPV[2] + Op.CALLF[1] + Op.STOP, + code=Op.PUSH1(0) + Op.RJUMPV[jump_table] + Op.CALLF[1] + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=1, ), @@ -641,10 +901,23 @@ def test_rjumpv_into_callf( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjumpv_into_dupn( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """EOF code containing RJUMP with target DUPN immediate""" + invalid_destination = 1 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ @@ -652,7 +925,7 @@ def test_rjumpv_into_dupn( code=Op.PUSH1(1) + Op.PUSH1(1) + Op.PUSH1(0) - + Op.RJUMPV[1] + + Op.RJUMPV[jump_table] + Op.DUPN[1] + Op.SSTORE + Op.STOP, @@ -665,10 +938,23 @@ def test_rjumpv_into_dupn( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjumpv_into_swapn( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """EOF code containing RJUMP with target SWAPN immediate""" + invalid_destination = 1 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ @@ -676,7 +962,7 @@ def test_rjumpv_into_swapn( code=Op.PUSH1(1) + Op.PUSH1(1) + Op.PUSH1(0) - + Op.RJUMPV[1] + + Op.RJUMPV[jump_table] + Op.SWAPN[1] + Op.SSTORE + Op.STOP, @@ -689,10 +975,23 @@ def test_rjumpv_into_swapn( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjump_into_exchange( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """EOF code containing RJUMP with target EXCHANGE immediate""" + invalid_destination = 1 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ @@ -714,15 +1013,31 @@ def test_rjump_into_exchange( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjumpv_into_eofcreate( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """EOF code containing RJUMP with target EOFCREATE immediate""" + invalid_destination = 9 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ Section.Code( - code=Op.PUSH1(0) + Op.RJUMPV[9] + Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP, + code=Op.PUSH1(0) + + Op.RJUMPV[jump_table] + + Op.EOFCREATE[0](0, 0, 0, 0) + + Op.STOP, code_outputs=NON_RETURNING_SECTION, max_stack_height=4, ), @@ -753,10 +1068,23 @@ def test_rjumpv_into_eofcreate( ) +@pytest.mark.parametrize( + "table_size,invalid_index", + [ + pytest.param(1, 0, id="t1i0"), + pytest.param(256, 0, id="t256i0"), + pytest.param(256, 255, id="t256i255"), + ], +) def test_rjumpv_into_returncontract( eof_test: EOFTestFiller, + table_size: int, + invalid_index: int, ): """EOF code containing RJUMP with target RETURNCONTRACT immediate""" + invalid_destination = 5 + jump_table = [0 for _ in range(table_size)] + jump_table[invalid_index] = invalid_destination eof_test( data=Container( sections=[ @@ -769,7 +1097,9 @@ def test_rjumpv_into_returncontract( container=Container( sections=[ Section.Code( - code=Op.PUSH1(0) + Op.RJUMPV[5] + Op.RETURNCONTRACT[0](0, 0), + code=Op.PUSH1(0) + + Op.RJUMPV[jump_table] + + Op.RETURNCONTRACT[0](0, 0), code_outputs=NON_RETURNING_SECTION, max_stack_height=2, ), From e2463964ce176498b530d568fbbc3efcfd14dcc6 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 4 Jun 2024 00:23:54 +0000 Subject: [PATCH 6/7] fix(tests): tox fixes --- .../prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py | 2 +- .../prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py | 2 +- .../prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py index cd007b1cfb..0a385f23da 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py @@ -396,7 +396,7 @@ def test_rjump_into_push_n( data_portion_end: bool, ): """EOF1I4200_0011 (Invalid) EOF code containing RJUMP with target PUSH2+ immediate""" - data_portion_length = int.from_bytes(opcode) - 0x5F + data_portion_length = int.from_bytes(opcode, byteorder="big") - 0x5F if jump == JumpDirection.FORWARD: offset = data_portion_length if data_portion_end else 1 code = Op.RJUMP[offset] + opcode[0] diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py index 37891dfcb2..fa9aa418ac 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpi.py @@ -561,7 +561,7 @@ def test_rjumpi_into_push_n( data_portion_end: bool, ): """EOF1I4200_0024 (Invalid) EOF code containing RJUMPI with target PUSH2+ immediate""" - data_portion_length = int.from_bytes(opcode) - 0x5F + data_portion_length = int.from_bytes(opcode, byteorder="big") - 0x5F if jump == JumpDirection.FORWARD: offset = data_portion_length if data_portion_end else 1 code = Op.PUSH1(1) + Op.RJUMPI[offset] + opcode[0] diff --git a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py index 6d4f63cb69..15abf46df3 100644 --- a/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py +++ b/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjumpv.py @@ -777,7 +777,7 @@ def test_rjumpv_into_push_n( data_portion_end: bool, ): """EOF1I4200_0039 (Invalid) EOF code containing RJUMPV with target PUSH1 immediate""" - data_portion_length = int.from_bytes(opcode) - 0x5F + data_portion_length = int.from_bytes(opcode, byteorder="big") - 0x5F if jump == JumpDirection.FORWARD: invalid_destination = data_portion_length + 1 if data_portion_end else 2 jump_table = [0 for _ in range(table_size)] From 7b25ea63bf754927a74bcf80bcfff2b4a58e6a7e Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 4 Jun 2024 02:24:30 +0000 Subject: [PATCH 7/7] docs: Changelog --- docs/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4192c29a1a..4b1d987a6c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,8 +16,8 @@ Test fixtures for use by clients are available for each release on the [Github r - ✨ Add tests for [EIP-6110: Supply validator deposits on chain](https://eips.ethereum.org/EIPS/eip-6110) ([#530](https://github.com/ethereum/execution-spec-tests/pull/530)). - ✨ Add tests for [EIP-7002: Execution layer triggerable withdrawals](https://eips.ethereum.org/EIPS/eip-7002) ([#530](https://github.com/ethereum/execution-spec-tests/pull/530)). - ✨ Add tests for [EIP-7685: General purpose execution layer requests](https://eips.ethereum.org/EIPS/eip-7685) ([#530](https://github.com/ethereum/execution-spec-tests/pull/530)). -- ✨ Add tests for [EIP-2935: Serve historical block hashes from state -](https://eips.ethereum.org/EIPS/eip-2935) ([#564](https://github.com/ethereum/execution-spec-tests/pull/564)). +- ✨ Add tests for [EIP-2935: Serve historical block hashes from state](https://eips.ethereum.org/EIPS/eip-2935) ([#564](https://github.com/ethereum/execution-spec-tests/pull/564)). +- ✨ Add tests for [EIP-4200: EOF - Static relative jumps](https://eips.ethereum.org/EIPS/eip-4200) ([#581](https://github.com/ethereum/execution-spec-tests/pull/581)). ### 🛠️ Framework