@@ -75,3 +75,75 @@ def test_call_large_offset_mstore(
75
75
)
76
76
},
77
77
)
78
+
79
+
80
+ # TODO: There's an issue with gas definitions on forks previous to Berlin, remove this when fixed.
81
+ # https://github.com/ethereum/execution-spec-tests/pull/1952#discussion_r2237634275
82
+ @pytest .mark .valid_from ("Berlin" )
83
+ def test_call_memory_expands_on_early_revert (
84
+ state_test : StateTestFiller ,
85
+ pre : Alloc ,
86
+ fork : Fork ,
87
+ ):
88
+ """
89
+ When CALL reverts early (e.g. because of not enough balance by the sender),
90
+ memory should be expanded anyway.
91
+ We check this with an MSTORE.
92
+
93
+ This is for a bug in an EVM implementation where memory is expanded after executing a CALL, but
94
+ not when an early revert happens.
95
+ """
96
+ sender = pre .fund_eoa ()
97
+
98
+ gsc = fork .gas_costs ()
99
+ ret_size = 128 # arbitrary number, greater than memory size to trigger an expansion
100
+
101
+ call_measure = CodeGasMeasure (
102
+ code = Op .CALL (gas = 0 , value = 100 , ret_size = ret_size ), # CALL with value
103
+ overhead_cost = gsc .G_VERY_LOW * len (Op .CALL .kwargs ), # Cost of pushing CALL args
104
+ extra_stack_items = 1 , # Because CALL pushes 1 item to the stack
105
+ sstore_key = 0 ,
106
+ stop = False , # Because it's the first CodeGasMeasure
107
+ )
108
+ mstore_measure = CodeGasMeasure (
109
+ code = Op .MSTORE (offset = ret_size // 2 , value = 1 ), # Low offset for not expanding memory
110
+ overhead_cost = gsc .G_VERY_LOW * len (Op .MSTORE .kwargs ), # Cost of pushing MSTORE args
111
+ extra_stack_items = 0 ,
112
+ sstore_key = 1 ,
113
+ )
114
+
115
+ # Contract without enough balance to send value transfer
116
+ contract = pre .deploy_contract (code = call_measure + mstore_measure , balance = 0 )
117
+
118
+ tx = Transaction (
119
+ gas_limit = 500_000 ,
120
+ to = contract ,
121
+ value = 0 ,
122
+ sender = sender ,
123
+ )
124
+
125
+ memory_expansion_gas_calc = fork .memory_expansion_gas_calculator ()
126
+ # call cost: address_access_cost + new_acc_cost + memory_expansion_cost + value - stipend
127
+ call_cost = (
128
+ gsc .G_COLD_ACCOUNT_ACCESS
129
+ + gsc .G_NEW_ACCOUNT
130
+ + memory_expansion_gas_calc (new_bytes = ret_size )
131
+ + gsc .G_CALL_VALUE
132
+ - gsc .G_CALL_STIPEND
133
+ )
134
+
135
+ # mstore cost: base cost. No memory expansion cost needed, it was expanded on CALL.
136
+ mstore_cost = gsc .G_MEMORY
137
+ state_test (
138
+ env = Environment (),
139
+ pre = pre ,
140
+ tx = tx ,
141
+ post = {
142
+ contract : Account (
143
+ storage = {
144
+ 0 : call_cost ,
145
+ 1 : mstore_cost ,
146
+ },
147
+ )
148
+ },
149
+ )
0 commit comments