@@ -26,7 +26,7 @@ import (
2626 "github.com/ethereum/go-ethereum/params"
2727)
2828
29- func makeGasSStoreFunc (clearingRefund uint64 ) gasFunc {
29+ func makeGasSStoreFunc (clearingRefund uint64 , warmLoadCosts uint64 ) gasFunc {
3030 return func (evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) (uint64 , error ) {
3131 // If we fail the minimum gas availability invariant, fail (0)
3232 if contract .Gas <= params .SstoreSentryGasEIP2200 {
@@ -50,7 +50,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
5050 if current == value { // noop (1)
5151 // EIP 2200 original clause:
5252 // return params.SloadGasEIP2200, nil
53- return cost + params . WarmStorageReadCostEIP2929 , nil // SLOAD_GAS
53+ return cost + warmLoadCosts , nil // SLOAD_GAS
5454 }
5555 if original == current {
5656 if original == (common.Hash {}) { // create slot (2.1.1)
@@ -74,19 +74,19 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
7474 if original == (common.Hash {}) { // reset to original inexistent slot (2.2.2.1)
7575 // EIP 2200 Original clause:
7676 //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
77- evm .StateDB .AddRefund (params .SstoreSetGasEIP2200 - params . WarmStorageReadCostEIP2929 )
77+ evm .StateDB .AddRefund (params .SstoreSetGasEIP2200 - warmLoadCosts )
7878 } else { // reset to original existing slot (2.2.2.2)
7979 // EIP 2200 Original clause:
8080 // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
8181 // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
8282 // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
8383 // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
84- evm .StateDB .AddRefund ((params .SstoreResetGasEIP2200 - params .ColdSloadCostEIP2929 ) - params . WarmStorageReadCostEIP2929 )
84+ evm .StateDB .AddRefund ((params .SstoreResetGasEIP2200 - params .ColdSloadCostEIP2929 ) - warmLoadCosts )
8585 }
8686 }
8787 // EIP-2200 original clause:
8888 //return params.SloadGasEIP2200, nil // dirty update (2.2)
89- return cost + params . WarmStorageReadCostEIP2929 , nil // dirty update (2.2)
89+ return cost + warmLoadCosts , nil // dirty update (2.2)
9090 }
9191}
9292
@@ -108,6 +108,24 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
108108 return params .WarmStorageReadCostEIP2929 , nil
109109}
110110
111+ // gasSLoadEIP7904 calculates dynamic gas for SLOAD according to EIP-7904
112+ // For SLOAD, if the (address, storage_key) pair (where address is the address of the contract
113+ // whose storage is being read) is not yet in accessed_storage_keys,
114+ // charge 2100 gas and add the pair to accessed_storage_keys.
115+ // If the pair is already in accessed_storage_keys, charge 5 gas.
116+ func gasSLoadEIP7904 (evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) (uint64 , error ) {
117+ loc := stack .peek ()
118+ slot := common .Hash (loc .Bytes32 ())
119+ // Check slot presence in the access list
120+ if _ , slotPresent := evm .StateDB .SlotInAccessList (contract .Address (), slot ); ! slotPresent {
121+ // If the caller cannot afford the cost, this change will be rolled back
122+ // If he does afford it, we can skip checking the same thing later on, during execution
123+ evm .StateDB .AddSlotToAccessList (contract .Address (), slot )
124+ return params .ColdSloadCostEIP2929 , nil
125+ }
126+ return params .WarmStorageReadCostEIP7904 , nil
127+ }
128+
111129// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
112130// EIP spec:
113131// > If the target is not in accessed_addresses,
@@ -152,6 +170,25 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
152170 return 0 , nil
153171}
154172
173+ // gasEip7904AccountCheck checks whether the first stack item (as address) is present in the access list.
174+ // If it is, this method returns '0', otherwise 'cold-warm' gas, presuming that the opcode using it
175+ // is also using 'warm' as constant factor.
176+ // This method is used by:
177+ // - extcodehash,
178+ // - extcodesize,
179+ // - (ext) balance
180+ func gasEip7904AccountCheck (evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) (uint64 , error ) {
181+ addr := common .Address (stack .peek ().Bytes20 ())
182+ // Check slot presence in the access list
183+ if ! evm .StateDB .AddressInAccessList (addr ) {
184+ // If the caller cannot afford the cost, this change will be rolled back
185+ evm .StateDB .AddAddressToAccessList (addr )
186+ // The warm storage read cost is already charged as constantGas
187+ return params .ColdAccountAccessCostEIP2929 - params .WarmStorageReadCostEIP7904 , nil
188+ }
189+ return 0 , nil
190+ }
191+
155192func makeCallVariantGasCallEIP2929 (oldCalculator gasFunc , addressPosition int ) gasFunc {
156193 return func (evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) (uint64 , error ) {
157194 addr := common .Address (stack .Back (addressPosition ).Bytes20 ())
@@ -191,11 +228,55 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
191228 }
192229}
193230
231+ func makeCallVariantGasCallEIP7904 (oldCalculator gasFunc , addressPosition int ) gasFunc {
232+ return func (evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) (uint64 , error ) {
233+ addr := common .Address (stack .Back (addressPosition ).Bytes20 ())
234+ // Check slot presence in the access list
235+ warmAccess := evm .StateDB .AddressInAccessList (addr )
236+ // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
237+ // the cost to charge for cold access, if any, is Cold - Warm
238+ coldCost := params .ColdAccountAccessCostEIP2929 - params .WarmStorageReadCostEIP7904
239+ if ! warmAccess {
240+ evm .StateDB .AddAddressToAccessList (addr )
241+ // Charge the remaining difference here already, to correctly calculate available
242+ // gas for call
243+ if ! contract .UseGas (coldCost , evm .Config .Tracer , tracing .GasChangeCallStorageColdAccess ) {
244+ return 0 , ErrOutOfGas
245+ }
246+ }
247+ // Now call the old calculator, which takes into account
248+ // - create new account
249+ // - transfer value
250+ // - memory expansion
251+ // - 63/64ths rule
252+ gas , err := oldCalculator (evm , contract , stack , mem , memorySize )
253+ if warmAccess || err != nil {
254+ return gas , err
255+ }
256+ // In case of a cold access, we temporarily add the cold charge back, and also
257+ // add it to the returned gas. By adding it to the return, it will be charged
258+ // outside of this function, as part of the dynamic gas, and that will make it
259+ // also become correctly reported to tracers.
260+ contract .Gas += coldCost
261+
262+ var overflow bool
263+ if gas , overflow = math .SafeAdd (gas , coldCost ); overflow {
264+ return 0 , ErrGasUintOverflow
265+ }
266+ return gas , nil
267+ }
268+ }
269+
194270var (
195271 gasCallEIP2929 = makeCallVariantGasCallEIP2929 (gasCall , 1 )
196272 gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929 (gasDelegateCall , 1 )
197273 gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929 (gasStaticCall , 1 )
198274 gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929 (gasCallCode , 1 )
275+
276+ gasCallEIP7904 = makeCallVariantGasCallEIP7904 (gasCall , 1 )
277+ gasDelegateCallEIP7904 = makeCallVariantGasCallEIP7904 (gasDelegateCall , 1 )
278+ gasStaticCallEIP7904 = makeCallVariantGasCallEIP7904 (gasStaticCall , 1 )
279+ gasCallCodeEIP7904 = makeCallVariantGasCallEIP7904 (gasCallCode , 1 )
199280 gasSelfdestructEIP2929 = makeSelfdestructGasFn (true )
200281 // gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
201282 gasSelfdestructEIP3529 = makeSelfdestructGasFn (false )
@@ -212,11 +293,15 @@ var (
212293 //
213294 //The other parameters defined in EIP 2200 are unchanged.
214295 // see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
215- gasSStoreEIP2929 = makeGasSStoreFunc (params .SstoreClearsScheduleRefundEIP2200 )
296+ gasSStoreEIP2929 = makeGasSStoreFunc (params .SstoreClearsScheduleRefundEIP2200 , params . WarmStorageReadCostEIP2929 )
216297
217298 // gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
218299 // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
219- gasSStoreEIP3529 = makeGasSStoreFunc (params .SstoreClearsScheduleRefundEIP3529 )
300+ gasSStoreEIP3529 = makeGasSStoreFunc (params .SstoreClearsScheduleRefundEIP3529 , params .WarmStorageReadCostEIP2929 )
301+
302+ // gasSStoreEIP7904 implements gas cost for SSTORE according to EIP-7904
303+ // Lowers `WARM_STORAGE_READ_COST`.
304+ gasSStoreEIP7904 = makeGasSStoreFunc (params .SstoreClearsScheduleRefundEIP3529 , params .WarmStorageReadCostEIP7904 )
220305)
221306
222307// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529
0 commit comments