From 710c3f32ac8e4e5829a6a631dcfb1e0e13a49220 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:41:44 +0100 Subject: [PATCH] vm: simplify error handling in `vm.EVM.create()` (#30292) To allow all error paths in `vm.EVM.create()` to consume the necessary gas, there is currently a pattern of gating code on `if err == nil` instead of returning as soon as the error occurs. The same behaviour can be achieved by abstracting the gated code into a method that returns immediately on error, improving readability and thus making it easier to understand and maintain. --- core/vm/evm.go | 97 ++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 1944189b5d..7617d843c7 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -510,66 +510,61 @@ 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.chainRules.IsEIP4762 { - if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) { - err = ErrOutOfGas - } - } - - if err == nil { - ret, err = evm.interpreter.Run(contract, nil, false) - } - - // Check whether the max code size has been exceeded, assign err if the case. - if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { - err = ErrMaxCodeSizeExceeded - } - - // Reject code starting with 0xEF if EIP-3541 is enabled. - if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon { - err = ErrInvalidCode - } - - // if the contract creation ran successfully and no errors were returned - // calculate the gas required to store the code. If the code could not - // be stored due to not enough gas set an error and let it be handled - // by the error checking condition below. - if err == nil { - if !evm.chainRules.IsEIP4762 { - createDataGas := uint64(len(ret)) * params.CreateDataGas - if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) { - err = ErrCodeStoreOutOfGas - } - } else { - // Contract creation completed, touch the missing fields in the contract - if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) { - err = ErrCodeStoreOutOfGas - } - - if err == nil && len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) { - err = ErrCodeStoreOutOfGas - } - } - - if err == nil { - evm.StateDB.SetCode(address, ret) - } - } - - // When an error was returned by the EVM or when setting the creation code - // above we revert to the snapshot and consume any gas remaining. Additionally, - // when we're in homestead this also counts for code storage gas errors. + ret, err = evm.initNewContract(contract, address, value) if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution) } } - return ret, address, contract.Gas, err } +// initNewContract runs a new contract's creation code, performs checks on the +// resulting code that is to be deployed, and consumes necessary gas. +func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) { + // Charge the contract creation init gas in verkle mode + if evm.chainRules.IsEIP4762 { + if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) { + return nil, ErrOutOfGas + } + } + + ret, err := evm.interpreter.Run(contract, nil, false) + if err != nil { + return ret, err + } + + // Check whether the max code size has been exceeded, assign err if the case. + if evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { + return ret, ErrMaxCodeSizeExceeded + } + + // Reject code starting with 0xEF if EIP-3541 is enabled. + if len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon { + return ret, ErrInvalidCode + } + + if !evm.chainRules.IsEIP4762 { + createDataGas := uint64(len(ret)) * params.CreateDataGas + if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) { + return ret, ErrCodeStoreOutOfGas + } + } else { + // Contract creation completed, touch the missing fields in the contract + if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) { + return ret, ErrCodeStoreOutOfGas + } + + if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) { + return ret, ErrCodeStoreOutOfGas + } + } + + evm.StateDB.SetCode(address, ret) + return ret, nil +} + // Create creates a new contract using code as deployment code. func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))