Skip to content

Commit 7fa7f18

Browse files
committed
new(tests): EOF - EIP-7620 - EOFCREATE gas testing
1 parent cf40b3f commit 7fa7f18

File tree

3 files changed

+273
-11
lines changed

3 files changed

+273
-11
lines changed

tests/prague/eip7692_eof_v1/eip7069_extcall/test_gas.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from ethereum_test_tools import Account, Alloc, Environment, StateTestFiller, Transaction
99
from ethereum_test_tools.eof.v1 import Container
1010
from ethereum_test_tools.vm.opcode import Opcodes as Op
11+
from ethereum_test_types.eof.v1 import Section
12+
from ethereum_test_types.types import EOA
1113
from ethereum_test_vm import Bytecode, EVMCodeType
1214

1315
from .. import EOF_FORK_NAME
@@ -54,6 +56,10 @@ def gas_test(
5456
tear_down_code: Bytecode,
5557
cold_gas: int,
5658
warm_gas: int | None = None,
59+
subject_subcontainer: Container | None = None,
60+
sender: EOA | None = None,
61+
subject_balance: int = 0,
62+
oog_difference: int = 1,
5763
):
5864
"""
5965
Creates a State Test to check the gas cost of a sequence of EOF code.
@@ -67,11 +73,21 @@ def gas_test(
6773
if warm_gas is None:
6874
warm_gas = cold_gas
6975

70-
sender = pre.fund_eoa()
76+
if not sender:
77+
sender = pre.fund_eoa()
7178

7279
address_baseline = pre.deploy_contract(Container.Code(setup_code + tear_down_code))
80+
code_subject = setup_code + subject_code + tear_down_code
7381
address_subject = pre.deploy_contract(
74-
Container.Code(setup_code + subject_code + tear_down_code)
82+
Container.Code(code_subject)
83+
if not subject_subcontainer
84+
else Container(
85+
sections=[
86+
Section.Code(code_subject),
87+
Section.Container(subject_subcontainer),
88+
]
89+
),
90+
balance=subject_balance,
7591
)
7692
# 2 times GAS, POP, CALL, 6 times PUSH1 - instructions charged for at every gas run
7793
gas_single_gas_run = 2 * 2 + 2 + WARM_ACCOUNT_ACCESS_GAS + 6 * 3
@@ -114,11 +130,11 @@ def gas_test(
114130
# - DUP7 is the gas of the baseline gas run, after other CALL args were pushed
115131
# - subtract the gas charged by the harness
116132
# - add warm gas charged by the subject
117-
# - subtract 1 to cause OOG exception
133+
# - subtract `oog_difference` to cause OOG exception (1 by default)
118134
+ Op.SSTORE(
119135
slot_oog_call_result,
120136
Op.CALL(
121-
gas=Op.ADD(warm_gas - gas_single_gas_run - 1, Op.DUP7),
137+
gas=Op.ADD(warm_gas - gas_single_gas_run - oog_difference, Op.DUP7),
122138
address=address_subject,
123139
),
124140
)

tests/prague/eip7692_eof_v1/eip7620_eof_create/helpers.py

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
slot_returndata_size = next(_slot)
1919
slot_max_depth = next(_slot)
2020
slot_call_or_create = next(_slot)
21+
slot_counter = next(_slot)
2122

2223
slot_last_slot = next(_slot)
2324

@@ -30,12 +31,7 @@
3031
value_eof_call_result_reverted = 1
3132
value_eof_call_result_failed = 2
3233

33-
smallest_runtime_subcontainer = Container(
34-
name="Runtime Subcontainer",
35-
sections=[
36-
Section.Code(code=Op.STOP),
37-
],
38-
)
34+
smallest_runtime_subcontainer = Container.Code(code=Op.STOP, name="Runtime Subcontainer")
3935

4036
smallest_initcode_subcontainer = Container(
4137
name="Initcode Subcontainer",
@@ -44,5 +40,46 @@
4440
Section.Container(container=smallest_runtime_subcontainer),
4541
],
4642
)
43+
smallest_initcode_subcontainer_gas = 2 * 3
44+
45+
aborting_container = Container.Code(Op.INVALID, name="Aborting Container")
46+
reverting_container = Container.Code(Op.REVERT(0, 0), name="Reverting Container")
47+
expensively_reverting_container = Container.Code(
48+
Op.SHA3(0, 32) + Op.REVERT(0, 0), name="Expensively Reverting Container"
49+
)
50+
expensively_reverting_container_gas = 2 * 3 + 30 + 3 + 6 + 2 * 3
51+
big_runtime_subcontainer = Container.Code(Op.NOOP * 10000 + Op.STOP, name="Big Subcontainer")
52+
53+
bigger_initcode_subcontainer_gas = 3 + 4 + 2 * 3
54+
bigger_initcode_subcontainer = Container(
55+
name="Bigger Initcode Subcontainer",
56+
sections=[
57+
Section.Code(
58+
code=Op.RJUMPI[len(Op.RETURNCONTRACT[0](0, 0))](1)
59+
+ Op.RETURNCONTRACT[0](0, 0)
60+
+ Op.RETURNCONTRACT[1](0, 0)
61+
),
62+
Section.Container(container=smallest_runtime_subcontainer),
63+
Section.Container(container=smallest_runtime_subcontainer),
64+
],
65+
)
4766

48-
aborting_container = Container.Code(Op.INVALID)
67+
data_runtime_container = smallest_runtime_subcontainer.copy()
68+
data_runtime_container.sections.append(Section.Data("0x00"))
69+
70+
data_initcode_subcontainer = Container(
71+
name="Data Initcode Subcontainer",
72+
sections=[
73+
Section.Code(code=Op.RETURNCONTRACT[0](0, 0)),
74+
Section.Container(container=data_runtime_container),
75+
],
76+
)
77+
78+
data_appending_initcode_subcontainer = Container(
79+
name="Data Appending Initcode Subcontainer",
80+
sections=[
81+
Section.Code(code=Op.RETURNCONTRACT[0](0, 1)),
82+
Section.Container(container=smallest_runtime_subcontainer),
83+
],
84+
)
85+
data_appending_initcode_subcontainer_gas = 2 * 3 + 3
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
"""
2+
Test good and bad EOFCREATE cases
3+
"""
4+
5+
import pytest
6+
7+
from ethereum_test_tools import Alloc, Environment, StateTestFiller, compute_eofcreate_address
8+
from ethereum_test_tools.eof.v1 import Container, Section
9+
from ethereum_test_tools.vm.opcode import Opcodes as Op
10+
11+
from .. import EOF_FORK_NAME
12+
from ..eip7069_extcall.test_gas import gas_test
13+
from .helpers import (
14+
aborting_container,
15+
big_runtime_subcontainer,
16+
bigger_initcode_subcontainer,
17+
bigger_initcode_subcontainer_gas,
18+
data_appending_initcode_subcontainer,
19+
data_appending_initcode_subcontainer_gas,
20+
data_initcode_subcontainer,
21+
data_runtime_container,
22+
expensively_reverting_container,
23+
expensively_reverting_container_gas,
24+
reverting_container,
25+
slot_counter,
26+
smallest_initcode_subcontainer,
27+
smallest_initcode_subcontainer_gas,
28+
smallest_runtime_subcontainer,
29+
)
30+
31+
REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7620.md"
32+
REFERENCE_SPEC_VERSION = "52ddbcdddcf72dd72427c319f2beddeb468e1737"
33+
34+
pytestmark = pytest.mark.valid_from(EOF_FORK_NAME)
35+
36+
37+
COLD_ACCOUNT_ACCESS_GAS = 2600
38+
WARM_ACCOUNT_ACCESS_GAS = 100
39+
CALL_WITH_VALUE_GAS = 9000
40+
ACCOUNT_CREATION_GAS = 25000
41+
42+
43+
EOFCREATE_GAS = 32000
44+
45+
46+
def make_initcode(runtime: Container):
47+
return Container(
48+
name="Initcode Subcontainer",
49+
sections=[
50+
Section.Code(code=Op.RETURNCONTRACT[0](0, 0)),
51+
Section.Container(container=runtime),
52+
],
53+
)
54+
55+
56+
def make_factory(initcode: Container):
57+
return Container(
58+
name="Factory Subcontainer",
59+
sections=[
60+
Section.Code(Op.EOFCREATE[0](0, 0, 0, 0) + Op.STOP),
61+
Section.Container(initcode),
62+
],
63+
)
64+
65+
66+
@pytest.mark.parametrize(
67+
["cold_gas", "value", "new_account"],
68+
[
69+
pytest.param(
70+
EOFCREATE_GAS,
71+
0,
72+
False,
73+
id="EOFCREATE",
74+
),
75+
pytest.param(
76+
EOFCREATE_GAS,
77+
1,
78+
False,
79+
id="EOFCREATE_with_value",
80+
),
81+
pytest.param(
82+
EOFCREATE_GAS,
83+
0,
84+
True,
85+
id="EOFCREATE_new_acc",
86+
),
87+
pytest.param(
88+
EOFCREATE_GAS,
89+
1,
90+
True,
91+
id="EOFCREATE_with_value_new_acc",
92+
),
93+
],
94+
)
95+
@pytest.mark.parametrize(
96+
["mem_expansion_size", "mem_expansion_extra_gas"],
97+
[
98+
pytest.param(0, 0, id="no_mem_expansion"),
99+
pytest.param(1, 3, id="1byte_mem_expansion"),
100+
pytest.param(32, 3, id="1word_mem_expansion"),
101+
pytest.param(33, 6, id="33bytes_mem_expansion"),
102+
],
103+
)
104+
@pytest.mark.parametrize(
105+
["initcode", "initcode_execution_cost", "runtime"],
106+
[
107+
pytest.param(
108+
smallest_initcode_subcontainer,
109+
smallest_initcode_subcontainer_gas,
110+
smallest_runtime_subcontainer,
111+
id="smallest_code",
112+
),
113+
pytest.param(
114+
make_initcode(aborting_container),
115+
smallest_initcode_subcontainer_gas,
116+
aborting_container,
117+
id="aborting_runtime",
118+
),
119+
pytest.param(
120+
reverting_container, smallest_initcode_subcontainer_gas, None, id="reverting_initcode"
121+
),
122+
pytest.param(
123+
expensively_reverting_container,
124+
expensively_reverting_container_gas,
125+
None,
126+
id="expensively_reverting_initcode",
127+
),
128+
pytest.param(
129+
make_initcode(big_runtime_subcontainer),
130+
smallest_initcode_subcontainer_gas,
131+
big_runtime_subcontainer,
132+
id="big_runtime",
133+
),
134+
pytest.param(
135+
make_initcode(make_factory(smallest_initcode_subcontainer)),
136+
smallest_initcode_subcontainer_gas,
137+
make_factory(smallest_initcode_subcontainer),
138+
id="nested_initcode",
139+
),
140+
pytest.param(
141+
bigger_initcode_subcontainer,
142+
bigger_initcode_subcontainer_gas,
143+
smallest_runtime_subcontainer,
144+
id="bigger_initcode",
145+
),
146+
pytest.param(
147+
data_initcode_subcontainer,
148+
smallest_initcode_subcontainer_gas,
149+
data_runtime_container,
150+
id="data_initcode",
151+
),
152+
pytest.param(
153+
data_appending_initcode_subcontainer,
154+
data_appending_initcode_subcontainer_gas,
155+
data_runtime_container,
156+
id="data_appending_initcode",
157+
),
158+
],
159+
)
160+
def test_eofcreate_gas(
161+
state_test: StateTestFiller,
162+
pre: Alloc,
163+
cold_gas: int,
164+
value: int,
165+
new_account: bool,
166+
mem_expansion_size: int,
167+
mem_expansion_extra_gas: int,
168+
initcode: Container,
169+
initcode_execution_cost: int,
170+
runtime: Container,
171+
):
172+
"""Tests variations of EOFCREATE gas"""
173+
initcode_hashing_cost = 6 * ((len(initcode) + 31) // 32)
174+
deployed_code_cost = 200 * len(runtime) if runtime else 0
175+
176+
sender = pre.fund_eoa()
177+
178+
salt_addresses = [compute_eofcreate_address(sender, i, initcode) for i in range(10)]
179+
180+
if not new_account:
181+
for a in salt_addresses:
182+
pre.fund_address(a, 1)
183+
184+
# Using `TLOAD` / `TSTORE` to work around warm/cold gas differences. We need a counter to pick
185+
# a distinct salt on each `EOFCREATE` and avoid running into address conflicts.
186+
code_increment_counter = (
187+
Op.TLOAD(slot_counter) + Op.DUP1 + Op.TSTORE(slot_counter, Op.PUSH1(1) + Op.ADD)
188+
)
189+
190+
gas_test(
191+
state_test,
192+
Environment(),
193+
pre,
194+
setup_code=Op.PUSH1(mem_expansion_size)
195+
+ Op.PUSH0
196+
+ code_increment_counter
197+
+ Op.PUSH32(value),
198+
subject_code=Op.EOFCREATE[0],
199+
tear_down_code=Op.STOP,
200+
cold_gas=cold_gas
201+
+ mem_expansion_extra_gas
202+
+ initcode_hashing_cost
203+
+ initcode_execution_cost
204+
+ deployed_code_cost,
205+
subject_subcontainer=initcode,
206+
sender=sender,
207+
subject_balance=10**18,
208+
oog_difference=initcode_execution_cost + deployed_code_cost + 1,
209+
)

0 commit comments

Comments
 (0)