1717package state
1818
1919import (
20+ "errors"
21+
2022 "github.com/ethereum/go-ethereum/common"
2123 "github.com/ethereum/go-ethereum/common/math"
2224 "github.com/ethereum/go-ethereum/params"
@@ -89,83 +91,152 @@ func (aw *AccessWitness) Copy() *AccessWitness {
8991 return naw
9092}
9193
92- func (aw * AccessWitness ) TouchFullAccount (addr []byte , isWrite bool , availableGas uint64 ) uint64 {
93- var gas uint64
94+ func (aw * AccessWitness ) FullAccountGas (addr []byte , isWrite bool ) uint64 {
95+ return aw .calculateWitnessGas (addr , zeroTreeIndex , isWrite , utils .BasicDataLeafKey , utils .CodeHashLeafKey )
96+ }
97+
98+ func (aw * AccessWitness ) TouchFullAccount (addr []byte , isWrite bool ) {
9499 for i := utils .BasicDataLeafKey ; i <= utils .CodeHashLeafKey ; i ++ {
95- consumed , wanted := aw .touchAddressAndChargeGas (addr , zeroTreeIndex , byte (i ), isWrite , availableGas )
96- if consumed < wanted {
97- return wanted + gas
98- }
99- availableGas -= consumed
100- gas += consumed
100+ aw .touchLocation (addr , zeroTreeIndex , byte (i ), isWrite )
101101 }
102- return gas
103102}
104103
105- func (aw * AccessWitness ) TouchAndChargeMessageCall (addr []byte , availableGas uint64 ) uint64 {
106- _ , wanted := aw .touchAddressAndChargeGas (addr , zeroTreeIndex , utils . BasicDataLeafKey , false , availableGas )
104+ func (aw * AccessWitness ) MessageCallGas (addr []byte ) uint64 {
105+ wanted := aw .calculateWitnessGas (addr , zeroTreeIndex , false , utils . BasicDataLeafKey )
107106 if wanted == 0 {
108107 wanted = params .WarmStorageReadCostEIP2929
109108 }
110109 return wanted
111110}
112111
113- func (aw * AccessWitness ) TouchAndChargeValueTransfer (callerAddr , targetAddr []byte , availableGas uint64 ) uint64 {
114- _ , wanted1 := aw .touchAddressAndChargeGas (callerAddr , zeroTreeIndex , utils .BasicDataLeafKey , true , availableGas )
115- if wanted1 > availableGas {
116- return wanted1
117- }
118- _ , wanted2 := aw .touchAddressAndChargeGas (targetAddr , zeroTreeIndex , utils .BasicDataLeafKey , true , availableGas - wanted1 )
112+ func (aw * AccessWitness ) TouchMessageCall (addr []byte ) {
113+ aw .touchLocation (addr , zeroTreeIndex , utils .BasicDataLeafKey , false )
114+ }
115+
116+ func (aw * AccessWitness ) TouchValueTransfer (callerAddr , targetAddr []byte ) {
117+ aw .touchLocation (callerAddr , zeroTreeIndex , utils .BasicDataLeafKey , true )
118+ aw .touchLocation (targetAddr , zeroTreeIndex , utils .BasicDataLeafKey , true )
119+ }
120+
121+ func (aw * AccessWitness ) ValueTransferGas (callerAddr , targetAddr []byte ) uint64 {
122+ wanted1 := aw .calculateWitnessGas (callerAddr , zeroTreeIndex , true , utils .BasicDataLeafKey )
123+ wanted2 := aw .calculateWitnessGas (targetAddr , zeroTreeIndex , true , utils .BasicDataLeafKey )
119124 if wanted1 + wanted2 == 0 {
120125 return params .WarmStorageReadCostEIP2929
121126 }
122127 return wanted1 + wanted2
123128}
124129
130+ // ContractCreateCheckGas charges access costs before
131+ // a contract creation is initiated. It is just reads, because the
132+ // address collision is done before the transfer, and so no write
133+ // are guaranteed to happen at this point.
134+ func (aw * AccessWitness ) ContractCreateCheckGas (addr []byte ) uint64 {
135+ return aw .calculateWitnessGas (addr , zeroTreeIndex , false , utils .BasicDataLeafKey , utils .CodeHashLeafKey )
136+ }
137+
125138// TouchAndChargeContractCreateCheck charges access costs before
126139// a contract creation is initiated. It is just reads, because the
127140// address collision is done before the transfer, and so no write
128141// are guaranteed to happen at this point.
129- func (aw * AccessWitness ) TouchAndChargeContractCreateCheck (addr []byte , availableGas uint64 ) uint64 {
130- gas1 , wanted1 := aw .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , false , availableGas )
131- _ , wanted2 := aw .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , false , availableGas - gas1 )
132- return wanted1 + wanted2
142+ func (aw * AccessWitness ) TouchContractCreateCheck (addr []byte ) {
143+ aw .touchLocation (addr , zeroTreeIndex , utils .BasicDataLeafKey , false )
144+ aw .touchLocation (addr , zeroTreeIndex , utils .CodeHashLeafKey , false )
145+ }
146+
147+ // ContractCreateInitGas charges access costs to initiate a contract creation.
148+ func (aw * AccessWitness ) ContractCreateInitGas (addr []byte ) uint64 {
149+ return aw .calculateWitnessGas (addr , zeroTreeIndex , true , utils .BasicDataLeafKey , utils .CodeHashLeafKey )
133150}
134151
135152// TouchAndChargeContractCreateInit charges access costs to initiate
136153// a contract creation.
137- func (aw * AccessWitness ) TouchAndChargeContractCreateInit (addr []byte , availableGas uint64 ) (uint64 , uint64 ) {
138- gas1 , wanted1 := aw .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , true , availableGas )
139- gas2 , wanted2 := aw .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , true , availableGas - gas1 )
140- return gas1 + gas2 , wanted1 + wanted2
154+ func (aw * AccessWitness ) TouchContractCreateInit (addr []byte , availableGas uint64 ) {
155+ aw .touchLocation (addr , zeroTreeIndex , utils .BasicDataLeafKey , true )
156+ aw .touchLocation (addr , zeroTreeIndex , utils .CodeHashLeafKey , true )
141157}
142158
143159func (aw * AccessWitness ) TouchTxOriginAndComputeGas (originAddr []byte ) {
144160 for i := utils .BasicDataLeafKey ; i <= utils .CodeHashLeafKey ; i ++ {
145- aw .touchAddressAndChargeGas (originAddr , zeroTreeIndex , byte (i ), i == utils .BasicDataLeafKey , math . MaxUint64 )
161+ aw .touchLocation (originAddr , zeroTreeIndex , byte (i ), i == utils .BasicDataLeafKey )
146162 }
147163}
148164
149165func (aw * AccessWitness ) TouchTxTarget (targetAddr []byte , sendsValue , doesntExist bool ) {
150- aw .touchAddressAndChargeGas (targetAddr , zeroTreeIndex , utils .BasicDataLeafKey , sendsValue , math . MaxUint64 )
166+ aw .touchLocation (targetAddr , zeroTreeIndex , utils .BasicDataLeafKey , sendsValue )
151167 // Note that we do a write-event in CodeHash without distinguishing if the tx target account
152168 // exists or not. Pre-7702, there's no situation in which an existing codeHash can be mutated, thus
153169 // doing a write-event shouldn't cause an observable difference in gas usage.
154170 // TODO(7702): re-check this in the spec and implementation to be sure is a correct solution after
155171 // EIP-7702 is implemented.
156- aw .touchAddressAndChargeGas (targetAddr , zeroTreeIndex , utils .CodeHashLeafKey , doesntExist , math . MaxUint64 )
172+ aw .touchLocation (targetAddr , zeroTreeIndex , utils .CodeHashLeafKey , doesntExist )
157173}
158174
159- func (aw * AccessWitness ) TouchSlotAndChargeGas (addr []byte , slot common.Hash , isWrite bool , availableGas uint64 , warmCostCharging bool ) uint64 {
175+ func (aw * AccessWitness ) SlotGas (addr []byte , slot common.Hash , isWrite bool ) uint64 {
160176 treeIndex , subIndex := utils .GetTreeKeyStorageSlotTreeIndexes (slot .Bytes ())
161- _ , wanted := aw .touchAddressAndChargeGas (addr , * treeIndex , subIndex , isWrite , availableGas )
162- if wanted == 0 && warmCostCharging {
177+ wanted := aw .calculateWitnessGas (addr , * treeIndex , isWrite , subIndex )
178+ if wanted == 0 {
163179 wanted = params .WarmStorageReadCostEIP2929
164180 }
165181 return wanted
166182}
167183
168- func (aw * AccessWitness ) touchAddressAndChargeGas (addr []byte , treeIndex uint256.Int , subIndex byte , isWrite bool , availableGas uint64 ) (uint64 , uint64 ) {
184+ func (aw * AccessWitness ) TouchSlot (addr []byte , slot common.Hash , isWrite bool ) {
185+ treeIndex , subIndex := utils .GetTreeKeyStorageSlotTreeIndexes (slot .Bytes ())
186+ aw .touchLocation (addr , * treeIndex , subIndex , isWrite )
187+ }
188+
189+ func (aw * AccessWitness ) calculateWitnessGas (addr []byte , treeIndex uint256.Int , isWrite bool , subIndices ... byte ) uint64 {
190+ var (
191+ gas uint64
192+ branchKey = newBranchAccessKey (addr , treeIndex )
193+ branchRead , branchWrite bool
194+ )
195+
196+ // Read access.
197+ if _ , hasStem := aw .branches [branchKey ]; ! hasStem {
198+ branchRead = true
199+ }
200+
201+ // Write access.
202+ if isWrite {
203+ if (aw .branches [branchKey ] & AccessWitnessWriteFlag ) == 0 {
204+ branchWrite = true
205+ }
206+ }
207+
208+ if branchRead {
209+ gas += params .WitnessBranchReadCost
210+ }
211+ if branchWrite {
212+ gas += params .WitnessBranchWriteCost
213+ }
214+
215+ for _ , subIndex := range subIndices {
216+ var chunkRead , chunkWrite , chunkFill bool
217+ chunkKey := newChunkAccessKey (branchKey , subIndex )
218+ if _ , hasSelector := aw .chunks [chunkKey ]; ! hasSelector {
219+ chunkRead = true
220+
221+ }
222+ if isWrite && (aw .chunks [chunkKey ]& AccessWitnessWriteFlag ) == 0 {
223+ chunkWrite = true
224+ }
225+ if chunkRead {
226+ gas += params .WitnessChunkReadCost
227+ }
228+ if chunkWrite {
229+ gas += params .WitnessChunkWriteCost
230+ }
231+ if chunkFill {
232+ gas += params .WitnessChunkFillCost
233+ }
234+ }
235+
236+ return gas
237+ }
238+
239+ func (aw * AccessWitness ) touchLocation (addr []byte , treeIndex uint256.Int , subIndex byte , isWrite bool ) {
169240 branchKey := newBranchAccessKey (addr , treeIndex )
170241 chunkKey := newChunkAccessKey (branchKey , subIndex )
171242
@@ -191,28 +262,6 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256
191262 }
192263 }
193264
194- var gas uint64
195- if branchRead {
196- gas += params .WitnessBranchReadCost
197- }
198- if chunkRead {
199- gas += params .WitnessChunkReadCost
200- }
201- if branchWrite {
202- gas += params .WitnessBranchWriteCost
203- }
204- if chunkWrite {
205- gas += params .WitnessChunkWriteCost
206- }
207- if chunkFill {
208- gas += params .WitnessChunkFillCost
209- }
210-
211- if availableGas < gas {
212- // consumed != wanted
213- return availableGas , gas
214- }
215-
216265 if branchRead {
217266 aw .branches [branchKey ] = AccessWitnessReadFlag
218267 }
@@ -224,10 +273,11 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256
224273 }
225274 if chunkWrite {
226275 aw .chunks [chunkKey ] |= AccessWitnessWriteFlag
227- }
228276
229- // consumed == wanted
230- return gas , gas
277+ if chunkFill {
278+ // TODO when FILL_COST is implemented
279+ }
280+ }
231281}
232282
233283type branchAccessKey struct {
@@ -254,16 +304,16 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
254304 return lk
255305}
256306
257- // touchCodeChunksRangeOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
258- func (aw * AccessWitness ) TouchCodeChunksRangeAndChargeGas (contractAddr []byte , startPC , size uint64 , codeLen uint64 , isWrite bool , availableGas uint64 ) (uint64 , uint64 ) {
307+ // CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
308+ func (aw * AccessWitness ) CodeChunksRangeGas (contractAddr []byte , startPC , size uint64 , codeLen uint64 , isWrite bool ) (uint64 , error ) {
259309 // note that in the case where the copied code is outside the range of the
260310 // contract code but touches the last leaf with contract code in it,
261311 // we don't include the last leaf of code in the AccessWitness. The
262312 // reason that we do not need the last leaf is the account's code size
263313 // is already in the AccessWitness so a stateless verifier can see that
264314 // the code from the last leaf is not needed.
265315 if size == 0 || startPC >= codeLen {
266- return 0 , 0
316+ return 0 , nil
267317 }
268318
269319 endPC := startPC + size
@@ -278,39 +328,63 @@ func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, s
278328 for chunkNumber := startPC / 31 ; chunkNumber <= endPC / 31 ; chunkNumber ++ {
279329 treeIndex := * uint256 .NewInt ((chunkNumber + 128 ) / 256 )
280330 subIndex := byte ((chunkNumber + 128 ) % 256 )
281- consumed , wanted := aw .touchAddressAndChargeGas (contractAddr , treeIndex , subIndex , isWrite , availableGas )
282- // did we OOG ?
283- if wanted > consumed {
284- return statelessGasCharged + consumed , statelessGasCharged + wanted
285- }
331+ wanted := aw .calculateWitnessGas (contractAddr , treeIndex , isWrite , subIndex )
286332 var overflow bool
287- statelessGasCharged , overflow = math .SafeAdd (statelessGasCharged , consumed )
333+ statelessGasCharged , overflow = math .SafeAdd (statelessGasCharged , wanted )
288334 if overflow {
289- panic ( "overflow when adding gas " )
335+ return 0 , errors . New ( "gas uint overflow " )
290336 }
291- availableGas -= consumed
292337 }
293338
294- return statelessGasCharged , statelessGasCharged
339+ return statelessGasCharged , nil
295340}
296341
297- func (aw * AccessWitness ) TouchBasicData (addr []byte , isWrite bool , availableGas uint64 , warmCostCharging bool ) uint64 {
298- _ , wanted := aw .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .BasicDataLeafKey , isWrite , availableGas )
342+ // TouchCodeChunksRange is a helper function to touch every chunk in a code range and charge witness gas costs
343+ func (aw * AccessWitness ) TouchCodeChunksRange (contractAddr []byte , startPC , size uint64 , codeLen uint64 , isWrite bool ) {
344+ // note that in the case where the copied code is outside the range of the
345+ // contract code but touches the last leaf with contract code in it,
346+ // we don't include the last leaf of code in the AccessWitness. The
347+ // reason that we do not need the last leaf is the account's code size
348+ // is already in the AccessWitness so a stateless verifier can see that
349+ // the code from the last leaf is not needed.
350+ if size == 0 || startPC >= codeLen {
351+ return
352+ }
353+
354+ endPC := startPC + size
355+ if endPC > codeLen {
356+ endPC = codeLen
357+ }
358+ if endPC > 0 {
359+ endPC -= 1 // endPC is the last bytecode that will be touched.
360+ }
361+
362+ for chunkNumber := startPC / 31 ; chunkNumber <= endPC / 31 ; chunkNumber ++ {
363+ treeIndex := * uint256 .NewInt ((chunkNumber + 128 ) / 256 )
364+ subIndex := byte ((chunkNumber + 128 ) % 256 )
365+ aw .touchLocation (contractAddr , treeIndex , subIndex , isWrite )
366+ }
367+ }
368+
369+ func (aw * AccessWitness ) TouchBasicData (addr []byte , isWrite bool ) {
370+ aw .touchLocation (addr , zeroTreeIndex , utils .BasicDataLeafKey , isWrite )
371+ }
372+
373+ func (aw * AccessWitness ) BasicDataGas (addr []byte , isWrite bool , warmCostCharging bool ) uint64 {
374+ wanted := aw .calculateWitnessGas (addr , zeroTreeIndex , isWrite , utils .BasicDataLeafKey )
299375 if wanted == 0 && warmCostCharging {
300- if availableGas < params .WarmStorageReadCostEIP2929 {
301- return availableGas
302- }
303376 wanted = params .WarmStorageReadCostEIP2929
304377 }
305378 return wanted
306379}
307380
308- func (aw * AccessWitness ) TouchCodeHash (addr []byte , isWrite bool , availableGas uint64 , chargeWarmCosts bool ) uint64 {
309- _ , wanted := aw .touchAddressAndChargeGas (addr , zeroTreeIndex , utils .CodeHashLeafKey , isWrite , availableGas )
381+ func (aw * AccessWitness ) TouchCodeHash (addr []byte , isWrite bool ) {
382+ aw .touchLocation (addr , zeroTreeIndex , utils .CodeHashLeafKey , isWrite )
383+ }
384+
385+ func (aw * AccessWitness ) CodeHashGas (addr []byte , isWrite bool , chargeWarmCosts bool ) uint64 {
386+ wanted := aw .calculateWitnessGas (addr , zeroTreeIndex , isWrite , utils .CodeHashLeafKey )
310387 if wanted == 0 && chargeWarmCosts {
311- if availableGas < params .WarmStorageReadCostEIP2929 {
312- return availableGas
313- }
314388 wanted = params .WarmStorageReadCostEIP2929
315389 }
316390 return wanted
0 commit comments