From 839a2585472076acae2a023e7373eb0280871b8f Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:10:48 +0200 Subject: [PATCH] core/{state,vm}: FILL_COSTS using StateExists --- core/state/access_events.go | 73 +++++++++++++++++++++----------- core/state/access_events_test.go | 20 ++++----- core/state_transition.go | 2 +- core/vm/eips.go | 6 +-- core/vm/evm.go | 6 +-- core/vm/gas_table.go | 4 +- core/vm/interface.go | 2 + core/vm/interpreter.go | 2 +- core/vm/operations_verkle.go | 23 +++++----- 9 files changed, 83 insertions(+), 55 deletions(-) diff --git a/core/state/access_events.go b/core/state/access_events.go index c593f82027..170bea854a 100644 --- a/core/state/access_events.go +++ b/core/state/access_events.go @@ -44,6 +44,7 @@ var zeroTreeIndex uint256.Int type AccessEvents struct { branches map[branchAccessKey]mode chunks map[chunkAccessKey]mode + fills map[chunkAccessKey]struct{} pointCache *utils.PointCache } @@ -52,6 +53,7 @@ func NewAccessEvents(pointCache *utils.PointCache) *AccessEvents { return &AccessEvents{ branches: make(map[branchAccessKey]mode), chunks: make(map[chunkAccessKey]mode), + fills: make(map[chunkAccessKey]struct{}), pointCache: pointCache, } } @@ -66,6 +68,9 @@ func (ae *AccessEvents) Merge(other *AccessEvents) { for k, chunk := range other.chunks { ae.chunks[k] |= chunk } + for k := range other.fills { + ae.fills[k] = struct{}{} + } } // Keys returns, predictably, the list of keys that were touched during the @@ -85,17 +90,24 @@ func (ae *AccessEvents) Copy() *AccessEvents { cpy := &AccessEvents{ branches: maps.Clone(ae.branches), chunks: maps.Clone(ae.chunks), + fills: maps.Clone(ae.fills), pointCache: ae.pointCache, } return cpy } +// Reset resets all values of an access event, except its `fills` and point cache fields +func (ae *AccessEvents) Reset() { + ae.branches = make(map[branchAccessKey]mode) + ae.chunks = make(map[chunkAccessKey]mode) +} + // AddAccount returns the gas to be charged for each of the currently cold // member fields of an account. -func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 { +func (ae *AccessEvents) AddAccount(addr common.Address, isWrite, isFill bool) uint64 { var gas uint64 - gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite) - gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite) + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, isFill) + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, isFill) return gas } @@ -104,16 +116,18 @@ func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 { // call to that account. func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 { var gas uint64 - gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false) + gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false, false) return gas } // ValueTransferGas returns the gas to be charged for each of the currently // cold balance member fields of the caller and the callee accounts. -func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 { +func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address, isFill bool) uint64 { var gas uint64 - gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) - gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) + // TODO have a test to see what happens what happens when a tx from a non-existing + // account to another account (existing or not). + gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, false) + gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, isFill) return gas } @@ -121,34 +135,41 @@ func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) // a contract creation. func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 { var gas uint64 - gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true) + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, true) return gas } // AddTxOrigin adds the member fields of the sender account to the access event list, // so that cold accesses are not charged, since they are covered by the 21000 gas. func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) { - ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) - ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false) + ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, false) + ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, false) } // AddTxDestination adds the member fields of the sender account to the access event list, // so that cold accesses are not charged, since they are covered by the 21000 gas. func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) { - ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue) - ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false) + // For the balance, isFill is always set to true, as it will be covered by + // the intrinsic tx gas if needed. If the destination is written to during + // the contract execution, before the state db layer has saved it to disk, + // the fill cost will already have been covered by the intrinsic gas. + ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, true) + // This is set to false, because the hash leaf key isn't going to be written + // to by the contract execution, and the potential creation is covered by the + // intrinsic gas. + ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, false) } // SlotGas returns the amount of gas to be charged for a cold storage access. -func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool) uint64 { +func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite, isFill bool) uint64 { treeIndex, subIndex := utils.StorageIndex(slot.Bytes()) - return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite) + return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, isFill) } // touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold // access cost to be charged, if need be. -func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 { - stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite) +func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite, isFill bool) uint64 { + stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite, isFill) var gas uint64 if stemRead { @@ -170,7 +191,7 @@ func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex } // touchAddress adds any missing access event to the access event list. -func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) { +func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite, isFill bool) (bool, bool, bool, bool, bool) { branchKey := newBranchAccessKey(addr, treeIndex) chunkKey := newChunkAccessKey(branchKey, subIndex) @@ -198,7 +219,11 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, chunkWrite = true ae.chunks[chunkKey] |= AccessWitnessWriteFlag } - // TODO: charge chunk filling costs if the leaf was previously empty in the state + _, ok := ae.fills[chunkKey] + if isFill && !ok { + chunkFill = true + ae.fills[chunkKey] = struct{}{} + } } return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill } @@ -228,7 +253,7 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey { } // CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs -func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool) uint64 { +func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite, isFill bool) uint64 { // note that in the case where the copied code is outside the range of the // contract code but touches the last leaf with contract code in it, // we don't include the last leaf of code in the AccessWitness. The @@ -251,7 +276,7 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ { treeIndex := *uint256.NewInt((chunkNumber + 128) / 256) subIndex := byte((chunkNumber + 128) % 256) - gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite) + gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, isFill) var overflow bool statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas) if overflow { @@ -265,8 +290,8 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, // amount of gas that it costs. // Note that an access in write mode implies an access in read mode, whereas an // access in read mode does not imply an access in write mode. -func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 { - return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite) +func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite, isFill bool) uint64 { + return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, isFill) } // CodeHashGas adds the account's code hash to the accessed data, and returns the @@ -274,6 +299,6 @@ func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 { // in write mode. If false, the charged gas corresponds to an access in read mode. // Note that an access in write mode implies an access in read mode, whereas an access in // read mode does not imply an access in write mode. -func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 { - return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite) +func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite, isFill bool) uint64 { + return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, isFill) } diff --git a/core/state/access_events_test.go b/core/state/access_events_test.go index a35266950e..5c845ca3ae 100644 --- a/core/state/access_events_test.go +++ b/core/state/access_events_test.go @@ -40,50 +40,50 @@ func TestAccountHeaderGas(t *testing.T) { ae := NewAccessEvents(utils.NewPointCache(1024)) // Check cold read cost - gas := ae.BasicDataGas(testAddr, false) + gas := ae.BasicDataGas(testAddr, false, false) if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want { t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) } // Check warm read cost - gas = ae.BasicDataGas(testAddr, false) + gas = ae.BasicDataGas(testAddr, false, false) if gas != 0 { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } // Check cold read costs in the same group no longer incur the branch read cost - gas = ae.CodeHashGas(testAddr, false) + gas = ae.CodeHashGas(testAddr, false, false) if gas != params.WitnessChunkReadCost { t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost) } // Check cold write cost - gas = ae.BasicDataGas(testAddr, true) + gas = ae.BasicDataGas(testAddr, true, false) if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want { t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) } // Check warm write cost - gas = ae.BasicDataGas(testAddr, true) + gas = ae.BasicDataGas(testAddr, true, false) if gas != 0 { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } // Check a write without a read charges both read and write costs - gas = ae.BasicDataGas(testAddr2, true) + gas = ae.BasicDataGas(testAddr2, true, false) if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want { t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) } // Check that a write followed by a read charges nothing - gas = ae.BasicDataGas(testAddr2, false) + gas = ae.BasicDataGas(testAddr2, false, false) if gas != 0 { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } // Check that reading a slot from the account header only charges the // chunk read cost. - gas = ae.SlotGas(testAddr, common.Hash{}, false) + gas = ae.SlotGas(testAddr, common.Hash{}, false, false) if gas != params.WitnessChunkReadCost { t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost) } @@ -124,11 +124,11 @@ func TestMessageCallGas(t *testing.T) { } // Check that reading the basic data and code hash of the same account does not incur the branch read cost - gas = ae.BasicDataGas(testAddr, false) + gas = ae.BasicDataGas(testAddr, false, false) if gas != 0 { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } - gas = ae.CodeHashGas(testAddr, false) + gas = ae.CodeHashGas(testAddr, false, false) if gas != params.WitnessChunkReadCost { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } diff --git a/core/state_transition.go b/core/state_transition.go index 37807d3d2b..e2ac2264e7 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -470,7 +470,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { // add the coinbase to the witness iff the fee is greater than 0 if rules.IsEIP4762 && fee.Sign() != 0 { - st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true) + st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true, true) } } diff --git a/core/vm/eips.go b/core/vm/eips.go index edd6ec8d0a..e9faf62778 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -342,7 +342,7 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC self: AccountRef(addr), } paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) + statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, false) if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { scope.Contract.Gas = 0 return nil, ErrOutOfGas @@ -368,7 +368,7 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext // touch next chunk if PUSH1 is at the boundary. if so, *pc has // advanced past this boundary. contractAddr := scope.Contract.Address() - statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false) + statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, false) if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { scope.Contract.Gas = 0 return nil, ErrOutOfGas @@ -396,7 +396,7 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc { if !scope.Contract.IsDeployment { contractAddr := scope.Contract.Address() - statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false) + statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, false) if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { scope.Contract.Gas = 0 return nil, ErrOutOfGas diff --git a/core/vm/evm.go b/core/vm/evm.go index 7617d843c7..c1cc038311 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -209,7 +209,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if !evm.StateDB.Exist(addr) { if !isPrecompile && evm.chainRules.IsEIP4762 { // add proof of absence to witness - wgas := evm.AccessEvents.AddAccount(addr, false) + wgas := evm.AccessEvents.AddAccount(addr, false, false) if gas < wgas { evm.StateDB.RevertToSnapshot(snapshot) return nil, 0, ErrOutOfGas @@ -552,11 +552,11 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address, valu } } else { // Contract creation completed, touch the missing fields in the contract - if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) { + if !contract.UseGas(evm.AccessEvents.AddAccount(address, true, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) { return ret, ErrCodeStoreOutOfGas } - if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) { + if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) { return ret, ErrCodeStoreOutOfGas } } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index d294324b08..7c09f37845 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -396,7 +396,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize } if evm.chainRules.IsEIP4762 { if transfersValue { - gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) + gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address, !evm.StateDB.Exist(address))) if overflow { return 0, ErrGasUintOverflow } @@ -432,7 +432,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory address := common.Address(stack.Back(1).Bytes20()) transfersValue := !stack.Back(2).IsZero() if transfersValue { - gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) + gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address, !evm.StateDB.Exist(address))) if overflow { return 0, ErrGasUintOverflow } diff --git a/core/vm/interface.go b/core/vm/interface.go index 5f42643565..b4b39d20b6 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -90,6 +90,8 @@ type StateDB interface { AddPreimage(common.Hash, []byte) Witness() *stateless.Witness + + StateExists(common.Address, common.Hash) bool } // CallContext provides a basic interface for the EVM calling conventions. The EVM diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 6e7d28a0ce..85d1992891 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -229,7 +229,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // if the PC ends up in a new "chunk" of verkleized code, charge the // associated costs. contractAddr := contract.Address() - contract.Gas -= in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false) + contract.Gas -= in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, false) } // Get the operation from the jump table and validate the stack to ensure there are diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 722d5ed2ce..5e1bf2ce93 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -23,7 +23,8 @@ import ( ) func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true) + slotnr := stack.peek().Bytes32() + gas := evm.AccessEvents.SlotGas(contract.Address(), slotnr, true, !evm.StateDB.StateExists(contract.Address(), slotnr)) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -31,7 +32,7 @@ func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo } func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false) + gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, false) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -40,7 +41,7 @@ func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { address := stack.peek().Bytes20() - gas := evm.AccessEvents.BasicDataGas(address, false) + gas := evm.AccessEvents.BasicDataGas(address, false, false) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -52,7 +53,7 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil } - gas := evm.AccessEvents.BasicDataGas(address, false) + gas := evm.AccessEvents.BasicDataGas(address, false, false) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -64,7 +65,7 @@ func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil } - gas := evm.AccessEvents.CodeHashGas(address, false) + gas := evm.AccessEvents.CodeHashGas(address, false, false) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -101,15 +102,15 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem return 0, nil } contractAddr := contract.Address() - statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false) + statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false, false) if contractAddr != beneficiaryAddr { - statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, false) + statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, false) } // Charge write costs if it transfers value if evm.StateDB.GetBalance(contractAddr).Sign() != 0 { - statelessGas += evm.AccessEvents.BasicDataGas(contractAddr, true) + statelessGas += evm.AccessEvents.BasicDataGas(contractAddr, true, false) if contractAddr != beneficiaryAddr { - statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, true) + statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, !evm.StateDB.Exist(beneficiaryAddr)) } } return statelessGas, nil @@ -130,7 +131,7 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, } _, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64()) if !contract.IsDeployment { - gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) + gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, false) } return gas, nil } @@ -142,7 +143,7 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo return 0, err } addr := common.Address(stack.peek().Bytes20()) - wgas := evm.AccessEvents.BasicDataGas(addr, false) + wgas := evm.AccessEvents.BasicDataGas(addr, false, false) if wgas == 0 { wgas = params.WarmStorageReadCostEIP2929 }