Skip to content

Commit 13a09a4

Browse files
committed
core/{state, vm}: update stateless gas costs to follow the kaustinen7 testnet
Co-authored-by: Ignacio Hagopian <[email protected]> Signed-off-by: Guillaume Ballet <[email protected]>
1 parent 9c970d8 commit 13a09a4

10 files changed

+274
-201
lines changed

core/state/access_events.go

+117-68
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package state
1818

1919
import (
2020
"maps"
21+
gomath "math"
2122

2223
"github.com/ethereum/go-ethereum/common"
2324
"github.com/ethereum/go-ethereum/common/math"
@@ -92,127 +93,156 @@ func (ae *AccessEvents) Copy() *AccessEvents {
9293

9394
// AddAccount returns the gas to be charged for each of the currently cold
9495
// 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
99108
return gas
100109
}
101110

102111
// MessageCallGas returns the gas to be charged for each of the currently
103112
// cold member fields of an account, that need to be touched when making a message
104113
// 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
109120
}
110121

111122
// ValueTransferGas returns the gas to be charged for each of the currently
112123
// 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
118134
}
119135

120136
// ContractCreatePreCheckGas charges access costs before
121137
// a contract creation is initiated. It is just reads, because the
122138
// address collision is done before the transfer, and so no write
123139
// 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
129144
}
130145

131146
// ContractCreateInitGas returns the access gas costs for the initialization of
132147
// a contract creation.
133-
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address) uint64 {
148+
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, availableGas uint64) (uint64, uint64) {
134149
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
138155
}
139156

140157
// AddTxOrigin adds the member fields of the sender account to the access event list,
141158
// so that cold accesses are not charged, since they are covered by the 21000 gas.
142159
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)
145162
}
146163

147164
// AddTxDestination adds the member fields of the sender account to the access event list,
148165
// 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)
152169
}
153170

154171
// 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 {
156173
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
158179
}
159180

160181
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold
161182
// 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) {
186184
branchKey := newBranchAccessKey(addr, treeIndex)
187185
chunkKey := newChunkAccessKey(branchKey, subIndex)
188186

189187
// Read access.
190188
var branchRead, chunkRead bool
191189
if _, hasStem := ae.branches[branchKey]; !hasStem {
192190
branchRead = true
193-
ae.branches[branchKey] = AccessWitnessReadFlag
194191
}
195192
if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
196193
chunkRead = true
197-
ae.chunks[chunkKey] = AccessWitnessReadFlag
198194
}
199195

200196
// Write access.
201197
var branchWrite, chunkWrite, chunkFill bool
202198
if isWrite {
203199
if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
204200
branchWrite = true
205-
ae.branches[branchKey] |= AccessWitnessWriteFlag
206201
}
207202

208203
chunkValue := ae.chunks[chunkKey]
209204
if (chunkValue & AccessWitnessWriteFlag) == 0 {
210205
chunkWrite = true
211-
ae.chunks[chunkKey] |= AccessWitnessWriteFlag
212206
}
213-
// TODO: charge chunk filling costs if the leaf was previously empty in the state
214207
}
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
216246
}
217247

218248
type branchAccessKey struct {
@@ -240,15 +270,15 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
240270
}
241271

242272
// 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) {
244274
// note that in the case where the copied code is outside the range of the
245275
// contract code but touches the last leaf with contract code in it,
246276
// we don't include the last leaf of code in the AccessWitness. The
247277
// reason that we do not need the last leaf is the account's code size
248278
// is already in the AccessWitness so a stateless verifier can see that
249279
// the code from the last leaf is not needed.
250280
if (codeLen == 0 && size == 0) || startPC > codeLen {
251-
return 0
281+
return 0, 0
252282
}
253283

254284
endPC := startPC + size
@@ -263,29 +293,48 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
263293
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
264294
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
265295
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+
}
267301
var overflow bool
268-
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
302+
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, consumed)
269303
if overflow {
270304
panic("overflow when adding gas")
271305
}
306+
availableGas -= consumed
272307
}
273-
return statelessGasCharged
308+
return statelessGasCharged, statelessGasCharged
274309
}
275310

276311
// BasicDataGas adds the account's basic data to the accessed data, and returns the
277312
// amount of gas that it costs.
278313
// Note that an access in write mode implies an access in read mode, whereas an
279314
// 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
282324
}
283325

284326
// CodeHashGas adds the account's code hash to the accessed data, and returns the
285327
// amount of gas that it costs.
286328
// in write mode. If false, the charged gas corresponds to an access in read mode.
287329
// Note that an access in write mode implies an access in read mode, whereas an access in
288330
// 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
291340
}

0 commit comments

Comments
 (0)