Skip to content

Commit e1e5b34

Browse files
pdobaczmarioevz
andauthored
new(tests): EOF - EXTDELEGATECALL blocking vs 7702 (#1309)
* new(tests): EOF - EXTDELEGATECALL blocking vs 7702 * new(tests): EOF - EXT*CALL backfill test cases * Apply code review suggestions * Refactor target_address to fixture * Apply code review Co-authored-by: Mario Vega <[email protected]> --------- Co-authored-by: Mario Vega <[email protected]>
1 parent 7af6c71 commit e1e5b34

File tree

2 files changed

+162
-53
lines changed

2 files changed

+162
-53
lines changed

tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py

Lines changed: 155 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""test calls across EOF and Legacy."""
22

33
import itertools
4+
from enum import Enum, auto, unique
45

56
import pytest
67

@@ -13,6 +14,7 @@
1314
StateTestFiller,
1415
Storage,
1516
Transaction,
17+
compute_create_address,
1618
)
1719
from ethereum_test_tools.eof.v1 import Container, Section
1820
from ethereum_test_tools.vm.opcode import Opcodes as Op
@@ -57,12 +59,67 @@
5759
)
5860

5961

62+
@unique
63+
class TargetAccountType(Enum):
64+
"""Kinds of target accounts for calls."""
65+
66+
EMPTY = auto()
67+
EOA = auto()
68+
LEGACY_CONTRACT = auto()
69+
EOF_CONTRACT = auto()
70+
LEGACY_CONTRACT_INVALID = auto()
71+
EOF_CONTRACT_INVALID = auto()
72+
LEGACY_CONTRACT_REVERT = auto()
73+
EOF_CONTRACT_REVERT = auto()
74+
IDENTITY_PRECOMPILE = auto()
75+
76+
def __str__(self) -> str:
77+
"""Return string representation of the enum."""
78+
return f"{self.name}"
79+
80+
6081
@pytest.fixture
6182
def sender(pre: Alloc) -> EOA:
6283
"""Sender of the transaction."""
6384
return pre.fund_eoa()
6485

6586

87+
@pytest.fixture
88+
def target_address(pre: Alloc, target_account_type: TargetAccountType) -> Address:
89+
"""Target address of the call depending on required type of account."""
90+
match target_account_type:
91+
case TargetAccountType.EMPTY:
92+
return pre.fund_eoa(amount=0)
93+
case TargetAccountType.EOA:
94+
return pre.fund_eoa()
95+
case TargetAccountType.LEGACY_CONTRACT:
96+
return pre.deploy_contract(
97+
code=Op.STOP,
98+
)
99+
case TargetAccountType.EOF_CONTRACT:
100+
return pre.deploy_contract(
101+
code=Container.Code(Op.STOP),
102+
)
103+
case TargetAccountType.LEGACY_CONTRACT_INVALID:
104+
return pre.deploy_contract(
105+
code=Op.INVALID,
106+
)
107+
case TargetAccountType.EOF_CONTRACT_INVALID:
108+
return pre.deploy_contract(
109+
code=Container.Code(Op.INVALID),
110+
)
111+
case TargetAccountType.LEGACY_CONTRACT_REVERT:
112+
return pre.deploy_contract(
113+
code=Op.REVERT(0, 0),
114+
)
115+
case TargetAccountType.EOF_CONTRACT_REVERT:
116+
return pre.deploy_contract(
117+
code=Container.Code(Op.REVERT(0, 0)),
118+
)
119+
case TargetAccountType.IDENTITY_PRECOMPILE:
120+
return identity
121+
122+
66123
@pytest.mark.parametrize(
67124
"opcode",
68125
[
@@ -90,7 +147,7 @@ def test_legacy_calls_eof_sstore(
90147

91148
tx = Transaction(
92149
sender=sender,
93-
to=Address(calling_contract_address),
150+
to=calling_contract_address,
94151
gas_limit=50000000,
95152
)
96153

@@ -162,7 +219,7 @@ def test_legacy_calls_eof_mstore(
162219

163220
tx = Transaction(
164221
sender=sender,
165-
to=Address(calling_contract_address),
222+
to=calling_contract_address,
166223
gas_limit=50000000,
167224
)
168225

@@ -217,7 +274,7 @@ def test_eof_calls_eof_sstore(
217274

218275
tx = Transaction(
219276
sender=sender,
220-
to=Address(calling_contract_address),
277+
to=calling_contract_address,
221278
gas_limit=50000000,
222279
)
223280

@@ -290,7 +347,7 @@ def test_eof_calls_eof_mstore(
290347

291348
tx = Transaction(
292349
sender=sender,
293-
to=Address(calling_contract_address),
350+
to=calling_contract_address,
294351
gas_limit=50000000,
295352
)
296353

@@ -361,7 +418,7 @@ def test_eof_calls_precompile(
361418

362419
tx = Transaction(
363420
sender=sender,
364-
to=Address(calling_contract_address),
421+
to=calling_contract_address,
365422
gas_limit=5000000,
366423
)
367424

@@ -418,7 +475,7 @@ def test_eof_calls_legacy_sstore(
418475

419476
tx = Transaction(
420477
sender=sender,
421-
to=Address(calling_contract_address),
478+
to=calling_contract_address,
422479
gas_limit=50000000,
423480
)
424481

@@ -485,7 +542,7 @@ def test_eof_calls_legacy_mstore(
485542

486543
tx = Transaction(
487544
sender=sender,
488-
to=Address(calling_contract_address),
545+
to=calling_contract_address,
489546
gas_limit=50000000,
490547
)
491548

@@ -557,7 +614,7 @@ def test_callee_fails(
557614

558615
tx = Transaction(
559616
sender=sender,
560-
to=Address(calling_contract_address),
617+
to=calling_contract_address,
561618
gas_limit=4000000,
562619
)
563620

@@ -628,7 +685,7 @@ def test_callee_context(
628685

629686
tx = Transaction(
630687
sender=sender,
631-
to=Address(calling_contract_address),
688+
to=calling_contract_address,
632689
gas_limit=100000,
633690
value=tx_value,
634691
)
@@ -695,7 +752,7 @@ def test_eof_calls_eof_then_fails(
695752

696753
tx = Transaction(
697754
sender=sender,
698-
to=Address(calling_contract_address),
755+
to=calling_contract_address,
699756
gas_limit=50000000,
700757
)
701758

@@ -722,23 +779,15 @@ def test_eof_calls_eof_then_fails(
722779
)
723780
@pytest.mark.parametrize(
724781
"target_account_type",
725-
(
726-
"empty",
727-
"EOA",
728-
"LegacyContract",
729-
"EOFContract",
730-
"LegacyContractInvalid",
731-
"EOFContractInvalid",
732-
),
733-
ids=lambda x: x,
782+
TargetAccountType,
734783
)
735784
@pytest.mark.parametrize("value", [0, 1])
736785
def test_eof_calls_clear_return_buffer(
737786
state_test: StateTestFiller,
738787
pre: Alloc,
739788
sender: EOA,
740789
opcode: Op,
741-
target_account_type: str,
790+
target_address: Address,
742791
value: int,
743792
):
744793
"""Test EOF contracts calling clears returndata buffer."""
@@ -748,28 +797,6 @@ def test_eof_calls_clear_return_buffer(
748797
)
749798
filling_callee_address = pre.deploy_contract(filling_contract_code)
750799

751-
match target_account_type:
752-
case "empty":
753-
target_address = b"\x78" * 20
754-
case "EOA":
755-
target_address = pre.fund_eoa()
756-
case "LegacyContract":
757-
target_address = pre.deploy_contract(
758-
code=Op.STOP,
759-
)
760-
case "EOFContract":
761-
target_address = pre.deploy_contract(
762-
code=Container.Code(Op.STOP),
763-
)
764-
case "LegacyContractInvalid":
765-
target_address = pre.deploy_contract(
766-
code=Op.INVALID,
767-
)
768-
case "EOFContractInvalid":
769-
target_address = pre.deploy_contract(
770-
code=Container.Code(Op.INVALID),
771-
)
772-
773800
caller_contract = Container.Code(
774801
# First fill the return buffer and sanity check
775802
Op.EXTCALL(filling_callee_address, 0, 0, 0)
@@ -785,7 +812,7 @@ def test_eof_calls_clear_return_buffer(
785812

786813
tx = Transaction(
787814
sender=sender,
788-
to=Address(calling_contract_address),
815+
to=calling_contract_address,
789816
gas_limit=50000000,
790817
)
791818

@@ -845,7 +872,7 @@ def test_eof_calls_static_flag_with_value(
845872
)
846873
tx = Transaction(
847874
sender=sender,
848-
to=Address(calling_contract_address),
875+
to=calling_contract_address,
849876
gas_limit=5_000_000,
850877
)
851878

@@ -942,7 +969,7 @@ def test_eof_calls_min_callee_gas(
942969

943970
tx = Transaction(
944971
sender=sender,
945-
to=Address(calling_contract_address),
972+
to=calling_contract_address,
946973
gas_limit=no_oog_gas + extra_gas_limit,
947974
)
948975

@@ -988,7 +1015,7 @@ def test_eof_calls_with_value(
9881015
)
9891016
tx = Transaction(
9901017
sender=sender,
991-
to=Address(calling_contract_address),
1018+
to=calling_contract_address,
9921019
gas_limit=50000000,
9931020
)
9941021

@@ -1084,7 +1111,7 @@ def test_eof_calls_msg_depth(
10841111
)
10851112
tx = Transaction(
10861113
sender=sender,
1087-
to=Address(calling_contract_address),
1114+
to=calling_contract_address,
10881115
gas_limit=gas_limit,
10891116
)
10901117

@@ -1104,3 +1131,84 @@ def test_eof_calls_msg_depth(
11041131
post=post,
11051132
tx=tx,
11061133
)
1134+
1135+
1136+
@pytest.mark.parametrize("target_account_type", TargetAccountType)
1137+
@pytest.mark.parametrize("delegate", [True, False])
1138+
@pytest.mark.parametrize("call_from_initcode", [True, False])
1139+
def test_extdelegate_call_targets(
1140+
state_test: StateTestFiller,
1141+
pre: Alloc,
1142+
target_account_type: TargetAccountType,
1143+
target_address: Address,
1144+
delegate: bool,
1145+
call_from_initcode: bool,
1146+
):
1147+
"""
1148+
Test EOF contracts extdelegatecalling various targets, especially resolved via 7702
1149+
delegation.
1150+
"""
1151+
env = Environment()
1152+
1153+
if delegate:
1154+
target_address = pre.fund_eoa(0, delegation=target_address)
1155+
1156+
sender = pre.fund_eoa()
1157+
delegate_call_code = Op.SSTORE(
1158+
slot_call_result, Op.EXTDELEGATECALL(address=target_address)
1159+
) + Op.SSTORE(slot_code_worked, value_code_worked)
1160+
1161+
if call_from_initcode:
1162+
# Call from initcode
1163+
caller_contract = Container(
1164+
sections=[
1165+
Section.Code(
1166+
code=delegate_call_code + Op.RETURNCODE[0](0, 0),
1167+
),
1168+
Section.Container(Container.Code(Op.STOP)),
1169+
]
1170+
)
1171+
tx = Transaction(
1172+
sender=sender,
1173+
to=None,
1174+
data=caller_contract,
1175+
gas_limit=4_000_000,
1176+
)
1177+
calling_contract_address = tx.created_contract
1178+
else:
1179+
# Normal call from existing contract
1180+
caller_contract = Container.Code(
1181+
delegate_call_code + Op.STOP,
1182+
)
1183+
calling_contract_address = pre.deploy_contract(caller_contract)
1184+
1185+
tx = Transaction(
1186+
sender=sender,
1187+
to=calling_contract_address,
1188+
gas_limit=4_000_000,
1189+
)
1190+
1191+
calling_storage = {
1192+
slot_code_worked: value_code_worked,
1193+
slot_call_result: EXTCALL_SUCCESS
1194+
if target_account_type == TargetAccountType.EOF_CONTRACT
1195+
else EXTCALL_FAILURE
1196+
if target_account_type == TargetAccountType.EOF_CONTRACT_INVALID
1197+
else EXTCALL_REVERT,
1198+
}
1199+
storage_address = (
1200+
compute_create_address(address=sender, nonce=0)
1201+
if call_from_initcode
1202+
else calling_contract_address
1203+
)
1204+
1205+
post = {
1206+
storage_address: Account(storage=calling_storage),
1207+
}
1208+
1209+
state_test(
1210+
env=env,
1211+
pre=pre,
1212+
post=post,
1213+
tx=tx,
1214+
)

tests/osaka/eip7692_eof_v1/eof_tracker.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,6 @@
8585
- [x] Legacy executing EXTCODESIZE of EOF contract ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_extcode.py::test_legacy_calls_eof_sstore`](./eip3540_eof_v1/test_extcode/test_legacy_calls_eof_sstore.md))
8686
- [x] Legacy executing EXTCODEHASH of EOF contract ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_extcode.py::test_legacy_calls_eof_sstore`](./eip3540_eof_v1/test_extcode/test_legacy_calls_eof_sstore.md))
8787
- [x] Legacy executing EXTCODECOPY of EOF contract ([`tests/osaka/eip7692_eof_v1/eip3540_eof_v1/test_extcode.py::test_legacy_calls_eof_sstore`](./eip3540_eof_v1/test_extcode/test_legacy_calls_eof_sstore.md))
88-
- [ ] `*CALLs` from legacy contracts to EOF contracts (ethereum/tests: src/EIPTestsFiller/StateTests/stEOF/stEIP3540/EOF1_CallsFiller.yml)
89-
- [ ] `EXT*CALLs` from EOF to legacy contracts (ethereum/tests: src/EIPTestsFiller/StateTests/stEOF/stEIP3540/EOF1_CallsFiller.yml)
90-
- [ ] EXTDELEGATECALL from EOF to EOF contract (ethereum/tests: src/EIPTestsFiller/StateTests/stEOF/stEIP3540/EOF1_CallsFiller.yml)
91-
- [ ] EXTDELEGATECALL from EOF to legacy contract failing (ethereum/tests: src/EIPTestsFiller/StateTests/stEOF/stEIP3540/EOF1_CallsFiller.yml)
92-
- [ ] EXTDELEGATECALL from EOF to EOA failing
93-
- [ ] EXTDELEGATECALL from EOF to empty account failing
9488

9589
## EIP-3670: EOF - Code Validation
9690

@@ -420,6 +414,13 @@
420414
- [x] EXTSTATICCALL from EOF to non-pure legacy contract failing ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_eof_calls_legacy_sstore`](./eip7069_extcall/test_calls/test_eof_calls_legacy_sstore.md))
421415
- [x] EXTSTATICCALL from EOF to pure EOF contract ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_eof_calls_legacy_mstore`](./eip7069_extcall/test_calls/test_eof_calls_legacy_mstore.md))
422416
- [x] EXTSTATICCALL from EOF to non-pure EOF contract failing ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_eof_calls_eof_sstore`](./eip7069_extcall/test_calls/test_eof_calls_eof_sstore.md))
417+
- [x] `*CALLs` from legacy contracts to EOF contracts (ethereum/tests: ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_legacy_calls_eof_sstore`](./eip7069_extcall/test_calls/test_eof_calls_eof_sstore.md))
418+
- [x] `EXT*CALLs` from EOF to legacy contracts ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_eof_calls_legacy_sstore`](./eip7069_extcall/test_calls/test_eof_calls_eof_sstore.md))
419+
- [x] EXTDELEGATECALL from EOF to EOF contract ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_eof_calls_eof_sstore`](./eip7069_extcall/test_calls/test_eof_calls_eof_sstore.md))
420+
- [x] EXTDELEGATECALL from EOF to legacy contract failing ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_extdelegate_call_targets`](./eip7069_extcall/test_calls/test_extdelegate_call_targets.md))
421+
- [x] EXTDELEGATECALL from EOF to EOA failing ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_extdelegate_call_targets`](./eip7069_extcall/test_calls/test_extdelegate_call_targets.md))
422+
- [x] EXTDELEGATECALL from EOF to empty account failing ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_extdelegate_call_targets`](./eip7069_extcall/test_calls/test_extdelegate_call_targets.md))
423+
- [x] EXTDELEGATECALL to EIP-7702 delegate ([`./tests/osaka/eip7692_eof_v1/eip7069_extcall/test_calls.py::test_extdelegate_call_targets`](./eip7069_extcall/test_calls/test_extdelegate_call_targets.md))
423424

424425

425426
## EIP-7620: EOF Contract Creation

0 commit comments

Comments
 (0)