Skip to content

Commit

Permalink
new(tests): EIP-7069: Different RETURNDATACOPY oob
Browse files Browse the repository at this point in the history
  • Loading branch information
pdobacz committed Jun 14, 2024
1 parent 3d18f03 commit 95ab812
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 3 deletions.
9 changes: 9 additions & 0 deletions tests/prague/eip7692_eof_v1/eip7069_extcall/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@
_slot = itertools.count()
next(_slot) # don't use slot 0
slot_code_worked = next(_slot)
slot_eof_target_call_status = next(_slot)
slot_legacy_target_call_status = next(_slot)
slot_eof_target_returndata = next(_slot)
slot_eof_target_returndatasize = next(_slot)
slot_legacy_target_returndatasize = next(_slot)

slot_last_slot = next(_slot)

"""Storage value indicating an abort"""
value_exceptional_abort_canary = 0x1984

"""Storage values for common testing fields"""
value_code_worked = 0x2015
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ethereum_test_tools.vm.opcode import Opcodes as Op

from .. import EOF_FORK_NAME
from .helpers import value_exceptional_abort_canary
from .spec import CALL_SUCCESS, EXTCALL_REVERT, EXTCALL_SUCCESS

REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7069.md"
Expand All @@ -22,8 +23,6 @@
slot_target_call_status = next(_slot)
slot_target_returndata = next(_slot)

value_exceptional_abort_canary = 0x1984


@pytest.mark.parametrize(
"target_address",
Expand Down
122 changes: 121 additions & 1 deletion tests/prague/eip7692_eof_v1/eip7069_extcall/test_returndataload.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,23 @@
import pytest

from ethereum_test_tools import Account, Alloc, Environment, StateTestFiller, Transaction
from ethereum_test_tools.common.types import Storage
from ethereum_test_tools.eof.v1 import Container, Section
from ethereum_test_tools.vm.opcode import Opcodes as Op

from .. import EOF_FORK_NAME
from . import REFERENCE_SPEC_GIT_PATH, REFERENCE_SPEC_VERSION
from .helpers import slot_code_worked, value_code_worked
from .helpers import (
slot_code_worked,
slot_eof_target_call_status,
slot_eof_target_returndata,
slot_eof_target_returndatasize,
slot_legacy_target_call_status,
slot_legacy_target_returndatasize,
value_code_worked,
value_exceptional_abort_canary,
)
from .spec import CALL_FAILURE, CALL_SUCCESS, EXTCALL_FAILED, EXTCALL_SUCCESS

REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION
Expand Down Expand Up @@ -266,3 +277,112 @@ def test_returndataload_handling(
tx=tx,
post=post,
)


@pytest.mark.parametrize(
["call_prefix", "opcode", "call_suffix"],
[
pytest.param([500_000], Op.CALL, [0, 0, 0, 0, 0], id="CallerIsLegacy"),
pytest.param([], Op.EXTCALL, [0, 0, 0], id="CallerIsEOF"),
],
ids=lambda x: x,
)
def test_returndatacopy_oob(
state_test: StateTestFiller,
pre: Alloc,
call_prefix: List[int],
opcode: Op,
call_suffix: List[int],
):
"""
Extends the RETURNDATACOPY test for correct out-of-bounds behavior, by checking if the
caller frame's context being EOF or legacy doesn't impact the execution logic of the
RETURNDATACOPY instance under test.
"""
env = Environment()

sender = pre.fund_eoa(10**18)

# Both callee codes below make an OOB (out-of-bounds) RETURNDATACOPY of one byte,
# which they then attempt to return (Legacy should exceptionally halt on RETURNDATACOPY).
address_callee_eof = pre.deploy_contract(
code=Container(
sections=[
Section.Code(
code=Op.RETURNDATACOPY(0, 0, 1) + Op.RETURN(0, 1),
max_stack_height=3,
)
]
)
)
address_callee_legacy = pre.deploy_contract(Op.RETURNDATACOPY(0, 0, 1) + Op.RETURN(0, 1))

# Caller code is selected to either be Legacy or EOF using params.
code_entry_point = (
Op.SSTORE(
slot_eof_target_call_status, opcode(*call_prefix, address_callee_eof, *call_suffix)
)
+ Op.SSTORE(slot_eof_target_returndatasize, Op.RETURNDATASIZE)
+ Op.SSTORE(slot_eof_target_returndata, Op.RETURNDATACOPY(0, 0, 1) + Op.MLOAD(0))
+ Op.SSTORE(
slot_legacy_target_call_status,
opcode(*call_prefix, address_callee_legacy, *call_suffix),
)
+ Op.SSTORE(slot_legacy_target_returndatasize, Op.RETURNDATASIZE)
+ Op.STOP
)

storage_entry_point = Storage(
{
slot_eof_target_call_status: value_exceptional_abort_canary,
slot_eof_target_returndata: value_exceptional_abort_canary,
slot_eof_target_returndatasize: value_exceptional_abort_canary,
slot_legacy_target_call_status: value_exceptional_abort_canary,
slot_legacy_target_returndatasize: value_exceptional_abort_canary,
}
)

address_entry_point = (
pre.deploy_contract(code=code_entry_point, storage=storage_entry_point)
if opcode == Op.CALL
else pre.deploy_contract(
Container(
sections=[
Section.Code(
code=code_entry_point,
max_stack_height=4,
storage=storage_entry_point,
)
]
)
)
)

tx = Transaction(to=address_entry_point, gas_limit=2_000_000, sender=sender)

post = {
address_entry_point: Account(
storage={
slot_eof_target_call_status: CALL_SUCCESS,
slot_eof_target_returndata: "0x00",
slot_eof_target_returndatasize: "0x01",
slot_legacy_target_call_status: CALL_FAILURE,
slot_legacy_target_returndatasize: "0x00",
}
if opcode == Op.CALL
else {
slot_eof_target_call_status: EXTCALL_SUCCESS,
slot_eof_target_returndata: "0x00",
slot_eof_target_returndatasize: "0x01",
slot_legacy_target_call_status: EXTCALL_FAILED,
slot_legacy_target_returndatasize: "0x00",
}
)
}

state_test(
env=env,
pre=pre,
post=post,
tx=tx,
)
1 change: 1 addition & 0 deletions whitelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ num
number
ommer
ommers
oob
opc
oprypin
origin
Expand Down

0 comments on commit 95ab812

Please sign in to comment.