Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion tests/prague/eip7692_eof_v1/eip3540_eof_v1/opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,33 @@

V1_EOF_OPCODES: List[Op] = [
# new eof ops
# EIP-663 Swap and Dup
Op.DUPN,
Op.SWAPN,
Op.EXCHANGE,
# EIP-4200 Relative Jumps
Op.RJUMP,
Op.RJUMPI,
Op.RJUMPV,
# EIP-4750 functions
Op.CALLF,
Op.RETF,
# Op.JUMPF,
# EIP-6209 JUMPF Instruction
Op.JUMPF,
# EIP-7069 Revamped EOF Call
Op.EXTCALL,
Op.EXTDELEGATECALL,
Op.EXTSTATICCALL,
Op.RETURNDATALOAD,
# EIP-7480 EOF Data Section Access
Op.DATALOAD,
Op.DATALOADN,
Op.DATASIZE,
Op.DATACOPY,
# EIP-7620 EOF Create and Return Contract operation
Op.EOFCREATE,
Op.RETURNCONTRACT,
# Non-deprecated Legacy Opcodes
Op.STOP,
Op.ADD,
Op.MUL,
Expand Down Expand Up @@ -174,11 +195,30 @@
"""

V1_EOF_ONLY_OPCODES = [
Op.DUPN,
Op.SWAPN,
Op.EXCHANGE,
# EIP-4200 Relative Jumps
Op.RJUMP,
Op.RJUMPI,
Op.RJUMPV,
# EIP-4750 functions
Op.CALLF,
Op.RETF,
# EIP-6209 JUMPF Instruction
Op.JUMPF,
# EIP-7069 Revamped EOF Call
Op.EXTCALL,
Op.EXTDELEGATECALL,
Op.EXTSTATICCALL,
# EIP-7480 EOF Data Section Access
Op.DATALOAD,
Op.DATALOADN,
Op.DATASIZE,
Op.DATACOPY,
# EIP-7620 EOF Create and Return Contract operation
Op.EOFCREATE,
Op.RETURNCONTRACT,
]
"""
List of valid EOF V1 opcodes that are disabled in legacy bytecode.
Expand All @@ -190,6 +230,7 @@
Op.REVERT,
Op.INVALID,
Op.RETF,
Op.JUMPF,
]

INVALID_TERMINATING_OPCODES = [op for op in V1_EOF_OPCODES if op not in VALID_TERMINATING_OPCODES]
Expand Down
253 changes: 253 additions & 0 deletions tests/prague/eip7692_eof_v1/eip3540_eof_v1/test_opcodes_in_legacy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
"""
Tests all EOF-only opcodes in legacy contracts and expects failure.
"""
from typing import List

import pytest

from ethereum_test_base_types import Account
from ethereum_test_specs import StateTestFiller
from ethereum_test_tools import Initcode
from ethereum_test_tools import Opcodes as Op
from ethereum_test_tools.eof.v1 import Container, Section
from ethereum_test_types import Alloc, Environment, Transaction
from ethereum_test_vm import Opcodes

from .. import EOF_FORK_NAME

REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7692.md"
REFERENCE_SPEC_VERSION = "f0e7661ee0d16e612e0931ec88b4c9f208e9d513"

pytestmark = pytest.mark.valid_from(EOF_FORK_NAME)

slot_code_executed = b"EXEC"
slot_code_worked = b"WORK"
slot_create_address = b"ADDR"

value_code_executed = b"exec"
value_code_worked = b"work"
value_non_execution_canary = b"brid"
value_create_failed = 0

eof_opcode_blocks = [
pytest.param(Op.PUSH0 + Op.DUPN[0], id="DUPN"),
pytest.param(Op.PUSH0 + Op.PUSH0 + Op.SWAPN[0], id="SWAPN"),
pytest.param(Op.PUSH0 + Op.PUSH0 + Op.PUSH0 + Op.EXCHANGE[2, 3], id="EXCHANGE"),
pytest.param(Op.RJUMP[0], id="RJUMP"),
pytest.param(Op.PUSH0 + Op.RJUMPI[0], id="RJUMPI"),
pytest.param(Op.PUSH0 + Op.RJUMPV[0, 0], id="RJUMPI"),
pytest.param(Op.CALLF[1], id="CALLF"),
pytest.param(Op.RETF, id="RETF"),
pytest.param(Op.JUMPF[0], id="JUMPF"),
pytest.param(Op.PUSH0 + Op.PUSH0 + Op.PUSH0 + Op.PUSH1(2) + Op.EXTCALL, id="EXTCALL"),
pytest.param(
Op.PUSH0 + Op.PUSH0 + Op.PUSH0 + Op.PUSH1(2) + Op.EXTDELEGATECALL, id="EXTDELEGATECALL"
),
pytest.param(
Op.PUSH0 + Op.PUSH0 + Op.PUSH0 + Op.PUSH1(2) + Op.EXTSTATICCALL, id="EXTSTATICCALL"
),
pytest.param(Op.DATALOAD(0), id="DATALOAD"),
pytest.param(Op.DATALOADN[0], id="DATALOADN"),
pytest.param(Op.DATASIZE, id="DATASIZE"),
pytest.param(Op.DATACOPY(0, 0, 32), id="DATACOPY"),
pytest.param(Op.EOFCREATE[0](0, 0, 0, 0), id="EOFCREATE"),
pytest.param(Op.RETURNCONTRACT[0], id="RETURNCONTRACT"),
]


