@@ -18,6 +18,7 @@ package state
18
18
19
19
import (
20
20
"maps"
21
+ gomath "math"
21
22
22
23
"github.com/ethereum/go-ethereum/common"
23
24
"github.com/ethereum/go-ethereum/common/math"
@@ -92,127 +93,156 @@ func (ae *AccessEvents) Copy() *AccessEvents {
92
93
93
94
// AddAccount returns the gas to be charged for each of the currently cold
94
95
// member fields of an account.
95
- func (ae * AccessEvents ) AddAccount (addr common.Address , isWrite bool ) uint64 {
96
- var gas uint64
97
- gas += ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , isWrite )
98
- gas += ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , isWrite )
96
+ func (ae * AccessEvents ) AddAccount (addr common.Address , isWrite bool , availableGas uint64 ) uint64 {
97
+ var gas uint64 // accumulate the consumed gas
98
+ consumed , wanted := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , isWrite , availableGas )
99
+ if consumed < wanted {
100
+ return wanted
101
+ }
102
+ gas += consumed
103
+ consumed , wanted = ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , isWrite , availableGas - consumed )
104
+ if consumed < wanted {
105
+ return wanted + gas
106
+ }
107
+ gas += wanted
99
108
return gas
100
109
}
101
110
102
111
// MessageCallGas returns the gas to be charged for each of the currently
103
112
// cold member fields of an account, that need to be touched when making a message
104
113
// call to that account.
105
- func (ae * AccessEvents ) MessageCallGas (destination common.Address ) uint64 {
106
- var gas uint64
107
- gas += ae .touchAddressAndChargeGas (destination , zeroTreeIndex , utils .BasicDataLeafKey , false )
108
- return gas
114
+ func (ae * AccessEvents ) MessageCallGas (destination common.Address , availableGas uint64 ) uint64 {
115
+ _ , wanted := ae .touchAddressAndChargeGas (destination , zeroTreeIndex , utils .BasicDataLeafKey , false , availableGas )
116
+ if wanted == 0 {
117
+ wanted = params .WarmStorageReadCostEIP2929
118
+ }
119
+ return wanted
109
120
}
110
121
111
122
// ValueTransferGas returns the gas to be charged for each of the currently
112
123
// cold balance member fields of the caller and the callee accounts.
113
- func (ae * AccessEvents ) ValueTransferGas (callerAddr , targetAddr common.Address ) uint64 {
114
- var gas uint64
115
- gas += ae .touchAddressAndChargeGas (callerAddr , zeroTreeIndex , utils .BasicDataLeafKey , true )
116
- gas += ae .touchAddressAndChargeGas (targetAddr , zeroTreeIndex , utils .BasicDataLeafKey , true )
117
- return gas
124
+ func (ae * AccessEvents ) ValueTransferGas (callerAddr , targetAddr common.Address , availableGas uint64 ) uint64 {
125
+ _ , wanted1 := ae .touchAddressAndChargeGas (callerAddr , zeroTreeIndex , utils .BasicDataLeafKey , true , availableGas )
126
+ if wanted1 > availableGas {
127
+ return wanted1
128
+ }
129
+ _ , wanted2 := ae .touchAddressAndChargeGas (targetAddr , zeroTreeIndex , utils .BasicDataLeafKey , true , availableGas - wanted1 )
130
+ if wanted1 + wanted2 > availableGas {
131
+ return params .WarmStorageReadCostEIP2929
132
+ }
133
+ return wanted1 + wanted2
118
134
}
119
135
120
136
// ContractCreatePreCheckGas charges access costs before
121
137
// a contract creation is initiated. It is just reads, because the
122
138
// address collision is done before the transfer, and so no write
123
139
// are guaranteed to happen at this point.
124
- func (ae * AccessEvents ) ContractCreatePreCheckGas (addr common.Address ) uint64 {
125
- var gas uint64
126
- gas += ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , false )
127
- gas += ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , false )
128
- return gas
140
+ func (ae * AccessEvents ) ContractCreatePreCheckGas (addr common.Address , availableGas uint64 ) uint64 {
141
+ consumed , wanted1 := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , false , availableGas )
142
+ _ , wanted2 := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , false , availableGas - consumed )
143
+ return wanted1 + wanted2
129
144
}
130
145
131
146
// ContractCreateInitGas returns the access gas costs for the initialization of
132
147
// a contract creation.
133
- func (ae * AccessEvents ) ContractCreateInitGas (addr common.Address ) uint64 {
148
+ func (ae * AccessEvents ) ContractCreateInitGas (addr common.Address , availableGas uint64 ) ( uint64 , uint64 ) {
134
149
var gas uint64
135
- gas += ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , true )
136
- gas += ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , true )
137
- return gas
150
+ consumed , wanted1 := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , true , availableGas )
151
+ gas += consumed
152
+ consumed , wanted2 := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , true , availableGas - consumed )
153
+ gas += consumed
154
+ return gas , wanted1 + wanted2
138
155
}
139
156
140
157
// AddTxOrigin adds the member fields of the sender account to the access event list,
141
158
// so that cold accesses are not charged, since they are covered by the 21000 gas.
142
159
func (ae * AccessEvents ) AddTxOrigin (originAddr common.Address ) {
143
- ae .touchAddressAndChargeGas (originAddr , zeroTreeIndex , utils .BasicDataLeafKey , true )
144
- ae .touchAddressAndChargeGas (originAddr , zeroTreeIndex , utils .CodeHashLeafKey , false )
160
+ ae .touchAddressAndChargeGas (originAddr , zeroTreeIndex , utils .BasicDataLeafKey , true , gomath . MaxUint64 )
161
+ ae .touchAddressAndChargeGas (originAddr , zeroTreeIndex , utils .CodeHashLeafKey , false , gomath . MaxUint64 )
145
162
}
146
163
147
164
// AddTxDestination adds the member fields of the sender account to the access event list,
148
165
// so that cold accesses are not charged, since they are covered by the 21000 gas.
149
- func (ae * AccessEvents ) AddTxDestination (addr common.Address , sendsValue bool ) {
150
- ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , sendsValue )
151
- ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , false )
166
+ func (ae * AccessEvents ) AddTxDestination (addr common.Address , sendsValue , doesntExist bool ) {
167
+ ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , sendsValue , gomath . MaxUint64 )
168
+ ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , doesntExist , gomath . MaxUint64 )
152
169
}
153
170
154
171
// SlotGas returns the amount of gas to be charged for a cold storage access.
155
- func (ae * AccessEvents ) SlotGas (addr common.Address , slot common.Hash , isWrite bool ) uint64 {
172
+ func (ae * AccessEvents ) SlotGas (addr common.Address , slot common.Hash , isWrite bool , availableGas uint64 , chargeWarmCosts bool ) uint64 {
156
173
treeIndex , subIndex := utils .StorageIndex (slot .Bytes ())
157
- return ae .touchAddressAndChargeGas (addr , * treeIndex , subIndex , isWrite )
174
+ _ , wanted := ae .touchAddressAndChargeGas (addr , * treeIndex , subIndex , isWrite , availableGas )
175
+ if wanted == 0 && chargeWarmCosts {
176
+ wanted = params .WarmStorageReadCostEIP2929
177
+ }
178
+ return wanted
158
179
}
159
180
160
181
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold
161
182
// access cost to be charged, if need be.
162
- func (ae * AccessEvents ) touchAddressAndChargeGas (addr common.Address , treeIndex uint256.Int , subIndex byte , isWrite bool ) uint64 {
163
- stemRead , selectorRead , stemWrite , selectorWrite , selectorFill := ae .touchAddress (addr , treeIndex , subIndex , isWrite )
164
-
165
- var gas uint64
166
- if stemRead {
167
- gas += params .WitnessBranchReadCost
168
- }
169
- if selectorRead {
170
- gas += params .WitnessChunkReadCost
171
- }
172
- if stemWrite {
173
- gas += params .WitnessBranchWriteCost
174
- }
175
- if selectorWrite {
176
- gas += params .WitnessChunkWriteCost
177
- }
178
- if selectorFill {
179
- gas += params .WitnessChunkFillCost
180
- }
181
- return gas
182
- }
183
-
184
- // touchAddress adds any missing access event to the access event list.
185
- func (ae * AccessEvents ) touchAddress (addr common.Address , treeIndex uint256.Int , subIndex byte , isWrite bool ) (bool , bool , bool , bool , bool ) {
183
+ func (ae * AccessEvents ) touchAddressAndChargeGas (addr common.Address , treeIndex uint256.Int , subIndex byte , isWrite bool , availableGas uint64 ) (uint64 , uint64 ) {
186
184
branchKey := newBranchAccessKey (addr , treeIndex )
187
185
chunkKey := newChunkAccessKey (branchKey , subIndex )
188
186
189
187
// Read access.
190
188
var branchRead , chunkRead bool
191
189
if _ , hasStem := ae .branches [branchKey ]; ! hasStem {
192
190
branchRead = true
193
- ae .branches [branchKey ] = AccessWitnessReadFlag
194
191
}
195
192
if _ , hasSelector := ae .chunks [chunkKey ]; ! hasSelector {
196
193
chunkRead = true
197
- ae .chunks [chunkKey ] = AccessWitnessReadFlag
198
194
}
199
195
200
196
// Write access.
201
197
var branchWrite , chunkWrite , chunkFill bool
202
198
if isWrite {
203
199
if (ae .branches [branchKey ] & AccessWitnessWriteFlag ) == 0 {
204
200
branchWrite = true
205
- ae .branches [branchKey ] |= AccessWitnessWriteFlag
206
201
}
207
202
208
203
chunkValue := ae .chunks [chunkKey ]
209
204
if (chunkValue & AccessWitnessWriteFlag ) == 0 {
210
205
chunkWrite = true
211
- ae .chunks [chunkKey ] |= AccessWitnessWriteFlag
212
206
}
213
- // TODO: charge chunk filling costs if the leaf was previously empty in the state
214
207
}
215
- return branchRead , chunkRead , branchWrite , chunkWrite , chunkFill
208
+
209
+ var gas uint64
210
+ if branchRead {
211
+ gas += params .WitnessBranchReadCost
212
+ }
213
+ if chunkRead {
214
+ gas += params .WitnessChunkReadCost
215
+ }
216
+ if branchWrite {
217
+ gas += params .WitnessBranchWriteCost
218
+ }
219
+ if chunkWrite {
220
+ gas += params .WitnessChunkWriteCost
221
+ }
222
+ if chunkFill {
223
+ gas += params .WitnessChunkFillCost
224
+ }
225
+
226
+ if availableGas < gas {
227
+ // consumed != wanted
228
+ return availableGas , gas
229
+ }
230
+
231
+ if branchRead {
232
+ ae .branches [branchKey ] = AccessWitnessReadFlag
233
+ }
234
+ if branchWrite {
235
+ ae .branches [branchKey ] |= AccessWitnessWriteFlag
236
+ }
237
+ if chunkRead {
238
+ ae .chunks [chunkKey ] = AccessWitnessReadFlag
239
+ }
240
+ if chunkWrite {
241
+ ae .chunks [chunkKey ] |= AccessWitnessWriteFlag
242
+ }
243
+
244
+ // consumed == wanted
245
+ return gas , gas
216
246
}
217
247
218
248
type branchAccessKey struct {
@@ -240,15 +270,15 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
240
270
}
241
271
242
272
// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
243
- func (ae * AccessEvents ) CodeChunksRangeGas (contractAddr common.Address , startPC , size uint64 , codeLen uint64 , isWrite bool ) uint64 {
273
+ func (ae * AccessEvents ) CodeChunksRangeGas (contractAddr common.Address , startPC , size uint64 , codeLen uint64 , isWrite bool , availableGas uint64 ) ( uint64 , uint64 ) {
244
274
// note that in the case where the copied code is outside the range of the
245
275
// contract code but touches the last leaf with contract code in it,
246
276
// we don't include the last leaf of code in the AccessWitness. The
247
277
// reason that we do not need the last leaf is the account's code size
248
278
// is already in the AccessWitness so a stateless verifier can see that
249
279
// the code from the last leaf is not needed.
250
280
if (codeLen == 0 && size == 0 ) || startPC > codeLen {
251
- return 0
281
+ return 0 , 0
252
282
}
253
283
254
284
endPC := startPC + size
@@ -263,29 +293,48 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
263
293
for chunkNumber := startPC / 31 ; chunkNumber <= endPC / 31 ; chunkNumber ++ {
264
294
treeIndex := * uint256 .NewInt ((chunkNumber + 128 ) / 256 )
265
295
subIndex := byte ((chunkNumber + 128 ) % 256 )
266
- gas := ae .touchAddressAndChargeGas (contractAddr , treeIndex , subIndex , isWrite )
296
+ consumed , wanted := ae .touchAddressAndChargeGas (contractAddr , treeIndex , subIndex , isWrite , availableGas )
297
+ // did we OOG ?
298
+ if wanted > consumed {
299
+ return statelessGasCharged + consumed , statelessGasCharged + wanted
300
+ }
267
301
var overflow bool
268
- statelessGasCharged , overflow = math .SafeAdd (statelessGasCharged , gas )
302
+ statelessGasCharged , overflow = math .SafeAdd (statelessGasCharged , consumed )
269
303
if overflow {
270
304
panic ("overflow when adding gas" )
271
305
}
306
+ availableGas -= consumed
272
307
}
273
- return statelessGasCharged
308
+ return statelessGasCharged , statelessGasCharged
274
309
}
275
310
276
311
// BasicDataGas adds the account's basic data to the accessed data, and returns the
277
312
// amount of gas that it costs.
278
313
// Note that an access in write mode implies an access in read mode, whereas an
279
314
// access in read mode does not imply an access in write mode.
280
- func (ae * AccessEvents ) BasicDataGas (addr common.Address , isWrite bool ) uint64 {
281
- return ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , isWrite )
315
+ func (ae * AccessEvents ) BasicDataGas (addr common.Address , isWrite bool , availableGas uint64 , chargeWarmCosts bool ) uint64 {
316
+ _ , wanted := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , isWrite , availableGas )
317
+ if wanted == 0 && chargeWarmCosts {
318
+ if availableGas < params .WarmStorageReadCostEIP2929 {
319
+ return availableGas
320
+ }
321
+ wanted = params .WarmStorageReadCostEIP2929
322
+ }
323
+ return wanted
282
324
}
283
325
284
326
// CodeHashGas adds the account's code hash to the accessed data, and returns the
285
327
// amount of gas that it costs.
286
328
// in write mode. If false, the charged gas corresponds to an access in read mode.
287
329
// Note that an access in write mode implies an access in read mode, whereas an access in
288
330
// read mode does not imply an access in write mode.
289
- func (ae * AccessEvents ) CodeHashGas (addr common.Address , isWrite bool ) uint64 {
290
- return ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , isWrite )
331
+ func (ae * AccessEvents ) CodeHashGas (addr common.Address , isWrite bool , availableGas uint64 , chargeWarmCosts bool ) uint64 {
332
+ _ , wanted := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , isWrite , availableGas )
333
+ if wanted == 0 && chargeWarmCosts {
334
+ if availableGas < params .WarmStorageReadCostEIP2929 {
335
+ return availableGas
336
+ }
337
+ wanted = params .WarmStorageReadCostEIP2929
338
+ }
339
+ return wanted
291
340
}
0 commit comments