refactor: simplify error handling of vm.EVM.create()

This commit is contained in:
Arran Schlosberg 2024-08-12 17:29:18 +01:00
parent 1cf3b5d38a
commit 344a35d4e5
No known key found for this signature in database
GPG key ID: 8A30F7E4344B4EF3

View file

@ -445,11 +445,11 @@ func (c *codeAndHash) Hash() common.Hash {
} }
// create creates a new contract using code as deployment code. // create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, retErr error) {
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig()) evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig())
defer func(startGas uint64) { defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, startGas, leftOverGas, ret, retErr)
}(gas) }(gas)
} }
// Depth check execution. Fail if we're trying to execute above the // Depth check execution. Fail if we're trying to execute above the
@ -510,64 +510,67 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contract.SetCodeOptionalHash(&address, codeAndHash) contract.SetCodeOptionalHash(&address, codeAndHash)
contract.IsDeployment = true contract.IsDeployment = true
defer func() {
// 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.
if err := retErr; err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
}
}
}()
// Charge the contract creation init gas in verkle mode // Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 { if evm.chainRules.IsEIP4762 {
if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) { if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
err = ErrOutOfGas return nil, common.Address{}, 0, ErrOutOfGas
} }
} }
if err == nil { ret, err := evm.initNewContract(contract, address)
ret, err = evm.interpreter.Run(contract, nil, false) if err != nil {
return nil, common.Address{}, contract.Gas, err
}
evm.StateDB.SetCode(address, ret)
return ret, address, contract.Gas, err
}
// initNewContract perform initialization checks and actions on code for a newly
// create()d contract.
func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]byte, error) {
ret, err := evm.interpreter.Run(contract, nil, false)
if err != nil {
return nil, err
} }
// Check whether the max code size has been exceeded, assign err if the case. // Check whether the max code size has been exceeded, assign err if the case.
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { if evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
err = ErrMaxCodeSizeExceeded return nil, ErrMaxCodeSizeExceeded
} }
// Reject code starting with 0xEF if EIP-3541 is enabled. // Reject code starting with 0xEF if EIP-3541 is enabled.
if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon { if len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {
err = ErrInvalidCode return nil, ErrInvalidCode
} }
// if the contract creation ran successfully and no errors were returned if !evm.chainRules.IsEIP4762 {
// calculate the gas required to store the code. If the code could not createDataGas := uint64(len(ret)) * params.CreateDataGas
// be stored due to not enough gas set an error and let it be handled if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
// by the error checking condition below. return nil, ErrCodeStoreOutOfGas
if err == nil { }
if !evm.chainRules.IsEIP4762 { } else {
createDataGas := uint64(len(ret)) * params.CreateDataGas // Contract creation completed, touch the missing fields in the contract
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) { if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) {
err = ErrCodeStoreOutOfGas return nil, 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 { if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
evm.StateDB.SetCode(address, ret) return nil, ErrCodeStoreOutOfGas
} }
} }
return ret, nil
// 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.
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
} }
// Create creates a new contract using code as deployment code. // Create creates a new contract using code as deployment code.