@pytest.mark.parametrize(
"code",
eof_opcode_blocks,
)
def test_opcodes_in_legacy(state_test: StateTestFiller, pre: Alloc, code: Opcodes):
"""
Test all EOF only opcodes in legacy contracts and expects failure.
"""
env = Environment()

address_test_contract = pre.deploy_contract(
code=code + Op.SSTORE(slot_code_executed, value_code_executed),
storage={slot_code_executed: value_non_execution_canary},
)

post = {
# assert the canary is not over-written. If it was written then the EOF opcode was valid
address_test_contract: Account(storage={slot_code_executed: value_non_execution_canary}),
}

sender = pre.fund_eoa()

tx = Transaction(
sender=sender,
to=address_test_contract,
gas_limit=5_000_000,
gas_price=10,
protected=False,
data="",
)

state_test(
env=env,
pre=pre,
post=post,
tx=tx,
)


@pytest.mark.parametrize(
"code",
eof_opcode_blocks,
)
def test_opcodes_in_create_tx(state_test: StateTestFiller, pre: Alloc, code: Opcodes):
"""
Test all EOF only opcodes in legacy contracts and expects failure.
"""
env = Environment()

sender = pre.fund_eoa()

tx = Transaction(
sender=sender,
to=None,
gas_limit=5_000_000,
gas_price=10,
protected=False,
data=code,
)

post = {
# Should revert in initcode, which results in no contract created
tx.created_contract: Account.NONEXISTENT
}

state_test(
env=env,
pre=pre,
post=post,
tx=tx,
)


@pytest.mark.parametrize(
"legacy_create_opcode",
[
pytest.param(Op.CREATE, id="CREATE"),
pytest.param(Op.CREATE2, id="CREATE2"),
],
)
@pytest.mark.parametrize(
"code",
eof_opcode_blocks,
)
def test_opcodes_in_create_operation(
state_test: StateTestFiller,
pre: Alloc,
code: Opcodes,
legacy_create_opcode: Opcodes,
):
"""
Test all EOF only opcodes in legacy contracts and expects failure.
"""
env = Environment()

init_code = Initcode(initcode_prefix=code, deploy_code=Op.RETURN(0, 0))
salt_param = [0] if legacy_create_opcode == Op.CREATE2 else []
factory_code = (
Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE)
+ Op.SSTORE(slot_create_address, legacy_create_opcode(0, 0, Op.CALLDATASIZE, *salt_param))
+ Op.SSTORE(slot_code_worked, value_code_worked)
)

sender = pre.fund_eoa()
contract_address = pre.deploy_contract(code=factory_code)

sender = pre.fund_eoa()
contract_address = pre.deploy_contract(code=factory_code)

post = {
contract_address: Account(
storage={slot_create_address: value_create_failed, slot_code_worked: value_code_worked}
)
}
tx = Transaction(
to=contract_address,
gas_limit=10_000_000,
gas_price=10,
protected=False,
data=init_code,
sender=sender,
)

state_test(env=env, pre=pre, post=post, tx=tx)


@pytest.mark.parametrize(
("ext_call_opcode", "ext_call_suffix"),
[
pytest.param(Op.EXTCALL, [0], id="EXTCALL"),
pytest.param(Op.EXTDELEGATECALL, [], id="EXTDELEGATECALL"),
pytest.param(Op.EXTSTATICCALL, [], id="EXTSTATICCALL"),
],
)
@pytest.mark.parametrize(
"code",
eof_opcode_blocks,
)
def test_opcodes_in_eof_calling_legacy(
state_test: StateTestFiller,
pre: Alloc,
code: Opcodes,
ext_call_opcode: Op,
ext_call_suffix: List[int],
):
"""
Test all EOF only opcodes in legacy contracts and expects failure.
"""
env = Environment()

address_test_contract = pre.deploy_contract(
code=code + Op.SSTORE(slot_code_executed, value_code_executed),
storage={slot_code_executed: value_non_execution_canary},
)

address_entry_contract = pre.deploy_contract(
code=Container(
sections=[
Section.Code(
ext_call_opcode(address_test_contract, 0, 0, *ext_call_suffix)
+ Op.SSTORE(slot_code_worked, value_code_worked)
+ Op.STOP
)
]
),
storage={slot_code_executed: value_non_execution_canary},
)

post = {
# assert the canary is not over-written. If it was written then the EOF opcode was valid
address_test_contract: Account(storage={slot_code_executed: value_non_execution_canary}),
address_entry_contract: Account(
storage={
slot_code_executed: value_non_execution_canary,
slot_code_worked: value_code_worked,
}
),
}

sender = pre.fund_eoa()

tx = Transaction(
sender=sender,
to=address_entry_contract,
gas_limit=5_000_000,
gas_price=10,
protected=False,
data="",
)

state_test(
env=env,
pre=pre,
post=post,
tx=tx,
)
8 changes: 6 additions & 2 deletions tests/prague/eip7692_eof_v1/eip7069_extcall/test_gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ def gas_test(

sender = pre.fund_eoa(10**18)

address_baseline = pre.deploy_contract(setup_code + tear_down_code)
address_subject = pre.deploy_contract(setup_code + subject_code + tear_down_code)
address_baseline = pre.deploy_contract(
Container(sections=[Section.Code(setup_code + tear_down_code)])
)
address_subject = pre.deploy_contract(
Container(sections=[Section.Code(setup_code + subject_code + tear_down_code)])
)
address_legacy_harness = pre.deploy_contract(
code=(
# warm subject and baseline without executing
Expand Down