Skip to content

Commit b035ad3

Browse files
authored
new(tests): EOF - EIP-7620 EOFCREATE gas testing (#785)
* new(tests): EOF - EIP-7620 - EOFCREATE gas testing * fix(tests): EIP-7069/7620 - Use cost_memory_bytes * fix(tests): EIP-7620 - fix new_account tests for EOFCREATE * fix(tests): EIP-7620 - remaining review comments * fix(tests): EIP-7620 - use Container.Init * fix(tests): EIP-7620 - simplify parametrization in test_gas.py
1 parent e01f2d7 commit b035ad3

File tree

5 files changed

+361
-141
lines changed

5 files changed

+361
-141
lines changed

tests/prague/eip7692_eof_v1/eip7069_extcall/helpers.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
slot_call_status = next(_slot)
1717
slot_calldata_1 = next(_slot)
1818
slot_calldata_2 = next(_slot)
19-
slot_cold_gas = next(_slot)
20-
slot_warm_gas = next(_slot)
21-
slot_oog_call_result = next(_slot)
22-
slot_sanity_call_result = next(_slot)
2319

2420
slot_last_slot = next(_slot)
2521

@@ -28,8 +24,6 @@
2824

2925
"""Storage values for common testing fields"""
3026
value_code_worked = 0x2015
31-
value_call_legacy_abort = 0
32-
value_call_legacy_success = 1
3327

3428
"""Memory and storage value for calldata"""
3529
value_calldata_1 = 0xC1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1C1

tests/prague/eip7692_eof_v1/eip7069_extcall/test_gas.py

Lines changed: 12 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,14 @@
55

66
import pytest
77

8-
from ethereum_test_tools import Account, Alloc, Environment, StateTestFiller, Transaction
8+
from ethereum_test_tools import Alloc, Environment, StateTestFiller
99
from ethereum_test_tools.eof.v1 import Container
1010
from ethereum_test_tools.vm.opcode import Opcodes as Op
11-
from ethereum_test_vm import Bytecode, EVMCodeType
11+
from ethereum_test_types.helpers import cost_memory_bytes
1212

1313
from .. import EOF_FORK_NAME
14+
from ..gas_test import gas_test
1415
from . import REFERENCE_SPEC_GIT_PATH, REFERENCE_SPEC_VERSION
15-
from .helpers import (
16-
slot_cold_gas,
17-
slot_oog_call_result,
18-
slot_sanity_call_result,
19-
slot_warm_gas,
20-
value_call_legacy_abort,
21-
value_call_legacy_success,
22-
)
2316

2417
REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
2518
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION
@@ -45,112 +38,6 @@ def state_env() -> Environment:
4538
return Environment()
4639

4740

48-
def gas_test(
49-
state_test: StateTestFiller,
50-
env: Environment,
51-
pre: Alloc,
52-
setup_code: Bytecode,
53-
subject_code: Bytecode,
54-
tear_down_code: Bytecode,
55-
cold_gas: int,
56-
warm_gas: int | None = None,
57-
):
58-
"""
59-
Creates a State Test to check the gas cost of a sequence of EOF code.
60-
61-
`setup_code` and `tear_down_code` are called multiple times during the test, and MUST NOT have
62-
any side-effects which persist across message calls, and in particular, any effects on the gas
63-
usage of `subject_code`.
64-
"""
65-
if cold_gas <= 0:
66-
raise ValueError(f"Target gas allocations (cold_gas) must be > 0, got {cold_gas}")
67-
if warm_gas is None:
68-
warm_gas = cold_gas
69-
70-
sender = pre.fund_eoa()
71-
72-
address_baseline = pre.deploy_contract(Container.Code(setup_code + tear_down_code))
73-
address_subject = pre.deploy_contract(
74-
Container.Code(setup_code + subject_code + tear_down_code)
75-
)
76-
# 2 times GAS, POP, CALL, 6 times PUSH1 - instructions charged for at every gas run
77-
gas_single_gas_run = 2 * 2 + 2 + WARM_ACCOUNT_ACCESS_GAS + 6 * 3
78-
address_legacy_harness = pre.deploy_contract(
79-
code=(
80-
# warm subject and baseline without executing
81-
(Op.BALANCE(address_subject) + Op.POP + Op.BALANCE(address_baseline) + Op.POP)
82-
# Baseline gas run
83-
+ (
84-
Op.GAS
85-
+ Op.CALL(address=address_baseline, gas=Op.GAS)
86-
+ Op.POP
87-
+ Op.GAS
88-
+ Op.SWAP1
89-
+ Op.SUB
90-
)
91-
# cold gas run
92-
+ (
93-
Op.GAS
94-
+ Op.CALL(address=address_subject, gas=Op.GAS)
95-
+ Op.POP
96-
+ Op.GAS
97-
+ Op.SWAP1
98-
+ Op.SUB
99-
)
100-
# warm gas run
101-
+ (
102-
Op.GAS
103-
+ Op.CALL(address=address_subject, gas=Op.GAS)
104-
+ Op.POP
105-
+ Op.GAS
106-
+ Op.SWAP1
107-
+ Op.SUB
108-
)
109-
# Store warm gas: DUP3 is the gas of the baseline gas run
110-
+ (Op.DUP3 + Op.SWAP1 + Op.SUB + Op.PUSH2(slot_warm_gas) + Op.SSTORE)
111-
# store cold gas: DUP2 is the gas of the baseline gas run
112-
+ (Op.DUP2 + Op.SWAP1 + Op.SUB + Op.PUSH2(slot_cold_gas) + Op.SSTORE)
113-
# oog gas run:
114-
# - DUP7 is the gas of the baseline gas run, after other CALL args were pushed
115-
# - subtract the gas charged by the harness
116-
# - add warm gas charged by the subject
117-
# - subtract 1 to cause OOG exception
118-
+ Op.SSTORE(
119-
slot_oog_call_result,
120-
Op.CALL(
121-
gas=Op.ADD(warm_gas - gas_single_gas_run - 1, Op.DUP7),
122-
address=address_subject,
123-
),
124-
)
125-
# sanity gas run: not subtracting 1 to see if enough gas makes the call succeed
126-
+ Op.SSTORE(
127-
slot_sanity_call_result,
128-
Op.CALL(
129-
gas=Op.ADD(warm_gas - gas_single_gas_run, Op.DUP7),
130-
address=address_subject,
131-
),
132-
)
133-
+ Op.STOP
134-
),
135-
evm_code_type=EVMCodeType.LEGACY, # Needs to be legacy to use GAS opcode
136-
)
137-
138-
post = {
139-
address_legacy_harness: Account(
140-
storage={
141-
slot_warm_gas: warm_gas,
142-
slot_cold_gas: cold_gas,
143-
slot_oog_call_result: value_call_legacy_abort,
144-
slot_sanity_call_result: value_call_legacy_success,
145-
},
146-
),
147-
}
148-
149-
tx = Transaction(to=address_legacy_harness, gas_limit=env.gas_limit, sender=sender)
150-
151-
state_test(env=env, pre=pre, tx=tx, post=post)
152-
153-
15441
@pytest.mark.parametrize(
15542
["opcode", "pre_setup", "cold_gas", "warm_gas", "new_account"],
15643
[
@@ -221,13 +108,8 @@ def gas_test(
221108
],
222109
)
223110
@pytest.mark.parametrize(
224-
["mem_expansion_size", "mem_expansion_extra_gas"],
225-
[
226-
pytest.param(0, 0, id="no_mem_expansion"),
227-
pytest.param(1, 3, id="1byte_mem_expansion"),
228-
pytest.param(32, 3, id="1word_mem_expansion"),
229-
pytest.param(33, 6, id="33bytes_mem_expansion"),
230-
],
111+
"mem_expansion_bytes",
112+
[0, 1, 32, 33],
231113
)
232114
def test_ext_calls_gas(
233115
state_test: StateTestFiller,
@@ -238,8 +120,7 @@ def test_ext_calls_gas(
238120
cold_gas: int,
239121
warm_gas: int,
240122
new_account: bool,
241-
mem_expansion_size: int,
242-
mem_expansion_extra_gas: int,
123+
mem_expansion_bytes: int,
243124
):
244125
"""Tests variations of EXT*CALL gas, both warm and cold, without and with mem expansions"""
245126
address_target = (
@@ -250,9 +131,12 @@ def test_ext_calls_gas(
250131
state_test,
251132
state_env,
252133
pre,
253-
setup_code=pre_setup + Op.PUSH1(mem_expansion_size) + Op.PUSH0 + Op.PUSH20(address_target),
134+
setup_code=pre_setup
135+
+ Op.PUSH1(mem_expansion_bytes)
136+
+ Op.PUSH0
137+
+ Op.PUSH20(address_target),
254138
subject_code=opcode,
255139
tear_down_code=Op.STOP,
256-
cold_gas=cold_gas + mem_expansion_extra_gas,
257-
warm_gas=warm_gas + mem_expansion_extra_gas,
140+
cold_gas=cold_gas + cost_memory_bytes(mem_expansion_bytes, 0),
141+
warm_gas=warm_gas + cost_memory_bytes(mem_expansion_bytes, 0),
258142
)

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

0 commit comments

Comments
 (0)