@@ -18,6 +18,7 @@ package state
1818
1919import (
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 , expected := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , isWrite , availableGas )
99+ if consumed < expected {
100+ return expected
101+ }
102+ gas += consumed
103+ consumed , expected = ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , isWrite , availableGas - consumed )
104+ if consumed < expected {
105+ return expected + gas
106+ }
107+ gas += expected
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+ _ , expected := ae .touchAddressAndChargeGas (destination , zeroTreeIndex , utils .BasicDataLeafKey , false , availableGas )
116+ if expected == 0 {
117+ expected = params .WarmStorageReadCostEIP2929
118+ }
119+ return expected
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+ _ , expected1 := ae .touchAddressAndChargeGas (callerAddr , zeroTreeIndex , utils .BasicDataLeafKey , true , availableGas )
126+ if expected1 > availableGas {
127+ return expected1
128+ }
129+ _ , expected2 := ae .touchAddressAndChargeGas (targetAddr , zeroTreeIndex , utils .BasicDataLeafKey , true , availableGas - expected1 )
130+ if expected1 + expected2 == 0 {
131+ return params .WarmStorageReadCostEIP2929
132+ }
133+ return expected1 + expected2
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 , expected1 := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , false , availableGas )
142+ _ , expected2 := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , false , availableGas - consumed )
143+ return expected1 + expected2
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 , expected1 := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , true , availableGas )
151+ gas += consumed
152+ consumed , expected2 := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , true , availableGas - consumed )
153+ gas += consumed
154+ return gas , expected1 + expected2
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.
142159func (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 )
158- }
159-
160- // touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold
161- // 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
174+ _ , expected := ae .touchAddressAndChargeGas (addr , * treeIndex , subIndex , isWrite , availableGas )
175+ if expected == 0 && chargeWarmCosts {
176+ expected = params .WarmStorageReadCostEIP2929
177177 }
178- if selectorFill {
179- gas += params .WitnessChunkFillCost
180- }
181- return gas
178+ return expected
182179}
183180
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 ) {
181+ // touchAddressAndChargeGas adds any missing access event to the access event list, and returns the
182+ // consumed and required gas.
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 != expected
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 == expected
245+ return gas , gas
216246}
217247
218248type 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 , expected := ae .touchAddressAndChargeGas (contractAddr , treeIndex , subIndex , isWrite , availableGas )
297+ // did we OOG ?
298+ if expected > consumed {
299+ return statelessGasCharged + consumed , statelessGasCharged + expected
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+ _ , expected := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , isWrite , availableGas )
317+ if expected == 0 && chargeWarmCosts {
318+ if availableGas < params .WarmStorageReadCostEIP2929 {
319+ return availableGas
320+ }
321+ expected = params .WarmStorageReadCostEIP2929
322+ }
323+ return expected
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+ _ , expected := ae .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , isWrite , availableGas )
333+ if expected == 0 && chargeWarmCosts {
334+ if availableGas < params .WarmStorageReadCostEIP2929 {
335+ return availableGas
336+ }
337+ expected = params .WarmStorageReadCostEIP2929
338+ }
339+ return expected
291340}
0 commit comments