55
66import pytest
77
8- from ethereum_test_tools import Account , Alloc , Environment , StateTestFiller , Transaction
8+ from ethereum_test_tools import Alloc , Environment , StateTestFiller
99from ethereum_test_tools .eof .v1 import Container
1010from 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
1313from .. import EOF_FORK_NAME
14+ from ..gas_test import gas_test
1415from . 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
2417REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
2518REFERENCE_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)
232114def 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 )
0 commit comments