4
4
that produce a memory expansion, and potentially an out-of-gas error.
5
5
6
6
""" # noqa: E501
7
- from typing import Mapping , Tuple
7
+ from typing import Mapping
8
8
9
9
import pytest
10
10
11
11
from ethereum_test_tools import Account , Address , Alloc , Bytecode , Environment
12
12
from ethereum_test_tools import Opcodes as Op
13
- from ethereum_test_tools import StateTestFiller , Storage , Transaction , cost_memory_bytes
13
+ from ethereum_test_tools import StateTestFiller , Transaction , cost_memory_bytes
14
14
15
15
from .common import REFERENCE_SPEC_GIT_PATH , REFERENCE_SPEC_VERSION
16
16
@@ -31,6 +31,8 @@ def callee_bytecode(dest: int, src: int, length: int) -> Bytecode:
31
31
# Pushes for the return operation
32
32
bytecode += Op .PUSH1 (0x00 ) + Op .PUSH1 (0x00 )
33
33
34
+ bytecode += Op .SSTORE (1 , 1 )
35
+
34
36
# Perform the mcopy operation
35
37
bytecode += Op .MCOPY (dest , src , length )
36
38
@@ -40,14 +42,16 @@ def callee_bytecode(dest: int, src: int, length: int) -> Bytecode:
40
42
41
43
42
44
@pytest .fixture
43
- def subcall_exact_cost (
45
+ def call_exact_cost (
44
46
initial_memory : bytes ,
45
47
dest : int ,
46
48
length : int ,
47
49
) -> int :
48
50
"""
49
51
Returns the exact cost of the subcall, based on the initial memory and the length of the copy.
50
52
"""
53
+ intrinsic_cost = 21000 + len (initial_memory ) * 16
54
+
51
55
mcopy_cost = 3
52
56
mcopy_cost += 3 * ((length + 31 ) // 32 )
53
57
if length > 0 and dest + length > len (initial_memory ):
@@ -57,36 +61,18 @@ def subcall_exact_cost(
57
61
calldatacopy_cost += 3 * ((len (initial_memory ) + 31 ) // 32 )
58
62
calldatacopy_cost += cost_memory_bytes (len (initial_memory ), 0 )
59
63
60
- pushes_cost = 3 * 7
64
+ pushes_cost = 3 * 9
61
65
calldatasize_cost = 2
62
- return mcopy_cost + calldatacopy_cost + pushes_cost + calldatasize_cost
63
-
64
-
65
- @pytest .fixture
66
- def bytecode_storage (
67
- subcall_exact_cost : int ,
68
- successful : bool ,
69
- memory_expansion_address : Address ,
70
- ) -> Tuple [Bytecode , Storage .StorageDictType ]:
71
- """
72
- Prepares the bytecode and storage for the test, based on the expected result of the subcall
73
- (whether it succeeds or fails depending on the length of the memory expansion).
74
- """
75
- bytecode = Bytecode ()
76
- storage = {}
77
66
78
- # Pass on the calldata
79
- bytecode += Op . CALLDATACOPY ( 0x00 , 0x00 , Op . CALLDATASIZE ())
80
-
81
- subcall_gas = subcall_exact_cost if successful else subcall_exact_cost - 1
82
-
83
- # Perform the subcall and store a one in the result location
84
- bytecode += Op . SSTORE (
85
- Op . CALL ( subcall_gas , memory_expansion_address , 0 , 0 , Op . CALLDATASIZE (), 0 , 0 ), 1
67
+ sstore_cost = 22100
68
+ return (
69
+ intrinsic_cost
70
+ + mcopy_cost
71
+ + calldatacopy_cost
72
+ + pushes_cost
73
+ + calldatasize_cost
74
+ + sstore_cost
86
75
)
87
- storage [int (successful )] = 1
88
-
89
- return (bytecode , storage )
90
76
91
77
92
78
@pytest .fixture
@@ -101,10 +87,11 @@ def block_gas_limit() -> int: # noqa: D103
101
87
102
88
@pytest .fixture
103
89
def tx_gas_limit ( # noqa: D103
104
- subcall_exact_cost : int ,
90
+ call_exact_cost : int ,
105
91
block_gas_limit : int ,
92
+ successful : bool ,
106
93
) -> int :
107
- return min (max ( 500_000 , subcall_exact_cost * 2 ), block_gas_limit )
94
+ return min (call_exact_cost - ( 0 if successful else 1 ), block_gas_limit )
108
95
109
96
110
97
@pytest .fixture
@@ -115,14 +102,7 @@ def env( # noqa: D103
115
102
116
103
117
104
@pytest .fixture
118
- def caller_address ( # noqa: D103
119
- pre : Alloc , bytecode_storage : Tuple [bytes , Storage .StorageDictType ]
120
- ) -> Address :
121
- return pre .deploy_contract (code = bytecode_storage [0 ])
122
-
123
-
124
- @pytest .fixture
125
- def memory_expansion_address (pre : Alloc , callee_bytecode : Bytecode ) -> Address : # noqa: D103
105
+ def caller_address (pre : Alloc , callee_bytecode : bytes ) -> Address : # noqa: D103
126
106
return pre .deploy_contract (code = callee_bytecode )
127
107
128
108
@@ -151,11 +131,10 @@ def tx( # noqa: D103
151
131
152
132
@pytest .fixture
153
133
def post ( # noqa: D103
154
- caller_address : Address , bytecode_storage : Tuple [bytes , Storage .StorageDictType ]
134
+ caller_address : Address ,
135
+ successful : bool ,
155
136
) -> Mapping :
156
- return {
157
- caller_address : Account (storage = bytecode_storage [1 ]),
158
- }
137
+ return {caller_address : Account (storage = {1 : 1 } if successful else {})}
159
138
160
139
161
140
@pytest .mark .parametrize (
@@ -189,14 +168,15 @@ def post( # noqa: D103
189
168
@pytest .mark .parametrize (
190
169
"initial_memory" ,
191
170
[
192
- bytes (range (0x00 , 0x100 )),
171
+ bytes (range (0x01 , 0xFF )), # NOTE: must be non-zero bytes
193
172
bytes (),
194
173
],
195
174
ids = [
196
175
"from_existent_memory" ,
197
176
"from_empty_memory" ,
198
177
],
199
178
)
179
+ @pytest .mark .with_all_evm_code_types
200
180
@pytest .mark .valid_from ("Cancun" )
201
181
def test_mcopy_memory_expansion (
202
182
state_test : StateTestFiller ,
@@ -242,22 +222,23 @@ def test_mcopy_memory_expansion(
242
222
],
243
223
)
244
224
@pytest .mark .parametrize (
245
- "subcall_exact_cost " ,
225
+ "call_exact_cost " ,
246
226
[2 ** 128 - 1 ],
247
227
ids = ["" ],
248
228
) # Limit subcall gas, otherwise it would be impossibly large
249
229
@pytest .mark .parametrize ("successful" , [False ])
250
230
@pytest .mark .parametrize (
251
231
"initial_memory" ,
252
232
[
253
- bytes (range (0x00 , 0x100 )),
233
+ bytes (range (0x01 , 0xFF )), # NOTE: must be non-zero bytes
254
234
bytes (),
255
235
],
256
236
ids = [
257
237
"from_existent_memory" ,
258
238
"from_empty_memory" ,
259
239
],
260
240
)
241
+ @pytest .mark .with_all_evm_code_types
261
242
@pytest .mark .valid_from ("Cancun" )
262
243
def test_mcopy_huge_memory_expansion (
263
244
state_test : StateTestFiller ,
0 commit comments