diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 2fb49139d71f..3e8e9cb492b1 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -343,7 +343,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, // Amount is in gwei, turn into wei amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei)) statedb.AddBalance(w.Address, amount) - statedb.Witness().TouchFullAccount(w.Address[:], true, math.MaxUint64) + statedb.Witness().TouchFullAccount(w.Address[:], true, true, math.MaxUint64) } if chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) { if err := overlay.OverlayVerkleTransition(statedb, common.Hash{}, chainConfig.OverlayStride); err != nil { diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index c7bfd1e55b09..0f383023aa3e 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -359,7 +359,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. state.AddBalance(w.Address, amount) // The returned gas is not charged - state.Witness().TouchFullAccount(w.Address[:], true, math.MaxUint64) + state.Witness().TouchFullAccount(w.Address[:], true, true, math.MaxUint64) } if chain.Config().IsVerkle(header.Number, header.Time) { diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 940a507b0e4e..4efee7f4ece3 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -17,6 +17,8 @@ package state import ( + "maps" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" @@ -42,14 +44,16 @@ var zeroTreeIndex uint256.Int type AccessWitness struct { branches map[branchAccessKey]mode chunks map[chunkAccessKey]mode + fills map[chunkAccessKey]struct{} pointCache *utils.PointCache } -func NewAccessWitness(pointCache *utils.PointCache) *AccessWitness { +func NewAccessWitness(pointCache *utils.PointCache, fills map[chunkAccessKey]struct{}) *AccessWitness { return &AccessWitness{ branches: make(map[branchAccessKey]mode), chunks: make(map[chunkAccessKey]mode), + fills: fills, pointCache: pointCache, } } @@ -64,6 +68,9 @@ func (aw *AccessWitness) Merge(other *AccessWitness) { for k, chunk := range other.chunks { aw.chunks[k] |= chunk } + for k := range other.fills { + aw.fills[k] = struct{}{} + } } // Key returns, predictably, the list of keys that were touched during the @@ -81,18 +88,18 @@ func (aw *AccessWitness) Keys() [][]byte { func (aw *AccessWitness) Copy() *AccessWitness { naw := &AccessWitness{ - branches: make(map[branchAccessKey]mode), - chunks: make(map[chunkAccessKey]mode), + branches: maps.Clone(aw.branches), + chunks: maps.Clone(aw.chunks), + fills: maps.Clone(aw.fills), pointCache: aw.pointCache, } - naw.Merge(aw) return naw } -func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool, availableGas uint64) uint64 { +func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite, isFill bool, availableGas uint64) uint64 { var gas uint64 for i := utils.BasicDataLeafKey; i <= utils.CodeHashLeafKey; i++ { - consumed, wanted := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), isWrite, availableGas) + consumed, wanted := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), isWrite, isFill, availableGas) if consumed < wanted { return wanted + gas } @@ -103,19 +110,23 @@ func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool, availableGa } func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, availableGas uint64) uint64 { - _, wanted := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas) + _, wanted := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, false, availableGas) if wanted == 0 { wanted = params.WarmStorageReadCostEIP2929 } return wanted } -func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte, availableGas uint64) uint64 { - _, wanted1 := aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas) +func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte, isFill bool, availableGas uint64) uint64 { + _, wanted1 := aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, false, availableGas) if wanted1 > availableGas { return wanted1 } - _, wanted2 := aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas-wanted1) + // The isFill here is a bit strange: one would have to pay for the fill costs + // and potentially run out of gas before the account is created. This is due + // to the charging for a potential account creation being split in two locations: + // the basic data is written in the gas function, and the code hash in opCall. + _, wanted2 := aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, isFill, availableGas-wanted1) if wanted1+wanted2 == 0 { return params.WarmStorageReadCostEIP2929 } @@ -127,45 +138,45 @@ func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []by // address collision is done before the transfer, and so no write // are guaranteed to happen at this point. func (aw *AccessWitness) TouchAndChargeContractCreateCheck(addr []byte, availableGas uint64) uint64 { - gas1, wanted1 := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas) - _, wanted2 := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, availableGas-gas1) + gas1, wanted1 := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, false, availableGas) + _, wanted2 := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, false, availableGas-gas1) return wanted1 + wanted2 } // TouchAndChargeContractCreateInit charges access costs to initiate // a contract creation. -func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, availableGas uint64) (uint64, uint64) { - gas1, wanted1 := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas) - gas2, wanted2 := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true, availableGas-gas1) +func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, availableGas uint64, isFill bool) (uint64, uint64) { + gas1, wanted1 := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, isFill, availableGas) + gas2, wanted2 := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true, isFill, availableGas-gas1) return gas1 + gas2, wanted1 + wanted2 } func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) { for i := utils.BasicDataLeafKey; i <= utils.CodeHashLeafKey; i++ { - aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BasicDataLeafKey, math.MaxUint64) + aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BasicDataLeafKey, false, math.MaxUint64) } } func (aw *AccessWitness) TouchTxTarget(targetAddr []byte, sendsValue, doesntExist bool) { - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, math.MaxUint64) + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, doesntExist, math.MaxUint64) // Note that we do a write-event in CodeHash without distinguishing if the tx target account // exists or not. Pre-7702, there's no situation in which an existing codeHash can be mutated, thus // doing a write-event shouldn't cause an observable difference in gas usage. // TODO(7702): re-check this in the spec and implementation to be sure is a correct solution after // EIP-7702 is implemented. - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, doesntExist, math.MaxUint64) + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, doesntExist, doesntExist, math.MaxUint64) } -func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool, availableGas uint64, warmCostCharging bool) uint64 { +func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite, isFill bool, availableGas uint64, warmCostCharging bool) uint64 { treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slot.Bytes()) - _, wanted := aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, availableGas) + _, wanted := aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, isFill, availableGas) if wanted == 0 && warmCostCharging { wanted = params.WarmStorageReadCostEIP2929 } return wanted } -func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool, availableGas uint64) (uint64, uint64) { +func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite, isFill bool, availableGas uint64) (uint64, uint64) { branchKey := newBranchAccessKey(addr, treeIndex) chunkKey := newChunkAccessKey(branchKey, subIndex) @@ -189,6 +200,11 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256 if (chunkValue & AccessWitnessWriteFlag) == 0 { chunkWrite = true } + + _, ok := aw.fills[chunkKey] + if isFill && !ok { + chunkFill = true + } } var gas uint64 @@ -225,6 +241,9 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256 if chunkWrite { aw.chunks[chunkKey] |= AccessWitnessWriteFlag } + if chunkFill { + aw.fills[chunkKey] = struct{}{} + } // consumed == wanted return gas, gas @@ -255,7 +274,7 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey { } // touchCodeChunksRangeOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs -func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64, isWrite bool, availableGas uint64) (uint64, uint64) { +func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64, isWrite, isFill bool, availableGas uint64) (uint64, 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 @@ -278,7 +297,7 @@ func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, s for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ { treeIndex := *uint256.NewInt((chunkNumber + 128) / 256) subIndex := byte((chunkNumber + 128) % 256) - consumed, wanted := aw.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, availableGas) + consumed, wanted := aw.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, isFill, availableGas) // did we OOG ? if wanted > consumed { return statelessGasCharged + consumed, statelessGasCharged + wanted @@ -294,8 +313,8 @@ func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, s return statelessGasCharged, statelessGasCharged } -func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool, availableGas uint64, warmCostCharging bool) uint64 { - _, wanted := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas) +func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite, isFill bool, availableGas uint64, warmCostCharging bool) uint64 { + _, wanted := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, isFill, availableGas) if wanted == 0 && warmCostCharging { if availableGas < params.WarmStorageReadCostEIP2929 { return availableGas @@ -306,7 +325,7 @@ func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool, availableGas } func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 { - _, wanted := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, availableGas) + _, wanted := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, false, availableGas) if wanted == 0 && chargeWarmCosts { if availableGas < params.WarmStorageReadCostEIP2929 { return availableGas diff --git a/core/state/statedb.go b/core/state/statedb.go index 3b85508e39dc..f0dd75324c76 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -187,7 +187,7 @@ func (s *StateDB) Snaps() *snapshot.Tree { } func (s *StateDB) NewAccessWitness() *AccessWitness { - return NewAccessWitness(s.db.(*cachingDB).addrToPoint) + return NewAccessWitness(s.db.(*cachingDB).addrToPoint, make(map[chunkAccessKey]struct{})) } func (s *StateDB) Witness() *AccessWitness { diff --git a/core/state_processor.go b/core/state_processor.go index 9ad1a1a518a8..7c0b3ff43e88 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -178,7 +178,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo func InsertBlockHashHistoryAtEip2935Fork(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash, chain consensus.ChainHeaderReader) { // Make sure that the historical contract is added to the witness - statedb.Witness().TouchFullAccount(params.HistoryStorageAddress[:], true, math.MaxUint64) + statedb.Witness().TouchFullAccount(params.HistoryStorageAddress[:], true, true, math.MaxUint64) ancestor := chain.GetHeader(prevHash, prevNumber) for i := prevNumber; i > 0 && i >= prevNumber-params.Eip2935BlockHashHistorySize; i-- { @@ -192,5 +192,5 @@ func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash var key common.Hash binary.BigEndian.PutUint64(key[24:], ringIndex) statedb.SetState(params.HistoryStorageAddress, key, prevHash) - statedb.Witness().TouchSlotAndChargeGas(params.HistoryStorageAddress[:], key, true, math.MaxUint64, false) + statedb.Witness().TouchSlotAndChargeGas(params.HistoryStorageAddress[:], key, true, true, math.MaxUint64, false) } diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 359d6e08f7e4..ff15e1cafd74 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -505,47 +505,47 @@ func TestProcessVerkle(t *testing.T) { txCost1 := params.TxGas txCost2 := params.TxGas contractCreationCost := intrinsicContractCreationGas + - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkFillCost + /* creation */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* creation with value */ 739 /* execution costs */ codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkFillCost + /* creation (tx) */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkFillCost + /* creation (CREATE at pc=0x20) */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* write code hash */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #0 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #1 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #2 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #3 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #4 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #5 */ params.WitnessChunkReadCost + /* SLOAD in constructor */ - params.WitnessChunkWriteCost + /* SSTORE in constructor */ + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* SSTORE in constructor */ params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at PC=0x121) */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* write code hash */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #0 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #1 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #2 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #3 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #4 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #5 */ params.WitnessChunkReadCost + /* SLOAD in constructor */ - params.WitnessChunkWriteCost + /* SSTORE in constructor */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash for tx creation */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #6 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #7 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #8 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #9 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #10 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #11 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #12 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #13 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #14 */ + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* SSTORE in constructor */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* write code hash for tx creation */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #0 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #1 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #2 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #3 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #4 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #5 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #6 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #7 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #8 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #9 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #10 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #11 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #12 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #13 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessChunkFillCost + /* code chunk #14 */ 4844 /* execution costs */ blockGasUsagesExpected := []uint64{ txCost1*2 + txCost2, diff --git a/core/state_transition.go b/core/state_transition.go index 98e520d44982..2ed37b6e4994 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -453,7 +453,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.Accesses.TouchFullAccount(st.evm.Context.Coinbase[:], true, math.MaxUint64) + st.evm.Accesses.TouchFullAccount(st.evm.Context.Coinbase[:], true, true, math.MaxUint64) } } diff --git a/core/vm/evm.go b/core/vm/evm.go index 4371c0c41402..35fcd1a760a0 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -481,7 +481,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Charge the contract creation init gas in verkle mode if evm.chainRules.IsEIP4762 { - consumed, wanted := evm.Accesses.TouchAndChargeContractCreateInit(address.Bytes(), gas) + consumed, wanted := evm.Accesses.TouchAndChargeContractCreateInit(address.Bytes(), gas, !evm.StateDB.Exist(address)) if consumed < wanted { return nil, common.Address{}, 0, ErrOutOfGas } @@ -534,7 +534,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, err = ErrCodeStoreOutOfGas } } else { - consumed, wanted := evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas) + consumed, wanted := evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true, true, contract.Gas) contract.UseGas(consumed) // consumed <= contract.Gas, so no return value check is needed if len(ret) > 0 && (consumed < wanted) { err = ErrCodeStoreOutOfGas diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 4a9595283940..7fab37f66839 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -371,7 +371,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ codeAddr := scope.Contract.CodeAddr paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64()) if interpreter.evm.chainRules.IsEIP4762 && !scope.Contract.IsDeployment { - statelessGas, wanted := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), false, scope.Contract.Gas) + statelessGas, wanted := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), false, false, scope.Contract.Gas) scope.Contract.UseGas(statelessGas) if statelessGas < wanted { return nil, ErrOutOfGas @@ -401,7 +401,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) self: AccountRef(addr), } paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - statelessGas, wanted := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, scope.Contract.Gas) + statelessGas, wanted := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, false, scope.Contract.Gas) scope.Contract.UseGas(statelessGas) // statelessGas <= contract.Gas, so no need to check the return value if statelessGas < wanted { return nil, ErrOutOfGas @@ -462,7 +462,7 @@ func getBlockHashFromContract(number uint64, statedb StateDB, witness *state.Acc ringIndex := number % params.Eip2935BlockHashHistorySize var pnum common.Hash binary.BigEndian.PutUint64(pnum[24:], ringIndex) - statelessGas := witness.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false, availableGas, false) + statelessGas := witness.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false, false, availableGas, false) return statedb.GetState(params.HistoryStorageAddress, pnum), statelessGas } @@ -585,7 +585,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } pos := scope.Stack.pop() if !scope.Contract.validJumpdest(&pos) { - statelessGas, wanted := interpreter.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(scope.Contract.CodeAddr[:], pos.Uint64(), 1, uint64(len(scope.Contract.Code)), false, scope.Contract.Gas) + statelessGas, wanted := interpreter.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(scope.Contract.CodeAddr[:], pos.Uint64(), 1, uint64(len(scope.Contract.Code)), false, false, scope.Contract.Gas) scope.Contract.UseGas(statelessGas) if statelessGas < wanted { return nil, ErrOutOfGas @@ -603,7 +603,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by pos, cond := scope.Stack.pop(), scope.Stack.pop() if !cond.IsZero() { if !scope.Contract.validJumpdest(&pos) { - statelessGas, wanted := interpreter.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(scope.Contract.CodeAddr[:], pos.Uint64(), 1, uint64(len(scope.Contract.Code)), false, scope.Contract.Gas) + statelessGas, wanted := interpreter.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(scope.Contract.CodeAddr[:], pos.Uint64(), 1, uint64(len(scope.Contract.Code)), false, false, scope.Contract.Gas) scope.Contract.UseGas(statelessGas) if statelessGas < wanted { return nil, ErrOutOfGas @@ -954,7 +954,7 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by // touch next chunk if PUSH1 is at the boundary. if so, *pc has // advanced past this boundary. codeAddr := scope.Contract.CodeAddr - statelessGas, wanted := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], *pc, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas) + statelessGas, wanted := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], *pc, uint64(1), uint64(len(scope.Contract.Code)), false, false, scope.Contract.Gas) scope.Contract.UseGas(statelessGas) if statelessGas < wanted { return nil, ErrOutOfGas @@ -983,7 +983,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsVerkle { codeAddr := scope.Contract.CodeAddr - statelessGas, wanted := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas) + statelessGas, wanted := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, false, scope.Contract.Gas) scope.Contract.UseGas(statelessGas) if statelessGas < wanted { return nil, ErrOutOfGas diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 0a29133a9984..2e4721f43b8f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -183,7 +183,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. codeAddr := contract.CodeAddr - consumed, wanted := in.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], pc, 1, uint64(len(contract.Code)), false, contract.Gas) + consumed, wanted := in.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], pc, 1, uint64(len(contract.Code)), false, false, contract.Gas) contract.UseGas(consumed) if consumed < wanted { return nil, ErrOutOfGas diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 1d96c9578f5d..06eba17e9649 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -23,16 +23,16 @@ import ( ) func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return evm.Accesses.TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), true, contract.Gas, true), nil + return evm.Accesses.TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), true, true /* XXX needs a missing API */, contract.Gas, true), nil } func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return evm.Accesses.TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), false, contract.Gas, true), nil + return evm.Accesses.TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), false, false, contract.Gas, true), nil } func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { address := stack.peek().Bytes20() - return evm.Accesses.TouchBasicData(address[:], false, contract.Gas, true), nil + return evm.Accesses.TouchBasicData(address[:], false, false, contract.Gas, true), nil } func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { @@ -42,7 +42,7 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if isPrecompile || isSystemContract { return params.WarmStorageReadCostEIP2929, nil } - return evm.Accesses.TouchBasicData(address[:], false, contract.Gas, true), nil + return evm.Accesses.TouchBasicData(address[:], false, false, contract.Gas, true), nil } func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { @@ -65,7 +65,7 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga // If value is transferred, it is charged before 1/64th // is subtracted from the available gas pool. if withTransferCosts && !stack.Back(2).IsZero() { - wantedValueTransferWitnessGas := evm.Accesses.TouchAndChargeValueTransfer(contract.Address().Bytes()[:], target[:], contract.Gas) + wantedValueTransferWitnessGas := evm.Accesses.TouchAndChargeValueTransfer(contract.Address().Bytes()[:], target[:], !evm.StateDB.Exist(target), contract.Gas) if wantedValueTransferWitnessGas > contract.Gas { return wantedValueTransferWitnessGas, nil } @@ -111,7 +111,7 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem beneficiaryAddr := common.Address(stack.peek().Bytes20()) contractAddr := contract.Address() - wanted := evm.Accesses.TouchBasicData(contractAddr[:], false, contract.Gas, false) + wanted := evm.Accesses.TouchBasicData(contractAddr[:], false, false, contract.Gas, false) if wanted > contract.Gas { return wanted, nil } @@ -126,7 +126,7 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem } if contractAddr != beneficiaryAddr { - wanted := evm.Accesses.TouchBasicData(beneficiaryAddr[:], false, contract.Gas-statelessGas, false) + wanted := evm.Accesses.TouchBasicData(beneficiaryAddr[:], false, false, contract.Gas-statelessGas, false) if wanted > contract.Gas-statelessGas { return statelessGas + wanted, nil } @@ -134,7 +134,7 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem } // Charge write costs if it transfers value if !balanceIsZero { - wanted := evm.Accesses.TouchBasicData(contractAddr[:], true, contract.Gas-statelessGas, false) + wanted := evm.Accesses.TouchBasicData(contractAddr[:], true, false, contract.Gas-statelessGas, false) if wanted > contract.Gas-statelessGas { return statelessGas + wanted, nil } @@ -142,9 +142,9 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem if contractAddr != beneficiaryAddr { if evm.StateDB.Exist(beneficiaryAddr) { - wanted = evm.Accesses.TouchBasicData(beneficiaryAddr[:], true, contract.Gas-statelessGas, false) + wanted = evm.Accesses.TouchBasicData(beneficiaryAddr[:], true, false, contract.Gas-statelessGas, false) } else { - wanted = evm.Accesses.TouchFullAccount(beneficiaryAddr[:], true, contract.Gas-statelessGas) + wanted = evm.Accesses.TouchFullAccount(beneficiaryAddr[:], true, true, contract.Gas-statelessGas) } if wanted > contract.Gas-statelessGas { return statelessGas + wanted, nil @@ -172,7 +172,7 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo } return gas, nil } - wgas := evm.Accesses.TouchBasicData(addr[:], false, contract.Gas-gas, true) + wgas := evm.Accesses.TouchBasicData(addr[:], false, false, contract.Gas-gas, true) var overflow bool if gas, overflow = math.SafeAdd(gas, wgas); overflow { return 0, ErrGasUintOverflow