diff --git a/core/state/statedb.go b/core/state/statedb.go index 49773021a76a..0828ae95060a 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -288,6 +288,24 @@ func (s *StateDB) GetBalance(addr common.Address) *big.Int { return common.Big0 } +func (s *StateDB) GetNonceLittleEndian(address common.Address) []byte { + var nonceBytes [8]byte + binary.LittleEndian.PutUint64(nonceBytes[:], s.GetNonce(address)) + return nonceBytes[:] +} + +func (s *StateDB) GetBalanceLittleEndian(address common.Address) []byte { + var paddedBalance [32]byte + balanceBytes := s.GetBalance(address).Bytes() + // swap to little-endian + for i, j := 0, len(balanceBytes)-1; i < j; i, j = i+1, j-1 { + balanceBytes[i], balanceBytes[j] = balanceBytes[j], balanceBytes[i] + } + + copy(paddedBalance[:len(balanceBytes)], balanceBytes) + return paddedBalance[:len(balanceBytes)] +} + func (s *StateDB) GetNonce(addr common.Address) uint64 { stateObject := s.getStateObject(addr) if stateObject != nil { @@ -486,20 +504,27 @@ func (s *StateDB) updateStateObject(obj *stateObject) { if err := s.trie.TryUpdateAccount(addr[:], &obj.data); err != nil { s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err)) } - if len(obj.code) > 0 && s.trie.IsVerkle() { - cs := make([]byte, 32) - binary.BigEndian.PutUint64(cs, uint64(len(obj.code))) - if err := s.trie.TryUpdate(trieUtils.GetTreeKeyCodeSize(addr[:]), cs); err != nil { - s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err)) - } + if s.trie.IsVerkle() { + if len(obj.code) > 0 { + cs := make([]byte, 32) + binary.BigEndian.PutUint64(cs, uint64(len(obj.code))) + if err := s.trie.TryUpdate(trieUtils.GetTreeKeyCodeSize(addr[:]), cs); err != nil { + s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err)) + } - if obj.dirtyCode { - if chunks, err := trie.ChunkifyCode(addr, obj.code); err == nil { - for i := range chunks { - s.trie.TryUpdate(trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(uint64(i))), chunks[i][:]) + if obj.dirtyCode { + if chunks, err := trie.ChunkifyCode(addr, obj.code); err == nil { + for i := range chunks { + s.trie.TryUpdate(trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(uint64(i))), chunks[i][:]) + } + } else { + s.setError(err) } - } else { - s.setError(err) + } + } else { + cs := []byte{0} + if err := s.trie.TryUpdate(trieUtils.GetTreeKeyCodeSize(addr[:]), cs); err != nil { + s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err)) } } } diff --git a/core/state_transition.go b/core/state_transition.go index 19e52d8e0c71..f11a236edd7f 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" - trieUtils "github.com/ethereum/go-ethereum/trie/utils" ) var emptyCodeHash = crypto.Keccak256Hash(nil) @@ -261,6 +260,19 @@ 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. // @@ -305,25 +317,33 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas) } if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber) { + var targetBalance, targetNonce, targetCodeSize, targetCodeKeccak, originBalance, originNonce []byte + + targetAddr := msg.To() + originAddr := msg.From() + + statelessGasOrigin := st.evm.Accesses.TouchTxOriginAndChargeGas(originAddr.Bytes()) + if !tryConsumeGas(&st.gas, statelessGasOrigin) { + return nil, fmt.Errorf("insufficient gas to cover witness access costs") + } + originBalance = st.evm.StateDB.GetBalanceLittleEndian(originAddr) + originNonce = st.evm.StateDB.GetNonceLittleEndian(originAddr) + st.evm.Accesses.SetTxTouchedLeaves(originAddr.Bytes(), originBalance, originNonce) + if msg.To() != nil { - toBalance := trieUtils.GetTreeKeyBalance(msg.To().Bytes()) - pre := st.state.GetBalance(*msg.To()) - gas += st.evm.Accesses.TouchAddressAndChargeGas(toBalance, pre.Bytes()) - - // NOTE: Nonce also needs to be charged, because it is needed for execution - // on the statless side. - var preTN [8]byte - fromNonce := trieUtils.GetTreeKeyNonce(msg.To().Bytes()) - binary.BigEndian.PutUint64(preTN[:], st.state.GetNonce(*msg.To())) - gas += st.evm.Accesses.TouchAddressAndChargeGas(fromNonce, preTN[:]) + statelessGasDest := st.evm.Accesses.TouchTxExistingAndChargeGas(targetAddr.Bytes()) + if !tryConsumeGas(&st.gas, statelessGasDest) { + return nil, fmt.Errorf("insufficient gas to cover witness access costs") + } + targetBalance = st.evm.StateDB.GetBalanceLittleEndian(*targetAddr) + targetNonce = st.evm.StateDB.GetNonceLittleEndian(*targetAddr) + targetCodeKeccak = st.evm.StateDB.GetCodeHash(*targetAddr).Bytes() + + codeSize := uint64(st.evm.StateDB.GetCodeSize(*targetAddr)) + var codeSizeBytes [32]byte + binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize) + st.evm.Accesses.SetTxExistingTouchedLeaves(targetAddr.Bytes(), targetBalance, targetNonce, targetCodeSize, targetCodeKeccak) } - fromBalance := trieUtils.GetTreeKeyBalance(msg.From().Bytes()) - preFB := st.state.GetBalance(msg.From()).Bytes() - fromNonce := trieUtils.GetTreeKeyNonce(msg.From().Bytes()) - var preFN [8]byte - binary.BigEndian.PutUint64(preFN[:], st.state.GetNonce(msg.From())) - gas += st.evm.Accesses.TouchAddressAndChargeGas(fromNonce, preFN[:]) - gas += st.evm.Accesses.TouchAddressAndChargeGas(fromBalance, preFB[:]) } st.gas -= gas diff --git a/core/types/access_witness.go b/core/types/access_witness.go index 3fe6b78dc555..b05ba9e43a67 100644 --- a/core/types/access_witness.go +++ b/core/types/access_witness.go @@ -19,6 +19,7 @@ package types import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" ) // AccessWitness lists the locations of the state that are being accessed @@ -45,6 +46,125 @@ func NewAccessWitness() *AccessWitness { } } +// TODO TouchAndCharge + SetLeafValue* does redundant calls to GetTreeKey* + +func (aw *AccessWitness) TouchAndChargeProofOfAbsence(addr []byte) uint64 { + var gas uint64 + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr[:]), nil) + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(addr[:]), nil) + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(addr[:]), nil) + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeKeccak(addr[:]), nil) + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(addr[:]), nil) + return gas +} + +func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte) uint64 { + var gas uint64 + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr[:]), nil) + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(addr[:]), nil) + return gas +} + +func (aw *AccessWitness) SetLeafValuesMessageCall(addr, codeSize []byte) { + var data [32]byte + aw.TouchAddress(utils.GetTreeKeyVersion(addr[:]), data[:]) + aw.TouchAddress(utils.GetTreeKeyCodeSize(addr[:]), codeSize[:]) +} + +func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 { + var gas uint64 + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(callerAddr[:]), nil) + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(targetAddr[:]), nil) + return gas +} + +func (aw *AccessWitness) SetLeafValuesValueTransfer(callerAddr, targetAddr, callerBalance, targetBalance []byte) { + aw.TouchAddress(utils.GetTreeKeyBalance(callerAddr[:]), callerBalance) + aw.TouchAddress(utils.GetTreeKeyBalance(targetAddr[:]), targetBalance) +} + +// TouchAndChargeContractCreateInit charges access costs to initiate +// a contract creation +func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte) uint64 { + var gas uint64 + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr[:]), nil) + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(addr[:]), nil) + return gas +} + +func (aw *AccessWitness) SetLeafValuesContractCreateInit(addr, nonce []byte) { + var version [32]byte + aw.TouchAddress(utils.GetTreeKeyVersion(addr[:]), version[:]) + aw.TouchAddress(utils.GetTreeKeyNonce(addr[:]), nonce) +} + +// TouchAndChargeContractCreateCompleted charges access access costs after +// the completion of a contract creation to populate the created account in +// the tree +func (aw *AccessWitness) TouchAndChargeContractCreateCompleted(addr []byte, withValue bool) uint64 { + var gas uint64 + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr[:]), nil) + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(addr[:]), nil) + if withValue { + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(addr[:]), nil) + } + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(addr[:]), nil) + gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeKeccak(addr[:]), nil) + return gas +} + +func (aw *AccessWitness) SetLeafValuesContractCreateCompleted(addr, codeSize, codeKeccak []byte) { + aw.TouchAddress(utils.GetTreeKeyCodeSize(addr[:]), codeSize) + aw.TouchAddress(utils.GetTreeKeyCodeKeccak(addr[:]), codeKeccak) +} + +func (aw *AccessWitness) TouchTxAndChargeGas(originAddr, targetAddr []byte) uint64 { + var gasUsed uint64 + var version [32]byte + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(originAddr[:]), version[:]) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(originAddr[:]), nil) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(originAddr[:]), nil) + + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(targetAddr[:]), version[:]) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(targetAddr[:]), nil) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(targetAddr[:]), nil) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(targetAddr[:]), nil) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeKeccak(targetAddr[:]), nil) + return gasUsed +} + +func (aw *AccessWitness) TouchTxOriginAndChargeGas(originAddr []byte) uint64 { + var gasUsed uint64 + var version [32]byte + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(originAddr[:]), version[:]) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(originAddr[:]), nil) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(originAddr[:]), nil) + return gasUsed +} + +func (aw *AccessWitness) TouchTxExistingAndChargeGas(targetAddr []byte) uint64 { + var gasUsed uint64 + var version [32]byte + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(targetAddr[:]), version[:]) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(targetAddr[:]), nil) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(targetAddr[:]), nil) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(targetAddr[:]), nil) + gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeKeccak(targetAddr[:]), nil) + return gasUsed +} + +func (aw *AccessWitness) SetTxTouchedLeaves(originAddr, originBalance, originNonce []byte) { + aw.TouchAddress(utils.GetTreeKeyBalance(originAddr[:]), originBalance) + aw.TouchAddress(utils.GetTreeKeyNonce(originAddr[:]), originNonce) +} + +func (aw *AccessWitness) SetTxExistingTouchedLeaves(targetAddr, targetBalance, targetNonce, targetCodeSize, targetCodeHash []byte) { + aw.TouchAddress(utils.GetTreeKeyBalance(targetAddr[:]), targetBalance) + aw.TouchAddress(utils.GetTreeKeyNonce(targetAddr[:]), targetNonce) + aw.TouchAddress(utils.GetTreeKeyCodeSize(targetAddr[:]), targetCodeSize) + aw.TouchAddress(utils.GetTreeKeyCodeKeccak(targetAddr[:]), targetCodeHash) +} + // TouchAddress adds any missing addr to the witness and returns respectively // true if the stem or the stub weren't arleady present. func (aw *AccessWitness) TouchAddress(addr, value []byte) (bool, bool) { diff --git a/core/vm/evm.go b/core/vm/evm.go index 41a7062ba81f..178ff4b36a18 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -202,6 +202,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if !evm.StateDB.Exist(addr) { if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + // proof of absence + tryConsumeGas(&gas, evm.Accesses.TouchAndChargeProofOfAbsence(caller.Address().Bytes())) + } // Calling a non existing account, don't do anything, but ping the tracer if evm.Config.Debug { if evm.depth == 0 { @@ -216,6 +220,11 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) && value.Sign() != 0 { + callerBalanceBefore := evm.StateDB.GetBalanceLittleEndian(caller.Address()) + targetBalanceBefore := evm.StateDB.GetBalanceLittleEndian(addr) + evm.Accesses.SetLeafValuesValueTransfer(caller.Address().Bytes()[:], addr[:], callerBalanceBefore, targetBalanceBefore) + } evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) // Capture the tracer start/end events in debug mode @@ -240,21 +249,16 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. code := evm.StateDB.GetCode(addr) + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + codeSize := uint64(len(code)) + var codeSizeBytes [32]byte + binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize) + evm.Accesses.SetLeafValuesMessageCall(addr[:], codeSizeBytes[:]) + } + if len(code) == 0 { ret, err = nil, nil // gas is unchanged } else { - if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { - // Touch the account data - var data [32]byte - evm.Accesses.TouchAddress(utils.GetTreeKeyVersion(addr.Bytes()), data[:]) - binary.BigEndian.PutUint64(data[:], evm.StateDB.GetNonce(addr)) - evm.Accesses.TouchAddress(utils.GetTreeKeyNonce(addr[:]), data[:]) - evm.Accesses.TouchAddress(utils.GetTreeKeyBalance(addr[:]), evm.StateDB.GetBalance(addr).Bytes()) - binary.BigEndian.PutUint64(data[:], uint64(len(code))) - evm.Accesses.TouchAddress(utils.GetTreeKeyCodeSize(addr[:]), data[:]) - evm.Accesses.TouchAddress(utils.GetTreeKeyCodeKeccak(addr[:]), evm.StateDB.GetCodeHash(addr).Bytes()) - } - addrCopy := addr // If the account has no code, we can abort here // The depth-check is already done, and precompiles handled above @@ -316,6 +320,12 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas) } else { + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + codeSize := uint64(evm.StateDB.GetCodeSize(addr)) + var codeSizeBytes [32]byte + binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize) + evm.Accesses.SetLeafValuesMessageCall(addr.Bytes()[:], codeSizeBytes[:]) + } addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -360,6 +370,12 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas) } else { + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + codeSize := uint64(evm.StateDB.GetCodeSize(addr)) + var codeSizeBytes [32]byte + binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize) + evm.Accesses.SetLeafValuesMessageCall(addr.Bytes()[:], codeSizeBytes[:]) + } addrCopy := addr // Initialise a new contract and make initialise the delegate values contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() @@ -412,6 +428,12 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas) } else { + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + codeSize := uint64(evm.StateDB.GetCodeSize(addr)) + var codeSizeBytes [32]byte + binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize) + evm.Accesses.SetLeafValuesMessageCall(addr.Bytes()[:], codeSizeBytes[:]) + } // At this point, we use a copy of address. If we don't, the go compiler will // leak the 'contract' to the outer scope, and make allocation for 'contract' // even if the actual execution ends on RunPrecompiled above. @@ -449,6 +471,13 @@ func (c *codeAndHash) Hash() common.Hash { // create creates a new contract using code as deployment code. func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) { + var zeroVerkleLeaf [32]byte + var balanceBefore []byte + + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + evm.Accesses.SetLeafValuesContractCreateInit(address.Bytes()[:], zeroVerkleLeaf[:]) + } + // Depth check execution. Fail if we're trying to execute above the // limit. if evm.depth > int(params.CallCreateDepth) { @@ -478,6 +507,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.chainRules.IsEIP158 { evm.StateDB.SetNonce(address, 1) } + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + // note: assumption is that the nonce, code size, code hash + // will be 0x0000...00 at the target account before it is created + // otherwise would imply contract creation collision which is + // impossible if self-destruct is removed + balanceBefore = evm.StateDB.GetBalanceLittleEndian(address) + } + 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. @@ -535,6 +572,18 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } } + if err == nil && evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateCompleted(address.Bytes()[:], value.Sign() != 0)) { + evm.StateDB.RevertToSnapshot(snapshot) + err = ErrOutOfGas + } else { + evm.Accesses.SetLeafValuesContractCreateCompleted(address.Bytes()[:], zeroVerkleLeaf[:], zeroVerkleLeaf[:]) + if value.Sign() != 0 { + evm.Accesses.TouchAddress(utils.GetTreeKeyBalance(address.Bytes()[:]), balanceBefore) + } + } + } + if evm.Config.Debug { if evm.depth == 0 { evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 89ed80e63b62..951b8d608bfe 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" trieUtils "github.com/ethereum/go-ethereum/trie/utils" ) @@ -408,13 +409,6 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize transfersValue = !stack.Back(2).IsZero() address = common.Address(stack.Back(1).Bytes20()) ) - if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { - // Charge witness costs - for i := trieUtils.VersionLeafKey; i <= trieUtils.CodeSizeLeafKey; i++ { - index := trieUtils.GetTreeKeyAccountLeaf(address[:], byte(i)) - gas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil) - } - } if evm.chainRules.IsEIP158 { if transfersValue && evm.StateDB.Empty(address) { @@ -442,6 +436,20 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, ErrGasUintOverflow } + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + if _, isPrecompile := evm.precompile(address); !isPrecompile { + gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()[:])) + if overflow { + return 0, ErrGasUintOverflow + } + } + if transfersValue { + gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeValueTransfer(contract.Address().Bytes()[:], address.Bytes()[:])) + if overflow { + return 0, ErrGasUintOverflow + } + } + } return gas, nil } @@ -468,6 +476,15 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, ErrGasUintOverflow } + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + address := common.Address(stack.Back(1).Bytes20()) + if _, isPrecompile := evm.precompile(address); !isPrecompile { + gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes())) + if overflow { + return 0, ErrGasUintOverflow + } + } + } return gas, nil } @@ -484,6 +501,15 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, ErrGasUintOverflow } + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + address := common.Address(stack.Back(1).Bytes20()) + if _, isPrecompile := evm.precompile(address); !isPrecompile { + gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes())) + if overflow { + return 0, ErrGasUintOverflow + } + } + } return gas, nil } @@ -500,6 +526,15 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, ErrGasUintOverflow } + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + address := common.Address(stack.Back(1).Bytes20()) + if _, isPrecompile := evm.precompile(address); !isPrecompile { + gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes())) + if overflow { + return 0, ErrGasUintOverflow + } + } + } return gas, nil } @@ -520,6 +555,12 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me } } + if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { + // TODO turn this into a panic (when we are sure this method + // will never execute when verkle is enabled) + log.Warn("verkle witness accumulation not supported for selfdestruct") + } + if !evm.StateDB.HasSuicided(contract.Address()) { evm.StateDB.AddRefund(params.SelfdestructRefundGas) } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index abfa890274d8..dd80715daf7c 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -19,6 +19,7 @@ package vm import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" trieUtils "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" @@ -667,6 +668,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = scope.Contract.Gas ) + if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) { + contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address())) + statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:]) + if !tryConsumeGas(&gas, statelessGas) { + return nil, ErrExecutionReverted + } + } if interpreter.evm.chainRules.IsEIP150 { gas -= gas / 64 } @@ -709,6 +717,14 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = scope.Contract.Gas ) + if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) { + codeAndHash := &codeAndHash{code: input} + contractAddress := crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) + statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:]) + if !tryConsumeGas(&gas, statelessGas) { + return nil, ErrExecutionReverted + } + } // Apply EIP150 gas -= gas / 64 diff --git a/core/vm/interface.go b/core/vm/interface.go index b9c2eccb66f2..37a7eab63912 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -47,6 +47,9 @@ type StateDB interface { GetState(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) + GetBalanceLittleEndian(address common.Address) []byte + GetNonceLittleEndian(address common.Address) []byte + Suicide(common.Address) bool HasSuicided(common.Address) bool