From 9f44b79f0ee9267d62b31fc042a6f0fe1cf1acf8 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 12 Sep 2024 16:30:29 -0300 Subject: [PATCH 01/38] state: access witness with partial cost charging Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 173 +++++++++++++++++------------------ 1 file changed, 86 insertions(+), 87 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index a881e97717f3..270eb8056809 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -18,7 +18,6 @@ package state import ( "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" @@ -30,6 +29,15 @@ import ( // * the second bit is set if the branch has been read type mode byte +// UseGasFn is a function that can be used to charge gas for a given amount. +type UseGasFn func(uint64) bool + +// useGasAlwaysTrue is used for gas charging that is included in the intrisic gas cost. +// Thus, it always returns true. +func useGasAlwaysTrue(uint64) bool { + return true +} + const ( AccessWitnessReadFlag = mode(1) AccessWitnessWriteFlag = mode(2) @@ -89,100 +97,65 @@ func (aw *AccessWitness) Copy() *AccessWitness { return naw } -func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool) uint64 { - var gas uint64 +func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool, useGasFn UseGasFn) bool { for i := utils.BasicDataLeafKey; i <= utils.CodeHashLeafKey; i++ { - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), isWrite) + ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), isWrite, useGasFn) + if !ok { + return false + } } - return gas + return true } -func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte) uint64 { - var gas uint64 - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false) - return gas +func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, useGasFn UseGasFn) bool { + return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn) } -func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 { - var gas uint64 - gas += aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) - gas += aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) - return gas +func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte, useGasFn UseGasFn) bool { + ok := aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) + if !ok { + return false + } + return aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) } // TouchAndChargeContractCreateCheck charges access costs before // a contract creation is initiated. It is just reads, because the // address collision is done before the transfer, and so no write // are guaranteed to happen at this point. -func (aw *AccessWitness) TouchAndChargeContractCreateCheck(addr []byte) uint64 { - var gas uint64 - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false) - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false) - return gas +func (aw *AccessWitness) TouchAndChargeContractCreateCheck(addr []byte, useGasFn UseGasFn) bool { + if !aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn) { + return false + } + return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, useGasFn) } // TouchAndChargeContractCreateInit charges access costs to initiate // a contract creation. -func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte) uint64 { - var gas uint64 - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true) - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true) - return gas +func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, useGasFn UseGasFn) bool { + if !aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) { + return false + } + return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true, useGasFn) } -func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 { +func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) { for i := utils.BasicDataLeafKey; i <= utils.CodeHashLeafKey; i++ { - aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BasicDataLeafKey) + aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BasicDataLeafKey, useGasAlwaysTrue) } - - // Kaustinen note: we're currently experimenting with stop chargin gas for the origin address - // so simple transfer still take 21000 gas. This is to potentially avoid breaking existing tooling. - // This is the reason why we return 0 instead of `gas`. - // Note that we still have to touch the addresses to make sure the witness is correct. - return 0 } -func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 { - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue) - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false) - - // Kaustinen note: we're currently experimenting with stop chargin gas for the origin address - // so simple transfer still take 21000 gas. This is to potentially avoid breaking existing tooling. - // This is the reason why we return 0 instead of `gas`. - // Note that we still have to touch the addresses to make sure the witness is correct. - return 0 +func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) { + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, useGasAlwaysTrue) + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, useGasAlwaysTrue) } -func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool) uint64 { +func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool, useGasFn UseGasFn) bool { treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slot.Bytes()) - return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite) + return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, useGasFn) } -func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 { - stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := aw.touchAddress(addr, treeIndex, subIndex, isWrite) - - var gas uint64 - if stemRead { - gas += params.WitnessBranchReadCost - } - if selectorRead { - gas += params.WitnessChunkReadCost - } - if stemWrite { - gas += params.WitnessBranchWriteCost - } - if selectorWrite { - gas += params.WitnessChunkWriteCost - } - if selectorFill { - gas += params.WitnessChunkFillCost - } - - return gas -} - -// touchAddress adds any missing access event to the witness. -func (aw *AccessWitness) touchAddress(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) { +func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool, useGasFn UseGasFn) bool { branchKey := newBranchAccessKey(addr, treeIndex) chunkKey := newChunkAccessKey(branchKey, subIndex) @@ -190,11 +163,9 @@ func (aw *AccessWitness) touchAddress(addr []byte, treeIndex uint256.Int, subInd var branchRead, chunkRead bool if _, hasStem := aw.branches[branchKey]; !hasStem { branchRead = true - aw.branches[branchKey] = AccessWitnessReadFlag } if _, hasSelector := aw.chunks[chunkKey]; !hasSelector { chunkRead = true - aw.chunks[chunkKey] = AccessWitnessReadFlag } // Write access. @@ -202,19 +173,50 @@ func (aw *AccessWitness) touchAddress(addr []byte, treeIndex uint256.Int, subInd if isWrite { if (aw.branches[branchKey] & AccessWitnessWriteFlag) == 0 { branchWrite = true - aw.branches[branchKey] |= AccessWitnessWriteFlag } chunkValue := aw.chunks[chunkKey] if (chunkValue & AccessWitnessWriteFlag) == 0 { chunkWrite = true - aw.chunks[chunkKey] |= AccessWitnessWriteFlag } + } - // TODO: charge chunk filling costs if the leaf was previously empty in the state + var gas uint64 + if branchRead { + gas += params.WitnessBranchReadCost + } + if chunkRead { + gas += params.WitnessChunkReadCost + } + if branchWrite { + gas += params.WitnessBranchWriteCost + } + if chunkWrite { + gas += params.WitnessChunkWriteCost + } + if chunkFill { + gas += params.WitnessChunkFillCost + } + + if ok := useGasFn(gas); !ok { + return false + } + + if branchRead { + aw.branches[branchKey] = AccessWitnessReadFlag + } + if branchWrite { + aw.branches[branchKey] |= AccessWitnessWriteFlag + } + if chunkRead { + aw.chunks[chunkKey] = AccessWitnessReadFlag + } + if chunkWrite { + chunkWrite = true + aw.chunks[chunkKey] |= AccessWitnessWriteFlag } - return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill + return true } type branchAccessKey struct { @@ -242,7 +244,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) uint64 { +func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64, isWrite bool, useGasFn UseGasFn) bool { // 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 @@ -250,7 +252,7 @@ func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, s // is already in the AccessWitness so a stateless verifier can see that // the code from the last leaf is not needed. if size == 0 || startPC >= codeLen { - return 0 + return true } endPC := startPC + size @@ -261,25 +263,22 @@ func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, s endPC -= 1 // endPC is the last bytecode that will be touched. } - var statelessGasCharged uint64 for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ { treeIndex := *uint256.NewInt((chunkNumber + 128) / 256) subIndex := byte((chunkNumber + 128) % 256) - gas := aw.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite) - var overflow bool - statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas) - if overflow { - panic("overflow when adding gas") + ok := aw.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, useGasFn) + if !ok { + return false } } - return statelessGasCharged + return true } -func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool) uint64 { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite) +func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool, useGasFn UseGasFn) bool { + return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, useGasFn) } -func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool) uint64 { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite) +func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool, useGasFn UseGasFn) bool { + return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, useGasFn) } From 8b45fb7451315acba24f63daa9603f6ce5c03518 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 12 Sep 2024 18:40:51 -0300 Subject: [PATCH 02/38] fix most problems Signed-off-by: Ignacio Hagopian --- cmd/evm/internal/t8ntool/execution.go | 2 +- consensus/beacon/consensus.go | 2 +- core/state/access_witness.go | 18 +++++++----------- core/state_processor.go | 4 ++-- core/state_transition.go | 25 +++---------------------- core/vm/common.go | 12 ++++++++++++ core/vm/evm.go | 19 ++++++++++++------- core/vm/instructions.go | 12 ++++-------- core/vm/interpreter.go | 2 +- 9 files changed, 43 insertions(+), 53 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index a151047eeea1..219f0b80c18a 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) + statedb.Witness().TouchFullAccount(w.Address[:], true, nil) } 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 1fe5ffb612ff..741653f5c906 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -358,7 +358,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) + state.Witness().TouchFullAccount(w.Address[:], true, nil) } if chain.Config().IsVerkle(header.Number, header.Time) { diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 270eb8056809..b053c7f9ac51 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -32,12 +32,6 @@ type mode byte // UseGasFn is a function that can be used to charge gas for a given amount. type UseGasFn func(uint64) bool -// useGasAlwaysTrue is used for gas charging that is included in the intrisic gas cost. -// Thus, it always returns true. -func useGasAlwaysTrue(uint64) bool { - return true -} - const ( AccessWitnessReadFlag = mode(1) AccessWitnessWriteFlag = mode(2) @@ -141,13 +135,13 @@ func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, useGasFn func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) { for i := utils.BasicDataLeafKey; i <= utils.CodeHashLeafKey; i++ { - aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BasicDataLeafKey, useGasAlwaysTrue) + aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BasicDataLeafKey, nil) } } func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) { - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, useGasAlwaysTrue) - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, useGasAlwaysTrue) + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, nil) + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, nil) } func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool, useGasFn UseGasFn) bool { @@ -198,8 +192,10 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256 gas += params.WitnessChunkFillCost } - if ok := useGasFn(gas); !ok { - return false + if useGasFn != nil { + if ok := useGasFn(gas); !ok { + return false + } } if branchRead { diff --git a/core/state_processor.go b/core/state_processor.go index baf60a5525b7..cd561c7ab2c8 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -177,7 +177,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) + statedb.Witness().TouchFullAccount(params.HistoryStorageAddress[:], true, nil) ancestor := chain.GetHeader(prevHash, prevNumber) for i := prevNumber; i > 0 && i >= prevNumber-params.Eip2935BlockHashHistorySize; i-- { @@ -191,5 +191,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) + statedb.Witness().TouchSlotAndChargeGas(params.HistoryStorageAddress[:], key, true, nil) } diff --git a/core/state_transition.go b/core/state_transition.go index 0aa4a369018b..45a736d5e189 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -339,19 +339,6 @@ func (st *StateTransition) preCheck() error { return st.buyGas() } -// tryConsumeGas tries to subtract gas from gasPool, setting the result in gasPool -// if subtracting more gas than remains in gasPool, set gasPool = 0 and return false -// otherwise, do the subtraction setting the result in gasPool and return true -func tryConsumeGas(gasPool *uint64, gas uint64) bool { - if *gasPool < gas { - *gasPool = 0 - return false - } - - *gasPool -= gas - return true -} - // TransitionDb will transition the state by applying the current message and // returning the evm execution result with following fields. // @@ -406,16 +393,10 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { targetAddr := msg.To originAddr := msg.From - statelessGasOrigin := st.evm.Accesses.TouchTxOriginAndComputeGas(originAddr.Bytes()) - if !tryConsumeGas(&st.gasRemaining, statelessGasOrigin) { - return nil, fmt.Errorf("%w: Insufficient funds to cover witness access costs for transaction: have %d, want %d", ErrInsufficientBalanceWitness, st.gasRemaining, gas) - } + st.evm.Accesses.TouchTxOriginAndComputeGas(originAddr.Bytes()) if msg.To != nil { - statelessGasDest := st.evm.Accesses.TouchTxExistingAndComputeGas(targetAddr.Bytes(), msg.Value.Sign() != 0) - if !tryConsumeGas(&st.gasRemaining, statelessGasDest) { - return nil, fmt.Errorf("%w: Insufficient funds to cover witness access costs for transaction: have %d, want %d", ErrInsufficientBalanceWitness, st.gasRemaining, gas) - } + st.evm.Accesses.TouchTxExistingAndComputeGas(targetAddr.Bytes(), msg.Value.Sign() != 0) // ensure the code size ends up in the access witness st.evm.StateDB.GetCodeSize(*targetAddr) @@ -472,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) + st.evm.Accesses.TouchFullAccount(st.evm.Context.Coinbase[:], true, nil) } } diff --git a/core/vm/common.go b/core/vm/common.go index ba75950e370b..226a337acd56 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -92,3 +92,15 @@ func allZero(b []byte) bool { } return true } + +type gasConsumer struct { + availableGas uint64 +} + +func (gc *gasConsumer) consumeGas(gas uint64) bool { + if gc.availableGas < gas { + return false + } + gc.availableGas -= gas + return true +} diff --git a/core/vm/evm.go b/core/vm/evm.go index fed7302b5919..f937d537d8c3 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -202,12 +202,13 @@ 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.Accesses.TouchFullAccount(addr.Bytes(), false) - if gas < wgas { + gc := gasConsumer{availableGas: gas} + ok := evm.Accesses.TouchFullAccount(addr.Bytes(), false, gc.consumeGas) + if !ok { evm.StateDB.RevertToSnapshot(snapshot) return nil, 0, ErrOutOfGas } - gas -= wgas + gas = gc.availableGas } if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { @@ -457,7 +458,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Charge the contract creation init gas in verkle mode if evm.chainRules.IsEIP4762 { - statelessGas := evm.Accesses.TouchAndChargeContractCreateCheck(address.Bytes()) + gc := gasConsumer{availableGas: gas} + if !evm.Accesses.TouchAndChargeContractCreateCheck(address.Bytes(), gc.consumeGas){ if statelessGas > gas { return nil, common.Address{}, 0, ErrOutOfGas } @@ -496,8 +498,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, contract.SetCodeOptionalHash(&address, codeAndHash) contract.IsDeployment = true - // Charge the contract creation init gas in verkle mode - if evm.Config.Tracer != nil { if evm.depth == 0 { evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value) @@ -529,7 +529,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, err = ErrCodeStoreOutOfGas } } else { - if err == nil && len(ret) > 0 && !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) { + // Contract creation completed, touch the missing fields in the contract + if !evm.Accesses.TouchFullAccount(address.Bytes()[:], true, contract.UseGas) { + err = ErrCodeStoreOutOfGas + } + + if err == nil && len(ret) > 0 && !evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true, contract.UseGas) { err = ErrCodeStoreOutOfGas } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 653a9f1eead4..3210941ec72e 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -371,8 +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 := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), false) - if !scope.Contract.UseGas(statelessGas) { + if !interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), false, scope.Contract.UseGas) { scope.Contract.Gas = 0 return nil, ErrOutOfGas } @@ -401,8 +400,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) self: AccountRef(addr), } paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) - if !scope.Contract.UseGas(statelessGas) { + if !interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, scope.Contract.UseGas) { scope.Contract.Gas = 0 return nil, ErrOutOfGas } @@ -950,8 +948,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 := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false) - if !scope.Contract.UseGas(statelessGas) { + if !interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.UseGas) { scope.Contract.Gas = 0 return nil, ErrOutOfGas } @@ -979,8 +976,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsVerkle { codeAddr := scope.Contract.CodeAddr - statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false) - if !scope.Contract.UseGas(statelessGas) { + if !interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.UseGas) { scope.Contract.Gas = 0 return nil, ErrOutOfGas } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 9a1897ec7e83..c9a53584b19e 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 - if !contract.UseGas(in.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], pc, 1, uint64(len(contract.Code)), false)) { + if !in.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], pc, 1, uint64(len(contract.Code)), false, contract.UseGas) { return nil, ErrOutOfGas } } From d46fa010f1e7bb24beb6f53b663186fa24e7d2d9 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 12 Sep 2024 19:03:20 -0300 Subject: [PATCH 03/38] rebase fixes Signed-off-by: Ignacio Hagopian --- core/vm/evm.go | 11 +++++------ core/vm/instructions.go | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index f937d537d8c3..bdcd7d09619e 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -459,11 +459,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Charge the contract creation init gas in verkle mode if evm.chainRules.IsEIP4762 { gc := gasConsumer{availableGas: gas} - if !evm.Accesses.TouchAndChargeContractCreateCheck(address.Bytes(), gc.consumeGas){ - if statelessGas > gas { + if !evm.Accesses.TouchAndChargeContractCreateCheck(address.Bytes(), gc.consumeGas) { return nil, common.Address{}, 0, ErrOutOfGas } - gas = gas - statelessGas + gas = gc.availableGas } // We add this to the access list _before_ taking a snapshot. Even if the creation fails, // the access-list change should not be rolled back @@ -484,11 +483,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Charge the contract creation init gas in verkle mode if evm.chainRules.IsEIP4762 { - statelessGas := evm.Accesses.TouchAndChargeContractCreateInit(address.Bytes()) - if statelessGas > gas { + gc := gasConsumer{availableGas: gas} + if !evm.Accesses.TouchAndChargeContractCreateInit(address.Bytes(), gc.consumeGas) { return nil, common.Address{}, 0, ErrOutOfGas } - gas = gas - statelessGas + gas = gc.availableGas } evm.Context.Transfer(evm.StateDB, caller.Address(), address, value) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 3210941ec72e..227d8f6525ef 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -583,7 +583,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } pos := scope.Stack.pop() if !scope.Contract.validJumpdest(&pos) { - if !scope.Contract.UseGas(interpreter.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(scope.Contract.CodeAddr[:], pos.Uint64(), 1, uint64(len(scope.Contract.Code)), false)) { + if !interpreter.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(scope.Contract.CodeAddr[:], pos.Uint64(), 1, uint64(len(scope.Contract.Code)), false, scope.Contract.UseGas) { return nil, ErrOutOfGas } return nil, ErrInvalidJump @@ -599,7 +599,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) { - if !scope.Contract.UseGas(interpreter.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(scope.Contract.CodeAddr[:], pos.Uint64(), 1, uint64(len(scope.Contract.Code)), false)) { + if !interpreter.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(scope.Contract.CodeAddr[:], pos.Uint64(), 1, uint64(len(scope.Contract.Code)), false, scope.Contract.UseGas) { return nil, ErrOutOfGas } return nil, ErrInvalidJump From d9bab6664a4157fdcf6849a596a6bdbf717c9baf Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 16 Sep 2024 10:38:48 -0300 Subject: [PATCH 04/38] fixes Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 227d8f6525ef..c7c9b1f54a91 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -20,7 +20,6 @@ import ( "encoding/binary" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" @@ -456,14 +455,6 @@ func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ return nil, nil } -func getBlockHashFromContract(number uint64, statedb StateDB, witness *state.AccessWitness) (common.Hash, uint64) { - ringIndex := number % params.Eip2935BlockHashHistorySize - var pnum common.Hash - binary.BigEndian.PutUint64(pnum[24:], ringIndex) - statelessGas := witness.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false) - return statedb.GetState(params.HistoryStorageAddress, pnum), statelessGas -} - func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { num := scope.Stack.peek() num64, overflow := num.Uint64WithOverflow() @@ -484,12 +475,13 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( if num64 >= lower && num64 < upper { // if Verkle is active, read it from the history contract (EIP 2935). if evm.chainRules.IsVerkle { - blockHash, statelessGas := getBlockHashFromContract(num64, evm.StateDB, evm.Accesses) - if interpreter.evm.chainRules.IsEIP4762 { - if !scope.Contract.UseGas(statelessGas) { - return nil, ErrExecutionReverted - } + ringIndex := num64 % params.Eip2935BlockHashHistorySize + var pnum common.Hash + binary.BigEndian.PutUint64(pnum[24:], ringIndex) + if !evm.Accesses.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false, scope.Contract.UseGas) { + return nil, ErrExecutionReverted } + blockHash := evm.StateDB.GetState(params.HistoryStorageAddress, pnum) num.SetBytes(blockHash.Bytes()) } else { num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) From 3d79a1deaa3783c8bc62f759b81809edcfc167f3 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 16 Sep 2024 10:58:26 -0300 Subject: [PATCH 05/38] fixes Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index b053c7f9ac51..8030f5ad73dc 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -106,10 +106,6 @@ func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, useGasFn UseGasF } func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte, useGasFn UseGasFn) bool { - ok := aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) - if !ok { - return false - } return aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) } From 59720c433af575f69a5efd256b87b4ff744bc90c Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 16 Sep 2024 16:12:13 -0300 Subject: [PATCH 06/38] . Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 34 +++++++++++++------------ core/vm/eips.go | 8 +++--- core/vm/instructions.go | 36 +++++++++++++++++++++++++- core/vm/operations_verkle.go | 49 ------------------------------------ 4 files changed, 57 insertions(+), 70 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 8030f5ad73dc..3187c641f4b6 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -93,8 +93,7 @@ func (aw *AccessWitness) Copy() *AccessWitness { func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool, useGasFn UseGasFn) bool { for i := utils.BasicDataLeafKey; i <= utils.CodeHashLeafKey; i++ { - ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), isWrite, useGasFn) - if !ok { + if _, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), isWrite, useGasFn); !ok { return false } } @@ -102,11 +101,13 @@ func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool, useGasFn Us } func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, useGasFn UseGasFn) bool { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn) + _, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn) + return ok } func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte, useGasFn UseGasFn) bool { - return aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) + _, ok := aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) + return ok } // TouchAndChargeContractCreateCheck charges access costs before @@ -114,19 +115,21 @@ 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, useGasFn UseGasFn) bool { - if !aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn) { + if _, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn); !ok { return false } - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, useGasFn) + _, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, useGasFn) + return ok } // TouchAndChargeContractCreateInit charges access costs to initiate // a contract creation. func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, useGasFn UseGasFn) bool { - if !aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) { + if _, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn); !ok { return false } - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true, useGasFn) + _, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true, useGasFn) + return ok } func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) { @@ -140,12 +143,12 @@ func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsVa aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, nil) } -func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool, useGasFn UseGasFn) bool { +func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool, useGasFn UseGasFn) (uint64, bool) { treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slot.Bytes()) return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, useGasFn) } -func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool, useGasFn UseGasFn) bool { +func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool, useGasFn UseGasFn) (uint64, bool) { branchKey := newBranchAccessKey(addr, treeIndex) chunkKey := newChunkAccessKey(branchKey, subIndex) @@ -190,7 +193,7 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256 if useGasFn != nil { if ok := useGasFn(gas); !ok { - return false + return 0, false } } @@ -208,7 +211,7 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256 aw.chunks[chunkKey] |= AccessWitnessWriteFlag } - return true + return gas, true } type branchAccessKey struct { @@ -258,8 +261,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) - ok := aw.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, useGasFn) - if !ok { + if _, ok := aw.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, useGasFn); !ok { return false } } @@ -267,10 +269,10 @@ func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, s return true } -func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool, useGasFn UseGasFn) bool { +func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool, useGasFn UseGasFn) (uint64, bool) { return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, useGasFn) } -func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool, useGasFn UseGasFn) bool { +func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool, useGasFn UseGasFn) (uint64, bool) { return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, useGasFn) } diff --git a/core/vm/eips.go b/core/vm/eips.go index 1df79f6cffd0..a58cd2505b43 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -307,13 +307,13 @@ func enable6780(jt *JumpTable) { func enable4762(jt *JumpTable) { jt[SSTORE].constantGas = 0 - jt[SSTORE].dynamicGas = gasSStore4762 + jt[SSTORE].dynamicGas = nil jt[SLOAD].constantGas = 0 - jt[SLOAD].dynamicGas = gasSLoad4762 - jt[BALANCE].dynamicGas = gasBalance4762 + jt[SLOAD].dynamicGas = nil + jt[BALANCE].dynamicGas = nil jt[BALANCE].constantGas = 0 jt[EXTCODESIZE].constantGas = 0 - jt[EXTCODESIZE].dynamicGas = gasExtCodeSize4762 + jt[EXTCODESIZE].dynamicGas = nil jt[EXTCODEHASH].constantGas = 0 jt[EXTCODEHASH].dynamicGas = gasExtCodeHash4762 jt[EXTCODECOPY].constantGas = 0 diff --git a/core/vm/instructions.go b/core/vm/instructions.go index c7c9b1f54a91..cad173d677fe 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -261,6 +261,12 @@ func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) + if interpreter.evm.chainRules.IsVerkle { + chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas) + if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + return nil, ErrExecutionReverted + } + } slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address)) return nil, nil } @@ -345,6 +351,14 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) slot := scope.Stack.peek() address := slot.Bytes20() cs := uint64(interpreter.evm.StateDB.GetCodeSize(address)) + if interpreter.evm.chainRules.IsVerkle { + if _, isPrecompile := interpreter.evm.precompile(address); !isPrecompile { + chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas) + if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + return nil, ErrExecutionReverted + } + } + } slot.SetUint64(cs) return nil, nil } @@ -441,6 +455,14 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) + if interpreter.evm.chainRules.IsVerkle { + if _, isPrecompile := interpreter.evm.precompile(address); !isPrecompile { + chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas) + if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + return nil, ErrExecutionReverted + } + } + } if interpreter.evm.StateDB.Empty(address) { slot.Clear() } else { @@ -553,8 +575,13 @@ func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { loc := scope.Stack.peek() hash := common.Hash(loc.Bytes32()) + if interpreter.evm.chainRules.IsVerkle { + chargedGas, ok := interpreter.evm.Accesses.TouchSlotAndChargeGas(scope.Contract.Address().Bytes(), loc.Bytes32(), false, scope.Contract.UseGas) + if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + return nil, ErrExecutionReverted + } + } val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash) - loc.SetBytes(val.Bytes()) return nil, nil } @@ -565,6 +592,13 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } loc := scope.Stack.pop() val := scope.Stack.pop() + if interpreter.evm.chainRules.IsVerkle { + chargedGas, ok := interpreter.evm.Accesses.TouchSlotAndChargeGas(scope.Contract.Address().Bytes(), loc.Bytes32(), true, scope.Contract.UseGas) + if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + return nil, ErrExecutionReverted + } + } + interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32()) return nil, nil } diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 594c84236ba8..8c790e50e4e7 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -22,55 +22,6 @@ import ( "github.com/ethereum/go-ethereum/params" ) -func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.Accesses.TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), true) - if gas == 0 { - gas = params.WarmStorageReadCostEIP2929 - } - return gas, nil -} - -func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.Accesses.TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), false) - if gas == 0 { - gas = params.WarmStorageReadCostEIP2929 - } - return gas, nil -} - -func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - address := stack.peek().Bytes20() - gas := evm.Accesses.TouchBasicData(address[:], false) - if gas == 0 { - gas = params.WarmStorageReadCostEIP2929 - } - return gas, nil -} - -func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - address := stack.peek().Bytes20() - if _, isPrecompile := evm.precompile(address); isPrecompile { - return params.WarmStorageReadCostEIP2929, nil - } - wgas := evm.Accesses.TouchBasicData(address[:], false) - if wgas == 0 { - wgas = params.WarmStorageReadCostEIP2929 - } - return wgas, nil -} - -func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - address := stack.peek().Bytes20() - if _, isPrecompile := evm.precompile(address); isPrecompile || evm.isSystemContract(address) { - return params.WarmStorageReadCostEIP2929, nil - } - codehashgas := evm.Accesses.TouchCodeHash(address[:], false) - if codehashgas == 0 { - codehashgas = params.WarmStorageReadCostEIP2929 - } - return codehashgas, nil -} - func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { gas, err := oldCalculator(evm, contract, stack, mem, memorySize) From 49d84a2561a6c1a7551bf3c83057180fc888e6bb Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 10:55:34 -0300 Subject: [PATCH 07/38] remove 4762 gas call wrappers Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 5 ++--- core/vm/eips.go | 10 +++++----- core/vm/instructions.go | 33 ++++++++++++++++++++++++++++++++- core/vm/operations_verkle.go | 34 ---------------------------------- 4 files changed, 39 insertions(+), 43 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 3187c641f4b6..71daaa82edac 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -100,9 +100,8 @@ func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool, useGasFn Us return true } -func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, useGasFn UseGasFn) bool { - _, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn) - return ok +func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, useGasFn UseGasFn) (uint64, bool) { + return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn) } func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte, useGasFn UseGasFn) bool { diff --git a/core/vm/eips.go b/core/vm/eips.go index a58cd2505b43..c8d651a5e347 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -315,18 +315,18 @@ func enable4762(jt *JumpTable) { jt[EXTCODESIZE].constantGas = 0 jt[EXTCODESIZE].dynamicGas = nil jt[EXTCODEHASH].constantGas = 0 - jt[EXTCODEHASH].dynamicGas = gasExtCodeHash4762 + jt[EXTCODEHASH].dynamicGas = nil jt[EXTCODECOPY].constantGas = 0 jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP4762 jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP4762 jt[CREATE].constantGas = params.CreateNGasEip4762 jt[CREATE2].constantGas = params.CreateNGasEip4762 jt[CALL].constantGas = 0 - jt[CALL].dynamicGas = gasCallEIP4762 + jt[CALL].dynamicGas = gasCall jt[CALLCODE].constantGas = 0 - jt[CALLCODE].dynamicGas = gasCallCodeEIP4762 + jt[CALLCODE].dynamicGas = gasCallCode jt[STATICCALL].constantGas = 0 - jt[STATICCALL].dynamicGas = gasStaticCallEIP4762 + jt[STATICCALL].dynamicGas = gasStaticCall jt[DELEGATECALL].constantGas = 0 - jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP4762 + jt[DELEGATECALL].dynamicGas = gasDelegateCall } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index cad173d677fe..eee2761b39ac 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -500,7 +500,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( ringIndex := num64 % params.Eip2935BlockHashHistorySize var pnum common.Hash binary.BigEndian.PutUint64(pnum[24:], ringIndex) - if !evm.Accesses.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false, scope.Contract.UseGas) { + if _, ok := evm.Accesses.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false, scope.Contract.UseGas); !ok { return nil, ErrExecutionReverted } blockHash := evm.StateDB.GetState(params.HistoryStorageAddress, pnum) @@ -744,7 +744,29 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] return nil, nil } +func chargeCallVariantEIP4762(evm *EVM, scope *ScopeContext) bool { + target := common.Address(scope.Stack.Back(1).Bytes20()) + if _, isPrecompile := evm.precompile(target); isPrecompile { + return true + } + // The charging for the value transfer is done BEFORE subtracting + // the 1/64th gas, as this is considered part of the CALL instruction. + // (so before we get to this point) + // But the message call is part of the subcall, for which only 63/64th + // of the gas should be available. + chargedGas, ok := evm.Accesses.TouchAndChargeMessageCall(target.Bytes(), scope.Contract.UseGas) + if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + return false + } + return true + +} + func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if !chargeCallVariantEIP4762(interpreter.evm, scope) { + return nil, ErrExecutionReverted + } + stack := scope.Stack // Pop gas. The actual gas in interpreter.evm.callGasTemp. // We can use this as a temporary value @@ -786,6 +808,9 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if !chargeCallVariantEIP4762(interpreter.evm, scope) { + return nil, ErrExecutionReverted + } // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack // We use it as a temporary value @@ -821,6 +846,9 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if !chargeCallVariantEIP4762(interpreter.evm, scope) { + return nil, ErrExecutionReverted + } stack := scope.Stack // Pop gas. The actual gas is in interpreter.evm.callGasTemp. // We use it as a temporary value @@ -849,6 +877,9 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if !chargeCallVariantEIP4762(interpreter.evm, scope) { + return nil, ErrExecutionReverted + } // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack // We use it as a temporary value diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 8c790e50e4e7..8ccdaf2c4a4c 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -22,40 +22,6 @@ import ( "github.com/ethereum/go-ethereum/params" ) -func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc { - return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := oldCalculator(evm, contract, stack, mem, memorySize) - if err != nil { - return 0, err - } - target := common.Address(stack.Back(1).Bytes20()) - if _, isPrecompile := evm.precompile(target); isPrecompile { - var overflow bool - if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow { - return 0, ErrGasUintOverflow - } - return gas, nil - } - // The charging for the value transfer is done BEFORE subtracting - // the 1/64th gas, as this is considered part of the CALL instruction. - // (so before we get to this point) - // But the message call is part of the subcall, for which only 63/64th - // of the gas should be available. - wgas := evm.Accesses.TouchAndChargeMessageCall(target.Bytes()) - if wgas == 0 { - wgas = params.WarmStorageReadCostEIP2929 - } - return wgas + gas, nil - } -} - -var ( - gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall) - gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode) - gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall) - gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall) -) - func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { beneficiaryAddr := common.Address(stack.peek().Bytes20()) contractAddr := contract.Address() From ed4e5e55f3874c73f81cf063b51bc6ec02939110 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 14:26:42 -0300 Subject: [PATCH 08/38] more progress Signed-off-by: Ignacio Hagopian --- core/vm/eips.go | 4 +- core/vm/instructions.go | 46 +++++++++++++++++++++ core/vm/operations_verkle.go | 77 ------------------------------------ 3 files changed, 48 insertions(+), 79 deletions(-) delete mode 100644 core/vm/operations_verkle.go diff --git a/core/vm/eips.go b/core/vm/eips.go index c8d651a5e347..73fbf703c0b7 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -317,8 +317,8 @@ func enable4762(jt *JumpTable) { jt[EXTCODEHASH].constantGas = 0 jt[EXTCODEHASH].dynamicGas = nil jt[EXTCODECOPY].constantGas = 0 - jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP4762 - jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP4762 + jt[EXTCODECOPY].dynamicGas = gasExtCodeCopy + jt[SELFDESTRUCT].dynamicGas = nil jt[CREATE].constantGas = params.CreateNGasEip4762 jt[CREATE2].constantGas = params.CreateNGasEip4762 jt[CALL].constantGas = 0 diff --git a/core/vm/instructions.go b/core/vm/instructions.go index eee2761b39ac..d8b50d34c43a 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -401,6 +401,17 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) codeOffset = stack.pop() length = stack.pop() ) + + if interpreter.evm.chainRules.IsVerkle { + addr := common.Address(a.Bytes20()) + if _, isPrecompile := interpreter.evm.precompile(addr); !isPrecompile { + chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(addr[:], false, scope.Contract.UseGas) + if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + return nil, ErrExecutionReverted + } + } + } + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { uint64CodeOffset = 0xffffffffffffffff @@ -946,6 +957,41 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if interpreter.evm.chainRules.IsVerkle { + beneficiaryAddr := common.Address(scope.Stack.peek().Bytes20()) + contractAddr := scope.Contract.Address() + + if _, ok := interpreter.evm.Accesses.TouchBasicData(contractAddr[:], false, scope.Contract.UseGas); !ok { + return nil, ErrExecutionReverted + } + balanceIsZero := interpreter.evm.StateDB.GetBalance(contractAddr).Sign() == 0 + + if _, isPrecompile := interpreter.evm.precompile(beneficiaryAddr); !(isPrecompile && balanceIsZero) { + if contractAddr != beneficiaryAddr { + if _, ok := interpreter.evm.Accesses.TouchBasicData(beneficiaryAddr[:], false, scope.Contract.UseGas); !ok { + return nil, ErrExecutionReverted + } + } + // Charge write costs if it transfers value + if !balanceIsZero { + if _, ok := interpreter.evm.Accesses.TouchBasicData(contractAddr[:], true, scope.Contract.UseGas); !ok { + return nil, ErrExecutionReverted + } + if contractAddr != beneficiaryAddr { + if interpreter.evm.StateDB.Exist(beneficiaryAddr) { + if _, ok := interpreter.evm.Accesses.TouchBasicData(beneficiaryAddr[:], true, scope.Contract.UseGas); !ok { + return nil, ErrExecutionReverted + } + } else { + if !interpreter.evm.Accesses.TouchFullAccount(beneficiaryAddr[:], true, scope.Contract.UseGas) { + return nil, ErrExecutionReverted + } + } + } + } + } + } + if interpreter.readOnly { return nil, ErrWriteProtection } diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go deleted file mode 100644 index 8ccdaf2c4a4c..000000000000 --- a/core/vm/operations_verkle.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2024 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package vm - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/params" -) - -func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - beneficiaryAddr := common.Address(stack.peek().Bytes20()) - contractAddr := contract.Address() - - statelessGas := evm.Accesses.TouchBasicData(contractAddr[:], false) - balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0 - - if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile && balanceIsZero { - return statelessGas, nil - } - - if contractAddr != beneficiaryAddr { - statelessGas += evm.Accesses.TouchBasicData(beneficiaryAddr[:], false) - } - // Charge write costs if it transfers value - if !balanceIsZero { - statelessGas += evm.Accesses.TouchBasicData(contractAddr[:], true) - if contractAddr != beneficiaryAddr { - if evm.StateDB.Exist(beneficiaryAddr) { - statelessGas += evm.Accesses.TouchBasicData(beneficiaryAddr[:], true) - } else { - statelessGas += evm.Accesses.TouchFullAccount(beneficiaryAddr[:], true) - } - } - } - return statelessGas, nil -} - -func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - // memory expansion first (dynamic part of pre-2929 implementation) - gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize) - if err != nil { - return 0, err - } - addr := common.Address(stack.peek().Bytes20()) - - if _, isPrecompile := evm.precompile(addr); isPrecompile { - var overflow bool - if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow { - return 0, ErrGasUintOverflow - } - return gas, nil - } - wgas := evm.Accesses.TouchBasicData(addr[:], false) - if wgas == 0 { - wgas = params.WarmStorageReadCostEIP2929 - } - var overflow bool - if gas, overflow = math.SafeAdd(gas, wgas); overflow { - return 0, ErrGasUintOverflow - } - return gas, nil -} From 4ecd7130fedc81882ddd3441007b4f0917b4f520 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 14:38:48 -0300 Subject: [PATCH 09/38] call gas Signed-off-by: Ignacio Hagopian --- core/vm/gas_table.go | 19 ------------------- core/vm/instructions.go | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 7fcfd0472e3a..27233cc0ddbe 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -403,25 +403,6 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize return 0, ErrGasUintOverflow } - if evm.chainRules.IsEIP4762 { - // If value is transferred, it is charged before 1/64th - // is subtracted from the available gas pool. - if transfersValue { - gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeValueTransfer(contract.Address().Bytes()[:], address.Bytes()[:])) - if overflow { - return 0, ErrGasUintOverflow - } - } - } - - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) - if err != nil { - return 0, err - } - if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { - return 0, ErrGasUintOverflow - } - return gas, nil } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index d8b50d34c43a..30969a45398e 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -756,6 +756,28 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } func chargeCallVariantEIP4762(evm *EVM, scope *ScopeContext) bool { + if evm.chainRules.IsEIP4762 { + address := common.Address(scope.Stack.Back(1).Bytes20()) + transfersValue := !scope.Stack.Back(2).IsZero() + + // If value is transferred, it is charged before 1/64th + // is subtracted from the available gas pool. + if transfersValue { + if !evm.Accesses.TouchAndChargeValueTransfer(scope.Contract.Address().Bytes()[:], address.Bytes()[:], scope.Contract.UseGas) { + return false + } + } + } + + var err error + evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) + if err != nil { + return false + } + if scope.Contract.UseGas(evm.callGasTemp) { + return false + } + target := common.Address(scope.Stack.Back(1).Bytes20()) if _, isPrecompile := evm.precompile(target); isPrecompile { return true From 95f771ddbc308fd27d34202564d6be422c6a8ea7 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 14:54:24 -0300 Subject: [PATCH 10/38] fix Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 30969a45398e..481102147a71 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -467,7 +467,7 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) if interpreter.evm.chainRules.IsVerkle { - if _, isPrecompile := interpreter.evm.precompile(address); !isPrecompile { + if _, isPrecompile := interpreter.evm.precompile(address); !isPrecompile && !interpreter.evm.isSystemContract(address) { chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas) if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { return nil, ErrExecutionReverted From 81940b9fc693df03f92a78bee62a2786cac00444 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 15:11:38 -0300 Subject: [PATCH 11/38] fix Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 481102147a71..d67231a27746 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -467,7 +467,11 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) if interpreter.evm.chainRules.IsVerkle { - if _, isPrecompile := interpreter.evm.precompile(address); !isPrecompile && !interpreter.evm.isSystemContract(address) { + if _, isPrecompile := interpreter.evm.precompile(address); isPrecompile || interpreter.evm.isSystemContract(address) { + if !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) { + return nil, ErrExecutionReverted + } + } else { chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas) if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { return nil, ErrExecutionReverted @@ -774,7 +778,7 @@ func chargeCallVariantEIP4762(evm *EVM, scope *ScopeContext) bool { if err != nil { return false } - if scope.Contract.UseGas(evm.callGasTemp) { + if !scope.Contract.UseGas(evm.callGasTemp) { return false } From 09be3aadd6c32142286f54642e3c0acdb4398e15 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 15:37:58 -0300 Subject: [PATCH 12/38] fix Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 43 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index d67231a27746..933804984eb8 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -760,28 +760,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } func chargeCallVariantEIP4762(evm *EVM, scope *ScopeContext) bool { - if evm.chainRules.IsEIP4762 { - address := common.Address(scope.Stack.Back(1).Bytes20()) - transfersValue := !scope.Stack.Back(2).IsZero() - - // If value is transferred, it is charged before 1/64th - // is subtracted from the available gas pool. - if transfersValue { - if !evm.Accesses.TouchAndChargeValueTransfer(scope.Contract.Address().Bytes()[:], address.Bytes()[:], scope.Contract.UseGas) { - return false - } - } - } - - var err error - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) - if err != nil { - return false - } - if !scope.Contract.UseGas(evm.callGasTemp) { - return false - } - target := common.Address(scope.Stack.Back(1).Bytes20()) if _, isPrecompile := evm.precompile(target); isPrecompile { return true @@ -800,6 +778,27 @@ func chargeCallVariantEIP4762(evm *EVM, scope *ScopeContext) bool { } func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if interpreter.evm.chainRules.IsEIP4762 { + address := common.Address(scope.Stack.Back(1).Bytes20()) + transfersValue := !scope.Stack.Back(2).IsZero() + + // If value is transferred, it is charged before 1/64th + // is subtracted from the available gas pool. + if transfersValue { + if !interpreter.evm.Accesses.TouchAndChargeValueTransfer(scope.Contract.Address().Bytes()[:], address.Bytes()[:], scope.Contract.UseGas) { + return nil, ErrExecutionReverted + } + } + } + + var err error + interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) + if err != nil { + return nil, err + } + if !scope.Contract.UseGas(interpreter.evm.callGasTemp) { + return nil, ErrOutOfGas + } if !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrExecutionReverted } From f147cdc384afc28cfa3f867f4739c4447731bcee Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 15:44:48 -0300 Subject: [PATCH 13/38] fix Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 933804984eb8..87e39dc1ac4e 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -762,7 +762,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] func chargeCallVariantEIP4762(evm *EVM, scope *ScopeContext) bool { target := common.Address(scope.Stack.Back(1).Bytes20()) if _, isPrecompile := evm.precompile(target); isPrecompile { - return true + return scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) } // The charging for the value transfer is done BEFORE subtracting // the 1/64th gas, as this is considered part of the CALL instruction. From b2e2fb8b3288e51e337cfd1908c1e405580375c2 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 15:52:14 -0300 Subject: [PATCH 14/38] fix Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 71daaa82edac..4e5391215a97 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -105,7 +105,11 @@ func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, useGasFn UseGasF } func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte, useGasFn UseGasFn) bool { - _, ok := aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) + _, ok := aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) + if !ok { + return false + } + _, ok = aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) return ok } From e98688ee996dd51303b63bbecdffc7d3f0ab07e6 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 15:56:51 -0300 Subject: [PATCH 15/38] fix Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 87e39dc1ac4e..9081506d672b 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -352,7 +352,11 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) address := slot.Bytes20() cs := uint64(interpreter.evm.StateDB.GetCodeSize(address)) if interpreter.evm.chainRules.IsVerkle { - if _, isPrecompile := interpreter.evm.precompile(address); !isPrecompile { + if _, isPrecompile := interpreter.evm.precompile(address); isPrecompile { + if !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) { + return nil, ErrExecutionReverted + } + } else { chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas) if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { return nil, ErrExecutionReverted From f29b1482b697d61d0cf8858fa167539cb9cb7efe Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 15:57:39 -0300 Subject: [PATCH 16/38] fix Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 9081506d672b..fed193981a9c 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -408,7 +408,11 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if interpreter.evm.chainRules.IsVerkle { addr := common.Address(a.Bytes20()) - if _, isPrecompile := interpreter.evm.precompile(addr); !isPrecompile { + if _, isPrecompile := interpreter.evm.precompile(addr); isPrecompile { + if !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) { + return nil, ErrExecutionReverted + } + } else { chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(addr[:], false, scope.Contract.UseGas) if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { return nil, ErrExecutionReverted From 05e6092cbfc6162d5ed6e84faeb1a5313112f4b1 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 19 Sep 2024 16:37:27 -0300 Subject: [PATCH 17/38] fix Signed-off-by: Ignacio Hagopian --- core/vm/evm.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index bdcd7d09619e..ccf9ce4d4e36 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -528,11 +528,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, err = ErrCodeStoreOutOfGas } } else { - // Contract creation completed, touch the missing fields in the contract - if !evm.Accesses.TouchFullAccount(address.Bytes()[:], true, contract.UseGas) { - err = ErrCodeStoreOutOfGas - } - if err == nil && len(ret) > 0 && !evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true, contract.UseGas) { err = ErrCodeStoreOutOfGas } From fdb9c50985957e4678a4a7fcfbcb9a8db3585c0f Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Fri, 20 Sep 2024 16:13:19 -0300 Subject: [PATCH 18/38] fix Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index fed193981a9c..81bb11cf5e2d 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -480,7 +480,7 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) return nil, ErrExecutionReverted } } else { - chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas) + chargedGas, ok := interpreter.evm.Accesses.TouchCodeHash(address[:], false, scope.Contract.UseGas) if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { return nil, ErrExecutionReverted } From 9ea37be11a71bd019dd4e77ab3e86c05ea6c58a6 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 23 Sep 2024 15:56:40 -0300 Subject: [PATCH 19/38] simplify warm costs Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 20 ++++++++++++-------- core/state_processor.go | 2 +- core/vm/instructions.go | 31 ++++++++++++------------------- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 4e5391215a97..7fdfcec6fe50 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -100,8 +100,9 @@ func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool, useGasFn Us return true } -func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, useGasFn UseGasFn) (uint64, bool) { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn) +func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, useGasFn UseGasFn) bool { + chargedGas, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, useGasFn) + return ok && (chargedGas > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) } func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte, useGasFn UseGasFn) bool { @@ -146,9 +147,10 @@ func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsVa aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, nil) } -func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool, useGasFn UseGasFn) (uint64, bool) { +func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool, useGasFn UseGasFn, warmCostCharging bool) bool { treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slot.Bytes()) - return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, useGasFn) + chargedGas, ok := aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, useGasFn) + return ok && (!warmCostCharging || chargedGas > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) } func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool, useGasFn UseGasFn) (uint64, bool) { @@ -272,10 +274,12 @@ func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, s return true } -func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool, useGasFn UseGasFn) (uint64, bool) { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, useGasFn) +func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool, useGasFn UseGasFn, warmCostCharging bool) bool { + chargedGas, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, useGasFn) + return ok && (!warmCostCharging || chargedGas > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) } -func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool, useGasFn UseGasFn) (uint64, bool) { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, useGasFn) +func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool, useGasFn UseGasFn) bool { + chargedGas, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, useGasFn) + return ok && (chargedGas > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) } diff --git a/core/state_processor.go b/core/state_processor.go index cd561c7ab2c8..aa584845355a 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -191,5 +191,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, nil) + statedb.Witness().TouchSlotAndChargeGas(params.HistoryStorageAddress[:], key, true, nil, false) } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 81bb11cf5e2d..6006ab52b5aa 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -262,8 +262,7 @@ func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) if interpreter.evm.chainRules.IsVerkle { - chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas) - if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + if !interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas, true) { return nil, ErrExecutionReverted } } @@ -357,8 +356,7 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) return nil, ErrExecutionReverted } } else { - chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas) - if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + if !interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas, true) { return nil, ErrExecutionReverted } } @@ -413,8 +411,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) return nil, ErrExecutionReverted } } else { - chargedGas, ok := interpreter.evm.Accesses.TouchBasicData(addr[:], false, scope.Contract.UseGas) - if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + if !interpreter.evm.Accesses.TouchBasicData(addr[:], false, scope.Contract.UseGas, true) { return nil, ErrExecutionReverted } } @@ -480,8 +477,7 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) return nil, ErrExecutionReverted } } else { - chargedGas, ok := interpreter.evm.Accesses.TouchCodeHash(address[:], false, scope.Contract.UseGas) - if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + if !interpreter.evm.Accesses.TouchCodeHash(address[:], false, scope.Contract.UseGas) { return nil, ErrExecutionReverted } } @@ -523,7 +519,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( ringIndex := num64 % params.Eip2935BlockHashHistorySize var pnum common.Hash binary.BigEndian.PutUint64(pnum[24:], ringIndex) - if _, ok := evm.Accesses.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false, scope.Contract.UseGas); !ok { + if !evm.Accesses.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false, scope.Contract.UseGas, false) { return nil, ErrExecutionReverted } blockHash := evm.StateDB.GetState(params.HistoryStorageAddress, pnum) @@ -599,8 +595,7 @@ func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by loc := scope.Stack.peek() hash := common.Hash(loc.Bytes32()) if interpreter.evm.chainRules.IsVerkle { - chargedGas, ok := interpreter.evm.Accesses.TouchSlotAndChargeGas(scope.Contract.Address().Bytes(), loc.Bytes32(), false, scope.Contract.UseGas) - if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + if !interpreter.evm.Accesses.TouchSlotAndChargeGas(scope.Contract.Address().Bytes(), loc.Bytes32(), false, scope.Contract.UseGas, true) { return nil, ErrExecutionReverted } } @@ -616,8 +611,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b loc := scope.Stack.pop() val := scope.Stack.pop() if interpreter.evm.chainRules.IsVerkle { - chargedGas, ok := interpreter.evm.Accesses.TouchSlotAndChargeGas(scope.Contract.Address().Bytes(), loc.Bytes32(), true, scope.Contract.UseGas) - if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + if !interpreter.evm.Accesses.TouchSlotAndChargeGas(scope.Contract.Address().Bytes(), loc.Bytes32(), true, scope.Contract.UseGas, true) { return nil, ErrExecutionReverted } } @@ -777,8 +771,7 @@ func chargeCallVariantEIP4762(evm *EVM, scope *ScopeContext) bool { // (so before we get to this point) // But the message call is part of the subcall, for which only 63/64th // of the gas should be available. - chargedGas, ok := evm.Accesses.TouchAndChargeMessageCall(target.Bytes(), scope.Contract.UseGas) - if !ok || (chargedGas == 0 && !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929)) { + if !evm.Accesses.TouchAndChargeMessageCall(target.Bytes(), scope.Contract.UseGas) { return false } return true @@ -994,25 +987,25 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon beneficiaryAddr := common.Address(scope.Stack.peek().Bytes20()) contractAddr := scope.Contract.Address() - if _, ok := interpreter.evm.Accesses.TouchBasicData(contractAddr[:], false, scope.Contract.UseGas); !ok { + if !interpreter.evm.Accesses.TouchBasicData(contractAddr[:], false, scope.Contract.UseGas, false) { return nil, ErrExecutionReverted } balanceIsZero := interpreter.evm.StateDB.GetBalance(contractAddr).Sign() == 0 if _, isPrecompile := interpreter.evm.precompile(beneficiaryAddr); !(isPrecompile && balanceIsZero) { if contractAddr != beneficiaryAddr { - if _, ok := interpreter.evm.Accesses.TouchBasicData(beneficiaryAddr[:], false, scope.Contract.UseGas); !ok { + if !interpreter.evm.Accesses.TouchBasicData(beneficiaryAddr[:], false, scope.Contract.UseGas, false) { return nil, ErrExecutionReverted } } // Charge write costs if it transfers value if !balanceIsZero { - if _, ok := interpreter.evm.Accesses.TouchBasicData(contractAddr[:], true, scope.Contract.UseGas); !ok { + if !interpreter.evm.Accesses.TouchBasicData(contractAddr[:], true, scope.Contract.UseGas, false) { return nil, ErrExecutionReverted } if contractAddr != beneficiaryAddr { if interpreter.evm.StateDB.Exist(beneficiaryAddr) { - if _, ok := interpreter.evm.Accesses.TouchBasicData(beneficiaryAddr[:], true, scope.Contract.UseGas); !ok { + if !interpreter.evm.Accesses.TouchBasicData(beneficiaryAddr[:], true, scope.Contract.UseGas, false) { return nil, ErrExecutionReverted } } else { From 83aca339e6653e03b42476310f000012285a726c Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Wed, 25 Sep 2024 10:15:29 -0300 Subject: [PATCH 20/38] cleanup Signed-off-by: Ignacio Hagopian --- core/vm/evm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index ccf9ce4d4e36..1d12edafc437 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -528,7 +528,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, err = ErrCodeStoreOutOfGas } } else { - if err == nil && len(ret) > 0 && !evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true, contract.UseGas) { + if len(ret) > 0 && !evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true, contract.UseGas) { err = ErrCodeStoreOutOfGas } } From ffc1f2de44ba2c8fa7417bae871cd02176decbd8 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Fri, 27 Sep 2024 10:10:32 -0300 Subject: [PATCH 21/38] commit statedb Signed-off-by: Ignacio Hagopian --- cmd/evm/internal/t8ntool/execution.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 219f0b80c18a..04bef1b27f2e 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -451,6 +451,7 @@ func MakePreState(db ethdb.Database, chainConfig *params.ChainConfig, pre *Prest codeHash := crypto.Keccak256Hash(acc.Code) rawdb.WriteCode(codeWriter, codeHash, acc.Code) } + statedb.Commit(0, false) return statedb } From a00d50aa1590d7bed660723e2aa2fc7e58d64559 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 1 Oct 2024 15:58:26 -0300 Subject: [PATCH 22/38] add proper verkle flag Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 6006ab52b5aa..ff4dc65d215d 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -800,7 +800,8 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if !scope.Contract.UseGas(interpreter.evm.callGasTemp) { return nil, ErrOutOfGas } - if !chargeCallVariantEIP4762(interpreter.evm, scope) { + + if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrExecutionReverted } @@ -845,7 +846,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - if !chargeCallVariantEIP4762(interpreter.evm, scope) { + if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrExecutionReverted } // Pop gas. The actual gas is in interpreter.evm.callGasTemp. @@ -883,7 +884,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - if !chargeCallVariantEIP4762(interpreter.evm, scope) { + if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrExecutionReverted } stack := scope.Stack @@ -914,7 +915,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - if !chargeCallVariantEIP4762(interpreter.evm, scope) { + if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrExecutionReverted } // Pop gas. The actual gas is in interpreter.evm.callGasTemp. From 85196cc582dfedbe25e9e60cbfd93ffb97b2cb47 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 8 Oct 2024 08:45:39 -0300 Subject: [PATCH 23/38] spec bugfixes (#505) * fix Signed-off-by: Ignacio Hagopian * eip4762: push witness fix Signed-off-by: Ignacio Hagopian --------- Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index ff4dc65d215d..063901aca2fb 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -763,7 +763,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] func chargeCallVariantEIP4762(evm *EVM, scope *ScopeContext) bool { target := common.Address(scope.Stack.Back(1).Bytes20()) - if _, isPrecompile := evm.precompile(target); isPrecompile { + if _, isPrecompile := evm.precompile(target); isPrecompile || evm.isSystemContract(target) { return scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) } // The charging for the value transfer is done BEFORE subtracting @@ -1078,7 +1078,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 - if !interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.UseGas) { + if !interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(codeAddr[:], *pc, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.UseGas) { scope.Contract.Gas = 0 return nil, ErrOutOfGas } From 8b106f246b8962e0a4d13a559426b2af1f164a15 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 8 Oct 2024 16:05:22 -0300 Subject: [PATCH 24/38] selfdestruct: avoid BASIC_DATA inclusion for system contracts (#506) * fix Signed-off-by: Ignacio Hagopian * nit Signed-off-by: Ignacio Hagopian --------- Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 063901aca2fb..bfbf60c81e62 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -991,9 +991,11 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon if !interpreter.evm.Accesses.TouchBasicData(contractAddr[:], false, scope.Contract.UseGas, false) { return nil, ErrExecutionReverted } - balanceIsZero := interpreter.evm.StateDB.GetBalance(contractAddr).Sign() == 0 - if _, isPrecompile := interpreter.evm.precompile(beneficiaryAddr); !(isPrecompile && balanceIsZero) { + balanceIsZero := interpreter.evm.StateDB.GetBalance(contractAddr).Sign() == 0 + _, isPrecompile := interpreter.evm.precompile(beneficiaryAddr) + isSystemContract := interpreter.evm.isSystemContract(beneficiaryAddr) + if (!isPrecompile && !isSystemContract) || !balanceIsZero { if contractAddr != beneficiaryAddr { if !interpreter.evm.Accesses.TouchBasicData(beneficiaryAddr[:], false, scope.Contract.UseGas, false) { return nil, ErrExecutionReverted From efabf9503870c29b56316666dd310f2384c2cebb Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 8 Oct 2024 16:26:10 -0300 Subject: [PATCH 25/38] ci: new workflows for testing (#504) * ci: new workflows for testing Signed-off-by: Ignacio Hagopian * nit Signed-off-by: Ignacio Hagopian * ci: fix stable consumption Signed-off-by: Ignacio Hagopian * fix Signed-off-by: Ignacio Hagopian * more fixes Signed-off-by: Ignacio Hagopian * more fixes Signed-off-by: Ignacio Hagopian * update tag Signed-off-by: Ignacio Hagopian --------- Signed-off-by: Ignacio Hagopian --- .github/workflows/spec-tests-branch.yml | 22 +++++++--------------- .github/workflows/stable-spec-tests.yml | 15 +++++---------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/.github/workflows/spec-tests-branch.yml b/.github/workflows/spec-tests-branch.yml index ce739a7800fb..9648f6db699f 100644 --- a/.github/workflows/spec-tests-branch.yml +++ b/.github/workflows/spec-tests-branch.yml @@ -4,7 +4,7 @@ on: push: branches: [master] pull_request: - branches: [master, kaustinen-with-shapella] + branches: [master, kaustinen-with-shapella, jsign-witness-fix] workflow_dispatch: env: @@ -61,17 +61,13 @@ jobs: - name: Clone execution-spec-tests and fill tests run: | - git clone https://github.com/${{ env.EEST_USER }}/execution-spec-tests -b ${{ env.EEST_BRANCH }} + curl -LsSf https://astral.sh/uv/install.sh | sh + git clone https://github.com/${{ env.EEST_USER }}/execution-spec-tests -b ${{ env.EEST_BRANCH }} --depth 1 cd execution-spec-tests - python3 -m venv venv - . venv/bin/activate - pip install --upgrade pip - pip install -e ".[docs,lint,test]" - solc-select use 0.8.24 --always-install if [ "${{ matrix.test-type }}" == "genesis" ]; then - fill --fork Verkle --output=../fixtures-${{ matrix.test-type }} -v -m blockchain_test -n auto + uv run fill --evm-bin="${{ github.workspace }}/bin/evm" --fork Verkle --output=../fixtures-${{ matrix.test-type }} -v -m blockchain_test -n auto else - fill --from Shanghai --until EIP6800Transition --output=../fixtures-${{ matrix.test-type }} -v -m blockchain_test -n auto + uv run fill --evm-bin="${{ github.workspace }}/bin/evm" --from Shanghai --until EIP6800Transition --output=../fixtures-${{ matrix.test-type }} -v -m blockchain_test -n auto fi shell: bash @@ -107,12 +103,8 @@ jobs: - name: Clone execution-spec-tests and consume tests run: | + curl -LsSf https://astral.sh/uv/install.sh | sh git clone https://github.com/${{ env.EEST_USER }}/execution-spec-tests -b ${{ env.EEST_BRANCH }} cd execution-spec-tests - python3 -m venv venv - . venv/bin/activate - pip install --upgrade pip - pip install -e ".[docs,lint,test]" - solc-select use 0.8.24 --always-install - consume direct --input=../fixtures-${{ matrix.test-type }} -n auto + uv run consume direct --evm-bin="${{ github.workspace }}/bin/evm" --input=../fixtures-${{ matrix.test-type }} -n auto shell: bash diff --git a/.github/workflows/stable-spec-tests.yml b/.github/workflows/stable-spec-tests.yml index 7aa64a11d695..cc6bd3bc100c 100644 --- a/.github/workflows/stable-spec-tests.yml +++ b/.github/workflows/stable-spec-tests.yml @@ -4,11 +4,11 @@ on: push: branches: [master] pull_request: - branches: [master, kaustinen-with-shapella] + branches: [master, kaustinen-with-shapella, jsign-witness-fix] workflow_dispatch: env: - FIXTURES_TAG: "verkle@v0.0.3" + FIXTURES_TAG: "verkle@v0.0.4" jobs: setup: @@ -71,13 +71,8 @@ jobs: extract: true - name: Clone execution-spec-tests and consume tests run: | - git clone https://github.com/ethereum/execution-spec-tests + curl -LsSf https://astral.sh/uv/install.sh | sh + git clone https://github.com/ethereum/execution-spec-tests -b ${{ env.FIXTURES_TAG }} --depth 1 cd execution-spec-tests - git checkout 250a064ba38cd92cad02691f4f7c2ecbefedd954 - python3 -m venv venv - . venv/bin/activate - pip install --upgrade pip - pip install -e ".[docs,lint,test]" - solc-select use 0.8.24 --always-install - consume direct --input=../fixtures -n auto + uv run consume direct --evm-bin="${{ github.workspace }}/bin/evm" --input=../fixtures -n auto shell: bash From 51dca93bb04a4f93a2be9422a56ee8ea91b94efa Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Wed, 9 Oct 2024 13:34:07 -0300 Subject: [PATCH 26/38] eip4762: change returned error for witness oog (#507) Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index bfbf60c81e62..1155564ab5dc 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -263,7 +263,7 @@ func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] address := common.Address(slot.Bytes20()) if interpreter.evm.chainRules.IsVerkle { if !interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas, true) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address)) @@ -353,11 +353,11 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if interpreter.evm.chainRules.IsVerkle { if _, isPrecompile := interpreter.evm.precompile(address); isPrecompile { if !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } else { if !interpreter.evm.Accesses.TouchBasicData(address[:], false, scope.Contract.UseGas, true) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } } @@ -408,11 +408,11 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) addr := common.Address(a.Bytes20()) if _, isPrecompile := interpreter.evm.precompile(addr); isPrecompile { if !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } else { if !interpreter.evm.Accesses.TouchBasicData(addr[:], false, scope.Contract.UseGas, true) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } } @@ -474,11 +474,11 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if interpreter.evm.chainRules.IsVerkle { if _, isPrecompile := interpreter.evm.precompile(address); isPrecompile || interpreter.evm.isSystemContract(address) { if !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } else { if !interpreter.evm.Accesses.TouchCodeHash(address[:], false, scope.Contract.UseGas) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } } @@ -520,7 +520,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( var pnum common.Hash binary.BigEndian.PutUint64(pnum[24:], ringIndex) if !evm.Accesses.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false, scope.Contract.UseGas, false) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } blockHash := evm.StateDB.GetState(params.HistoryStorageAddress, pnum) num.SetBytes(blockHash.Bytes()) @@ -596,7 +596,7 @@ func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by hash := common.Hash(loc.Bytes32()) if interpreter.evm.chainRules.IsVerkle { if !interpreter.evm.Accesses.TouchSlotAndChargeGas(scope.Contract.Address().Bytes(), loc.Bytes32(), false, scope.Contract.UseGas, true) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash) @@ -612,7 +612,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b val := scope.Stack.pop() if interpreter.evm.chainRules.IsVerkle { if !interpreter.evm.Accesses.TouchSlotAndChargeGas(scope.Contract.Address().Bytes(), loc.Bytes32(), true, scope.Contract.UseGas, true) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } @@ -787,7 +787,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt // is subtracted from the available gas pool. if transfersValue { if !interpreter.evm.Accesses.TouchAndChargeValueTransfer(scope.Contract.Address().Bytes()[:], address.Bytes()[:], scope.Contract.UseGas) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } } @@ -802,7 +802,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } stack := scope.Stack @@ -847,7 +847,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack @@ -885,7 +885,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } stack := scope.Stack // Pop gas. The actual gas is in interpreter.evm.callGasTemp. @@ -916,7 +916,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack @@ -989,7 +989,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon contractAddr := scope.Contract.Address() if !interpreter.evm.Accesses.TouchBasicData(contractAddr[:], false, scope.Contract.UseGas, false) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } balanceIsZero := interpreter.evm.StateDB.GetBalance(contractAddr).Sign() == 0 @@ -998,22 +998,22 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon if (!isPrecompile && !isSystemContract) || !balanceIsZero { if contractAddr != beneficiaryAddr { if !interpreter.evm.Accesses.TouchBasicData(beneficiaryAddr[:], false, scope.Contract.UseGas, false) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } // Charge write costs if it transfers value if !balanceIsZero { if !interpreter.evm.Accesses.TouchBasicData(contractAddr[:], true, scope.Contract.UseGas, false) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } if contractAddr != beneficiaryAddr { if interpreter.evm.StateDB.Exist(beneficiaryAddr) { if !interpreter.evm.Accesses.TouchBasicData(beneficiaryAddr[:], true, scope.Contract.UseGas, false) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } else { if !interpreter.evm.Accesses.TouchFullAccount(beneficiaryAddr[:], true, scope.Contract.UseGas) { - return nil, ErrExecutionReverted + return nil, ErrOutOfGas } } } From b32bca96f6678cb7347e347349075e3abf745a21 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 10 Oct 2024 13:42:40 -0300 Subject: [PATCH 27/38] eip4762: move *CALL BASIC_DATA charging before reservation Signed-off-by: Ignacio Hagopian --- core/vm/gas_table.go | 24 +----------------------- core/vm/instructions.go | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 27233cc0ddbe..672b7d02b542 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -421,13 +421,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) - if err != nil { - return 0, err - } - if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { - return 0, ErrGasUintOverflow - } + return gas, nil } @@ -436,14 +430,6 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me if err != nil { return 0, err } - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) - if err != nil { - return 0, err - } - var overflow bool - if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { - return 0, ErrGasUintOverflow - } return gas, nil } @@ -452,14 +438,6 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo if err != nil { return 0, err } - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) - if err != nil { - return 0, err - } - var overflow bool - if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { - return 0, ErrGasUintOverflow - } return gas, nil } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 1155564ab5dc..67aa303b5f23 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -792,6 +792,10 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } } + if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { + return nil, ErrOutOfGas + } + var err error interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { @@ -801,10 +805,6 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt return nil, ErrOutOfGas } - if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { - return nil, ErrOutOfGas - } - stack := scope.Stack // Pop gas. The actual gas in interpreter.evm.callGasTemp. // We can use this as a temporary value @@ -849,6 +849,15 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } + var err error + interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) + if err != nil { + return nil, err + } + if !scope.Contract.UseGas(interpreter.evm.callGasTemp) { + return nil, ErrOutOfGas + } + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack // We use it as a temporary value @@ -887,6 +896,15 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } + var err error + interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) + if err != nil { + return nil, err + } + if !scope.Contract.UseGas(interpreter.evm.callGasTemp) { + return nil, ErrOutOfGas + } + stack := scope.Stack // Pop gas. The actual gas is in interpreter.evm.callGasTemp. // We use it as a temporary value @@ -918,6 +936,15 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } + var err error + interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) + if err != nil { + return nil, err + } + if !scope.Contract.UseGas(interpreter.evm.callGasTemp) { + return nil, ErrOutOfGas + } + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack // We use it as a temporary value From 26452f451f579d1c8ed073b0935869e7ee5bc8df Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Fri, 11 Oct 2024 10:58:41 -0300 Subject: [PATCH 28/38] CALL witness charge ordering change Signed-off-by: Ignacio Hagopian --- core/vm/eips.go | 8 +++--- core/vm/gas_table.go | 4 +++ core/vm/instructions.go | 59 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 73fbf703c0b7..99d884e0ae37 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -322,11 +322,11 @@ func enable4762(jt *JumpTable) { jt[CREATE].constantGas = params.CreateNGasEip4762 jt[CREATE2].constantGas = params.CreateNGasEip4762 jt[CALL].constantGas = 0 - jt[CALL].dynamicGas = gasCall + jt[CALL].dynamicGas = gasZero jt[CALLCODE].constantGas = 0 - jt[CALLCODE].dynamicGas = gasCallCode + jt[CALLCODE].dynamicGas = gasZero jt[STATICCALL].constantGas = 0 - jt[STATICCALL].dynamicGas = gasStaticCall + jt[STATICCALL].dynamicGas = gasZero jt[DELEGATECALL].constantGas = 0 - jt[DELEGATECALL].dynamicGas = gasDelegateCall + jt[DELEGATECALL].dynamicGas = gasZero } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 672b7d02b542..6fbc6e7dd299 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -425,6 +425,10 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory return gas, nil } +func gasZero(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return 0, nil +} + func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { gas, err := memoryGasCost(mem, memorySize) if err != nil { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 67aa303b5f23..0d442fe96bf0 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -20,6 +20,7 @@ import ( "encoding/binary" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" @@ -778,6 +779,27 @@ func chargeCallVariantEIP4762(evm *EVM, scope *ScopeContext) bool { } +func getMemSize(operation *operation, stack *Stack) (uint64, error) { + // All ops with a dynamic memory usage also has a dynamic gas cost. + var memorySize uint64 + // calculate the new memory size and expand the memory to fit + // the operation + // Memory check needs to be done prior to evaluating the dynamic gas portion, + // to detect calculation overflows + if operation.memorySize != nil { + memSize, overflow := operation.memorySize(stack) + if overflow { + return 0, ErrGasUintOverflow + } + // memory is expanded in words of 32 bytes. Gas + // is also calculated in words. + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return 0, ErrGasUintOverflow + } + } + return memorySize, nil +} + func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { if interpreter.evm.chainRules.IsEIP4762 { address := common.Address(scope.Stack.Back(1).Bytes20()) @@ -795,8 +817,15 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } + memSize, err := getMemSize(interpreter.table[CALL], scope.Stack) + if err != nil { + return nil, err + } + dynamicCost, err := gasCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) + if err != nil || !scope.Contract.UseGas(dynamicCost) { + return nil, ErrOutOfGas + } - var err error interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err @@ -849,7 +878,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } - var err error + memSize, err := getMemSize(interpreter.table[CALLCODE], scope.Stack) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallCode(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) + if err != nil || !scope.Contract.UseGas(dynamicCost) { + return nil, ErrOutOfGas + } interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err @@ -896,7 +932,14 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } - var err error + memSize, err := getMemSize(interpreter.table[DELEGATECALL], scope.Stack) + if err != nil { + return nil, err + } + dynamicCost, err := gasDelegateCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) + if err != nil || !scope.Contract.UseGas(dynamicCost) { + return nil, ErrOutOfGas + } interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err @@ -936,7 +979,15 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } - var err error + + memSize, err := getMemSize(interpreter.table[STATICCALL], scope.Stack) + if err != nil { + return nil, err + } + dynamicCost, err := gasCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) + if err != nil || !scope.Contract.UseGas(dynamicCost) { + return nil, ErrOutOfGas + } interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err From 4d4eabc8f705b49335287b4aadeca265018e7895 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Fri, 11 Oct 2024 22:24:35 -0300 Subject: [PATCH 29/38] criminal hacks Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 12 ++++++++++++ core/vm/interpreter.go | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0d442fe96bf0..65f9f120b623 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -825,6 +825,9 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if err != nil || !scope.Contract.UseGas(dynamicCost) { return nil, ErrOutOfGas } + if memSize > 0 { + scope.Memory.Resize(memSize) + } interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { @@ -886,6 +889,9 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ if err != nil || !scope.Contract.UseGas(dynamicCost) { return nil, ErrOutOfGas } + if memSize > 0 { + scope.Memory.Resize(memSize) + } interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err @@ -940,6 +946,9 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext if err != nil || !scope.Contract.UseGas(dynamicCost) { return nil, ErrOutOfGas } + if memSize > 0 { + scope.Memory.Resize(memSize) + } interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err @@ -988,6 +997,9 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if err != nil || !scope.Contract.UseGas(dynamicCost) { return nil, ErrOutOfGas } + if memSize > 0 { + scope.Memory.Resize(memSize) + } interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index c9a53584b19e..6d11df4625a5 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -233,7 +233,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } - if memorySize > 0 { + // TODO(hack): remove if conditions + if op != CALL && op != CALLCODE && op != DELEGATECALL && op != STATICCALL && memorySize > 0 { mem.Resize(memorySize) } } else if debug { From f4733cecc3341756d9226e9ce0f67cdf5bf095c1 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:25:12 +0200 Subject: [PATCH 30/38] fix: create init order redux (#510) Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- core/vm/evm.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 1d12edafc437..3a20b8f959ee 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -474,12 +474,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { return nil, common.Address{}, 0, ErrContractAddressCollision } - // Create a new account on the state - snapshot := evm.StateDB.Snapshot() - evm.StateDB.CreateAccount(address) - if evm.chainRules.IsEIP158 { - evm.StateDB.SetNonce(address, 1) - } // Charge the contract creation init gas in verkle mode if evm.chainRules.IsEIP4762 { @@ -489,6 +483,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } gas = gc.availableGas } + + // Create a new account on the state + snapshot := evm.StateDB.Snapshot() + evm.StateDB.CreateAccount(address) + if evm.chainRules.IsEIP158 { + evm.StateDB.SetNonce(address, 1) + } + evm.Context.Transfer(evm.StateDB, caller.Address(), address, value) // Initialise a new contract and set the code that is to be used by the EVM. From 4b91d84971c7b504bc49dbce77832e6ef7b7a8de Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 15 Oct 2024 13:09:47 -0300 Subject: [PATCH 31/38] Avoid unnecessary 100 gas cost in CALL & add missing write-event for CODEHASH (#508) * avoid extra 100 gas in value-bearing CALL Signed-off-by: Ignacio Hagopian * missing codeHash write event Signed-off-by: Ignacio Hagopian * feedback Signed-off-by: Ignacio Hagopian * Update core/vm/evm.go Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --------- Signed-off-by: Ignacio Hagopian Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- core/state/access_witness.go | 4 ++-- core/vm/evm.go | 11 ++++++++--- core/vm/instructions.go | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 7fdfcec6fe50..4a0c29111995 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -110,8 +110,8 @@ func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []by if !ok { return false } - _, ok = aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) - return ok + chargedGas, ok := aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) + return ok && (chargedGas > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) } // TouchAndChargeContractCreateCheck charges access costs before diff --git a/core/vm/evm.go b/core/vm/evm.go index 3a20b8f959ee..2ab9479e66d1 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -200,10 +200,15 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas debug := evm.Config.Tracer != nil if !evm.StateDB.Exist(addr) { - if !isPrecompile && evm.chainRules.IsEIP4762 { - // add proof of absence to witness + if !isPrecompile && evm.chainRules.IsEIP4762 && value.Sign() != 0 { gc := gasConsumer{availableGas: gas} - ok := evm.Accesses.TouchFullAccount(addr.Bytes(), false, gc.consumeGas) + // At this point, the read costs have already been charged, either because this + // is a direct tx call, in which case it's covered by the intrinsic gas, or because + // of a CALL instruction, in which case BASIC_DATA has been added to the access + // list in write mode. If there is enough gas paying for the addition of the code + // hash leaf to the access list, then account creation will proceed unimpaired. + // Thus, only pay for the creation of the code hash leaf here. + ok := evm.Accesses.TouchCodeHash(addr.Bytes(), true, gc.consumeGas) if !ok { evm.StateDB.RevertToSnapshot(snapshot) return nil, 0, ErrOutOfGas diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 65f9f120b623..fcf677db3835 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -801,9 +801,9 @@ func getMemSize(operation *operation, stack *Stack) (uint64, error) { } func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + transfersValue := !scope.Stack.Back(2).IsZero() if interpreter.evm.chainRules.IsEIP4762 { address := common.Address(scope.Stack.Back(1).Bytes20()) - transfersValue := !scope.Stack.Back(2).IsZero() // If value is transferred, it is charged before 1/64th // is subtracted from the available gas pool. @@ -814,7 +814,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } } - if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { + if interpreter.evm.chainRules.IsEIP4762 && !transfersValue && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } memSize, err := getMemSize(interpreter.table[CALL], scope.Stack) From 9635cc10b4e50d1e4bacd11b95c63aaf7b3f54f5 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 15 Oct 2024 16:05:56 -0300 Subject: [PATCH 32/38] filling fixes Signed-off-by: Ignacio Hagopian --- core/vm/instructions.go | 92 +++++++++++++++++++++++------------------ core/vm/interpreter.go | 2 +- 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index fcf677db3835..3a20e8c4f998 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -817,18 +817,21 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if interpreter.evm.chainRules.IsEIP4762 && !transfersValue && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } - memSize, err := getMemSize(interpreter.table[CALL], scope.Stack) - if err != nil { - return nil, err - } - dynamicCost, err := gasCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) - if err != nil || !scope.Contract.UseGas(dynamicCost) { - return nil, ErrOutOfGas - } - if memSize > 0 { - scope.Memory.Resize(memSize) + if interpreter.evm.chainRules.IsEIP4762 { + memSize, err := getMemSize(interpreter.table[CALL], scope.Stack) + if err != nil { + return nil, err + } + dynamicCost, err := gasCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) + if err != nil || !scope.Contract.UseGas(dynamicCost) { + return nil, ErrOutOfGas + } + if memSize > 0 { + scope.Memory.Resize(memSize) + } } + var err error interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err @@ -881,17 +884,20 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } - memSize, err := getMemSize(interpreter.table[CALLCODE], scope.Stack) - if err != nil { - return nil, err - } - dynamicCost, err := gasCallCode(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) - if err != nil || !scope.Contract.UseGas(dynamicCost) { - return nil, ErrOutOfGas - } - if memSize > 0 { - scope.Memory.Resize(memSize) + if interpreter.evm.chainRules.IsEIP4762 { + memSize, err := getMemSize(interpreter.table[CALLCODE], scope.Stack) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallCode(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) + if err != nil || !scope.Contract.UseGas(dynamicCost) { + return nil, ErrOutOfGas + } + if memSize > 0 { + scope.Memory.Resize(memSize) + } } + var err error interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err @@ -938,17 +944,20 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext if interpreter.evm.chainRules.IsEIP4762 && !chargeCallVariantEIP4762(interpreter.evm, scope) { return nil, ErrOutOfGas } - memSize, err := getMemSize(interpreter.table[DELEGATECALL], scope.Stack) - if err != nil { - return nil, err - } - dynamicCost, err := gasDelegateCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) - if err != nil || !scope.Contract.UseGas(dynamicCost) { - return nil, ErrOutOfGas - } - if memSize > 0 { - scope.Memory.Resize(memSize) + if interpreter.evm.chainRules.IsEIP4762 { + memSize, err := getMemSize(interpreter.table[DELEGATECALL], scope.Stack) + if err != nil { + return nil, err + } + dynamicCost, err := gasDelegateCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) + if err != nil || !scope.Contract.UseGas(dynamicCost) { + return nil, ErrOutOfGas + } + if memSize > 0 { + scope.Memory.Resize(memSize) + } } + var err error interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err @@ -989,17 +998,20 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) return nil, ErrOutOfGas } - memSize, err := getMemSize(interpreter.table[STATICCALL], scope.Stack) - if err != nil { - return nil, err - } - dynamicCost, err := gasCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) - if err != nil || !scope.Contract.UseGas(dynamicCost) { - return nil, ErrOutOfGas - } - if memSize > 0 { - scope.Memory.Resize(memSize) + if interpreter.evm.chainRules.IsEIP4762 { + memSize, err := getMemSize(interpreter.table[STATICCALL], scope.Stack) + if err != nil { + return nil, err + } + dynamicCost, err := gasStaticCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memSize) + if err != nil || !scope.Contract.UseGas(dynamicCost) { + return nil, ErrOutOfGas + } + if memSize > 0 { + scope.Memory.Resize(memSize) + } } + var err error interpreter.evm.callGasTemp, err = callGas(interpreter.evm.chainRules.IsEIP150, scope.Contract.Gas, 0, scope.Stack.Back(0)) if err != nil { return nil, err diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 6d11df4625a5..fed4067a07f9 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -234,7 +234,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( logged = true } // TODO(hack): remove if conditions - if op != CALL && op != CALLCODE && op != DELEGATECALL && op != STATICCALL && memorySize > 0 { + if (!in.evm.chainRules.IsEIP4762 || (op != CALL && op != CALLCODE && op != DELEGATECALL && op != STATICCALL)) && memorySize > 0 { mem.Resize(memorySize) } } else if debug { From 1ec805c8986e7df243c430037332177a6f84e42f Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Wed, 16 Oct 2024 16:08:03 -0300 Subject: [PATCH 33/38] More spec fixes (#511) * fix EXTCODESIZE rule for system contract Signed-off-by: Ignacio Hagopian * 21000 witness target write touch CODEHASH Signed-off-by: Ignacio Hagopian * fix EXTCODECOPY witness for system contract Signed-off-by: Ignacio Hagopian * rename and comment Signed-off-by: Ignacio Hagopian --------- Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 11 ++++++++--- core/state_transition.go | 2 +- core/vm/instructions.go | 6 ++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 4a0c29111995..0be2f5a3a9d7 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -142,9 +142,14 @@ func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) { } } -func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) { - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, nil) - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, nil) +func (aw *AccessWitness) TouchTxTarget(targetAddr []byte) { + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, nil) + // 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, true, nil) } func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool, useGasFn UseGasFn, warmCostCharging bool) bool { diff --git a/core/state_transition.go b/core/state_transition.go index 45a736d5e189..75d5c29573ce 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -396,7 +396,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.evm.Accesses.TouchTxOriginAndComputeGas(originAddr.Bytes()) if msg.To != nil { - st.evm.Accesses.TouchTxExistingAndComputeGas(targetAddr.Bytes(), msg.Value.Sign() != 0) + st.evm.Accesses.TouchTxTarget(targetAddr.Bytes()) // ensure the code size ends up in the access witness st.evm.StateDB.GetCodeSize(*targetAddr) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 3a20e8c4f998..912b9c583eeb 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -352,7 +352,8 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) address := slot.Bytes20() cs := uint64(interpreter.evm.StateDB.GetCodeSize(address)) if interpreter.evm.chainRules.IsVerkle { - if _, isPrecompile := interpreter.evm.precompile(address); isPrecompile { + isSystemContract := interpreter.evm.isSystemContract(address) + if _, isPrecompile := interpreter.evm.precompile(address); isPrecompile || isSystemContract { if !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) { return nil, ErrOutOfGas } @@ -407,7 +408,8 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if interpreter.evm.chainRules.IsVerkle { addr := common.Address(a.Bytes20()) - if _, isPrecompile := interpreter.evm.precompile(addr); isPrecompile { + isSystemContract := interpreter.evm.isSystemContract(addr) + if _, isPrecompile := interpreter.evm.precompile(addr); isPrecompile || isSystemContract { if !scope.Contract.UseGas(params.WarmStorageReadCostEIP2929) { return nil, ErrOutOfGas } From b2b96a265eed7ffade9cdfddb22949652eb67374 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Wed, 16 Oct 2024 16:19:16 -0300 Subject: [PATCH 34/38] nit Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 4 ++-- core/state_transition.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 0be2f5a3a9d7..24a1344e8e2b 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -142,8 +142,8 @@ func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) { } } -func (aw *AccessWitness) TouchTxTarget(targetAddr []byte) { - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, nil) +func (aw *AccessWitness) TouchTxTarget(targetAddr []byte, sendsValue bool) { + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, nil) // 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. diff --git a/core/state_transition.go b/core/state_transition.go index 75d5c29573ce..89424cf455fd 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -396,7 +396,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.evm.Accesses.TouchTxOriginAndComputeGas(originAddr.Bytes()) if msg.To != nil { - st.evm.Accesses.TouchTxTarget(targetAddr.Bytes()) + st.evm.Accesses.TouchTxTarget(targetAddr.Bytes(), msg.Value.Sign() != 0) // ensure the code size ends up in the access witness st.evm.StateDB.GetCodeSize(*targetAddr) From 8057173f7144c3801a6f3d566c942e571120a7a5 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 17 Oct 2024 07:29:17 -0300 Subject: [PATCH 35/38] extra fix (#512) Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 4 ++-- core/vm/evm.go | 4 ++-- core/vm/instructions.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 24a1344e8e2b..276485e2dd25 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -284,7 +284,7 @@ func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool, useGasFn UseG return ok && (!warmCostCharging || chargedGas > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) } -func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool, useGasFn UseGasFn) bool { +func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool, useGasFn UseGasFn, warmCostCharging bool) bool { chargedGas, ok := aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, useGasFn) - return ok && (chargedGas > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) + return ok && (!warmCostCharging || chargedGas > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) } diff --git a/core/vm/evm.go b/core/vm/evm.go index 2ab9479e66d1..f70f3552024a 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -206,9 +206,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // is a direct tx call, in which case it's covered by the intrinsic gas, or because // of a CALL instruction, in which case BASIC_DATA has been added to the access // list in write mode. If there is enough gas paying for the addition of the code - // hash leaf to the access list, then account creation will proceed unimpaired. + // hash leaf to the access list, then account creation will proceed unimpaired. // Thus, only pay for the creation of the code hash leaf here. - ok := evm.Accesses.TouchCodeHash(addr.Bytes(), true, gc.consumeGas) + ok := evm.Accesses.TouchCodeHash(addr.Bytes(), true, gc.consumeGas, false) if !ok { evm.StateDB.RevertToSnapshot(snapshot) return nil, 0, ErrOutOfGas diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 912b9c583eeb..412093b1bb04 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -480,7 +480,7 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) return nil, ErrOutOfGas } } else { - if !interpreter.evm.Accesses.TouchCodeHash(address[:], false, scope.Contract.UseGas) { + if !interpreter.evm.Accesses.TouchCodeHash(address[:], false, scope.Contract.UseGas, true) { return nil, ErrOutOfGas } } From 59426d92fb975a282c66dde20e0be4493e28bacd Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Thu, 17 Oct 2024 09:58:59 -0300 Subject: [PATCH 36/38] avoid BASIC_DATA branch edit for free (#513) Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 7 +------ core/state_transition.go | 3 +++ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 276485e2dd25..152c47cf3df0 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -144,12 +144,7 @@ func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) { func (aw *AccessWitness) TouchTxTarget(targetAddr []byte, sendsValue bool) { aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, nil) - // 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, true, nil) + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, nil) } func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool, useGasFn UseGasFn, warmCostCharging bool) bool { diff --git a/core/state_transition.go b/core/state_transition.go index 89424cf455fd..9f35b8d3a94a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -397,6 +397,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if msg.To != nil { st.evm.Accesses.TouchTxTarget(targetAddr.Bytes(), msg.Value.Sign() != 0) + if !st.state.Exist(*targetAddr) { + st.evm.Accesses.TouchCodeHash(targetAddr.Bytes(), true, nil, false) + } // ensure the code size ends up in the access witness st.evm.StateDB.GetCodeSize(*targetAddr) From 4694243826e6b93163c0b1fa3e5a76e4244c0a6f Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Fri, 18 Oct 2024 17:37:04 -0300 Subject: [PATCH 37/38] ci: stable fixtures target v0.0.5 (#514) Signed-off-by: Ignacio Hagopian --- .github/workflows/stable-spec-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stable-spec-tests.yml b/.github/workflows/stable-spec-tests.yml index cc6bd3bc100c..0b34773d5b0f 100644 --- a/.github/workflows/stable-spec-tests.yml +++ b/.github/workflows/stable-spec-tests.yml @@ -8,7 +8,7 @@ on: workflow_dispatch: env: - FIXTURES_TAG: "verkle@v0.0.4" + FIXTURES_TAG: "verkle@v0.0.5" jobs: setup: From c329550addc41301f7fb1482390e3381c375f25f Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 22 Oct 2024 16:45:14 -0300 Subject: [PATCH 38/38] fix Signed-off-by: Ignacio Hagopian --- core/state/access_witness.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 152c47cf3df0..2c05a3f76a78 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -106,12 +106,12 @@ func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte, useGasFn UseGasF } func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte, useGasFn UseGasFn) bool { - _, ok := aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) + chargedGas1, ok := aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) if !ok { return false } - chargedGas, ok := aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) - return ok && (chargedGas > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) + chargedGas2, ok := aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, useGasFn) + return ok && (chargedGas1+chargedGas2 > 0 || useGasFn(params.WarmStorageReadCostEIP2929)) } // TouchAndChargeContractCreateCheck charges access costs before