Skip to content

Commit

Permalink
Validate EIP-7676 Prepare for Address Space Extension
Browse files Browse the repository at this point in the history
Validate ASE behaviors on legacy and EOF opcodes for ASE, when targeting
EOAs, Contracts, and empty accounts.  Opcodes are BALANCE, EXTCALL/CALL,
EXTDELEGATECALL/DELEGATECALL,  EXTSTATICCALL/STATICCALL, and CALLCODE.

Signed-off-by: Danno Ferrin <[email protected]>
  • Loading branch information
shemnon committed Apr 23, 2024
1 parent a6a720f commit 3ed99a0
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 5 deletions.
37 changes: 33 additions & 4 deletions src/ethereum_test_tools/vm/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -5316,7 +5316,7 @@ class Opcodes(Opcode, Enum):
Fork
----
Prague
EOF Fork
Gas
----
Expand All @@ -5329,6 +5329,35 @@ class Opcodes(Opcode, Enum):
Source: [EIP-7069](https://eips.ethereum.org/EIPS/eip-7069)
"""

RETURNDATALOAD = Opcode(0xF7, popped_stack_items=1, pushed_stack_items=1)
"""
RETURNDATALOAD(offset) = returndata
----
Description
----
Copies 32 bytes of return data onto the operand stack
Inputs
----
- offset: The offset within the return data to start copying. There must be 32 byes of return
data at that address or an exceptional halt occurs.
Outputs
----
- returndata: the 32 bytes of return data at the offset
Fork
----
EOF Fork
Gas
----
3
Source: [EIP-7069](https://eips.ethereum.org/EIPS/eip-7069)
"""

EXTDELEGATECALL = Opcode(0xF9, popped_stack_items=3, pushed_stack_items=1)
"""
EXTDELEGATECALL(target_address, input_offset, input_size) = address
Expand All @@ -5354,7 +5383,7 @@ class Opcodes(Opcode, Enum):
Fork
----
Prague
EOF Fork
Gas
----
Expand Down Expand Up @@ -5400,7 +5429,7 @@ class Opcodes(Opcode, Enum):
Source: [evm.codes/#FA](https://www.evm.codes/#FA)
"""

EXTSTATICCALL = Opcode(0xFB, popped_stack_items=4, pushed_stack_items=1)
EXTSTATICCALL = Opcode(0xFB, popped_stack_items=3, pushed_stack_items=1)
"""
EXTSTATICCALL(target_address, input_offset, input_size) = address
----
Expand All @@ -5424,7 +5453,7 @@ class Opcodes(Opcode, Enum):
Fork
----
Prague
EOF Fork
Gas
----
Expand Down
3 changes: 3 additions & 0 deletions tests/prague/eip7676_address_space_extension/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Address Space Extension Tests
"""
5 changes: 5 additions & 0 deletions tests/prague/eip7676_address_space_extension/spec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""
EOF V1 Constants used throughout all tests
"""

EOF_FORK_NAME = "Prague"
219 changes: 219 additions & 0 deletions tests/prague/eip7676_address_space_extension/test_ase_opcodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
"""
Execution of CALLF, RETF opcodes within EOF V1 containers tests
"""

import pytest

from ethereum_test_tools import (
Account,
Address,
Environment,
StateTestFiller,
TestAddress,
Transaction,
)
from ethereum_test_tools.common.conversions import BytesConvertible, to_bytes
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 .spec import EOF_FORK_NAME

REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7676.md"
REFERENCE_SPEC_VERSION = (
"00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
)

pytestmark = pytest.mark.valid_from(EOF_FORK_NAME)


@pytest.mark.parametrize(
"target_address",
(
b"",
b"\x10\x00",
b"\x78" * 20,
b"\xff" * 20,
b"\x01" + (b"\x00" * 20),
b"\x5a" * 28,
b"\x5a" * 32,
b"\xff" * 32,
),
ids=["zero", "short", "mid20", "max20", "minAse", "midAse", "fullAse", "maxAse"],
)
@pytest.mark.parametrize("target_account_type", ("empty", "EOA", "Contract"), ids=lambda x: x)
def test_address_space_extension(
state_test: StateTestFiller,
target_address: BytesConvertible,
target_account_type: str,
):
"""
Test contacts with possibly extended address and fail if address is too large
"""
env = Environment()

address_bytes = to_bytes(target_address)
ase_address = len(address_bytes) > 20
if ase_address and address_bytes[0] == b"00":
raise ValueError("Test instrumentation requires target addresses trim leading zeros")

pre = {
TestAddress: Account(
balance=1000000000000000000000,
nonce=1,
),
Address(0x100): Account(
code=(
Op.MSTORE(0, Op.PUSH32(address_bytes))
+ Op.SSTORE(0, Op.CALL(50000, 0x200, 0, 0, 32, 0, 0))
+ Op.SSTORE(1, Op.CALL(50000, 0x300, 0, 0, 32, 0, 0))
+ Op.SSTORE(2, Op.CALL(50000, 0x400, 0, 0, 32, 0, 0))
+ Op.SSTORE(3, Op.CALL(50000, 0x500, 0, 0, 32, 0, 0))
+ Op.SSTORE(4, Op.CALL(50000, 0x201, 0, 0, 32, 0, 0))
+ Op.SSTORE(5, Op.CALL(50000, 0x301, 0, 0, 32, 0, 0))
+ Op.SSTORE(6, Op.CALL(50000, 0x401, 0, 0, 32, 0, 0))
+ Op.SSTORE(7, Op.CALL(50000, 0x501, 0, 0, 32, 0, 0))
+ Op.SSTORE(8, Op.CALL(50000, 0x601, 0, 0, 32, 0, 0))
+ Op.STOP()
),
nonce=1,
),
Address(0x200): Account(
code=Container(
sections=[
Section.Code(
code=Op.SSTORE(0, Op.BALANCE(Op.CALLDATALOAD(0))) + Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=2,
)
],
),
nonce=1,
),
Address(0x201): Account(
code=Op.SSTORE(0, Op.BALANCE(Op.CALLDATALOAD(0))) + Op.STOP,
nonce=1,
),
Address(0x300): Account(
code=Container(
sections=[
Section.Code(
code=Op.SSTORE(0, Op.EXTCALL(Op.CALLDATALOAD(0), 0, 0, 0))
+ Op.SSTORE(1, Op.RETURNDATALOAD(0))
+ Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=4,
)
],
),
nonce=1,
),
Address(0x301): Account(
code=Op.SSTORE(0, Op.CALL(Op.GAS, Op.CALLDATALOAD(0), 0, 0, 0, 0, 0))
+ Op.RETURNDATACOPY(0, 0, Op.RETURNDATASIZE)
+ Op.SSTORE(1, Op.MLOAD(0))
+ Op.STOP,
nonce=1,
),
Address(0x400): Account(
code=Container(
sections=[
Section.Code(
code=Op.SSTORE(0, Op.EXTDELEGATECALL(Op.CALLDATALOAD(0), 0, 0))
+ Op.SSTORE(1, Op.RETURNDATALOAD(0))
+ Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=3,
)
],
),
nonce=1,
),
Address(0x401): Account(
code=Op.SSTORE(0, Op.DELEGATECALL(Op.GAS, Op.CALLDATALOAD(0), 0, 0, 0, 0))
+ Op.RETURNDATACOPY(0, 0, Op.RETURNDATASIZE)
+ Op.SSTORE(1, Op.MLOAD(0))
+ Op.STOP,
nonce=1,
),
Address(0x500): Account(
code=Container(
sections=[
Section.Code(
code=Op.SSTORE(0, Op.EXTSTATICCALL(Op.CALLDATALOAD(0), 0, 0))
+ Op.SSTORE(1, Op.RETURNDATALOAD(0))
+ Op.STOP,
code_inputs=0,
code_outputs=NON_RETURNING_SECTION,
max_stack_height=3,
)
],
),
nonce=1,
),
Address(0x501): Account(
code=Op.SSTORE(0, Op.STATICCALL(Op.GAS, Op.CALLDATALOAD(0), 0, 0, 0, 0))
+ Op.RETURNDATACOPY(0, 0, Op.RETURNDATASIZE)
+ Op.SSTORE(1, Op.MLOAD(0))
+ Op.STOP,
nonce=1,
),
Address(0x601): Account(
code=Op.SSTORE(0, Op.CALLCODE(Op.GAS, Op.CALLDATALOAD(0), 0, 0, 0, 0, 0))
+ Op.RETURNDATACOPY(0, 0, Op.RETURNDATASIZE)
+ Op.SSTORE(1, Op.MLOAD(0))
+ Op.STOP,
nonce=1,
),
}

stripped_address = address_bytes[-20:] if ase_address else target_address
post = {
Address(0x100): Account(
storage={0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1}
if not ase_address
else {4: 1, 5: 1, 6: 1, 7: 1, 8: 1}
)
}
match target_account_type:
case "empty":
# add no account
pass
case "EOA":
pre[Address(stripped_address)] = Account(code="", balance=10**18, nonce=9)
post[Address(0x200)] = Account(storage={} if ase_address else {0: 10**18})
post[Address(0x201)] = Account(storage={0: 10**18})
case "Contract":
pre[Address(stripped_address)] = Account(
code=Op.MSTORE(0, Op.ADDRESS) + Op.RETURN(0, 32), balance=0, nonce=0
)
# For EOF variants the EXT*CALL reverts, so no storage updates for ASE address
post[Address(0x300)] = Account(storage={} if ase_address else {1: stripped_address})
post[Address(0x301)] = Account(storage={0: 1, 1: stripped_address})
# EXTDELEGATECALL fails when calling legacy, so no stripped address
post[Address(0x400)] = Account(storage={} if ase_address else {0: 1})
post[Address(0x401)] = Account(storage={0: 1, 1: 0x401})
post[Address(0x500)] = Account(storage={} if ase_address else {1: stripped_address})
post[Address(0x501)] = Account(storage={0: 1, 1: stripped_address})
post[Address(0x601)] = Account(storage={0: 1, 1: 0x601})
case _:
raise ValueError("Unknown account type: " + target_account_type)

tx = Transaction(
nonce=1,
to=Address(0x100),
gas_limit=50000000,
gas_price=10,
protected=False,
data="",
)

state_test(
env=env,
pre=pre,
post=post,
tx=tx,
)
4 changes: 3 additions & 1 deletion whitelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ acl
addr
address
address2
ase
alloc
api
apis
Expand Down Expand Up @@ -244,6 +245,7 @@ repo
repo's
repos
returndatacopy
returndataload
returndatasize
returncontract
rlp
Expand Down Expand Up @@ -272,7 +274,7 @@ StateTestFiller
staticcalled
stExample
str
streetsidesoftware
streetsidesoftiware
subcall
subclasscheck
subdirectories
Expand Down

0 comments on commit 3ed99a0

Please sign in to comment.