Skip to content

Commit 710c3f3

Browse files
authored
vm: simplify error handling in vm.EVM.create() (ethereum#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.
1 parent 09d889d commit 710c3f3

File tree

1 file changed

+34
-39
lines changed

1 file changed

+34
-39
lines changed

core/vm/evm.go

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -510,64 +510,59 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
510510
contract.SetCodeOptionalHash(&address, codeAndHash)
511511
contract.IsDeployment = true
512512

513+
ret, err = evm.initNewContract(contract, address, value)
514+
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
515+
evm.StateDB.RevertToSnapshot(snapshot)
516+
if err != ErrExecutionReverted {
517+
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
518+
}
519+
}
520+
return ret, address, contract.Gas, err
521+
}
522+
523+
// initNewContract runs a new contract's creation code, performs checks on the
524+
// resulting code that is to be deployed, and consumes necessary gas.
525+
func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) {
513526
// Charge the contract creation init gas in verkle mode
514527
if evm.chainRules.IsEIP4762 {
515528
if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
516-
err = ErrOutOfGas
529+
return nil, ErrOutOfGas
517530
}
518531
}
519532

520-
if err == nil {
521-
ret, err = evm.interpreter.Run(contract, nil, false)
533+
ret, err := evm.interpreter.Run(contract, nil, false)
534+
if err != nil {
535+
return ret, err
522536
}
523537

524538
// Check whether the max code size has been exceeded, assign err if the case.
525-
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
526-
err = ErrMaxCodeSizeExceeded
539+
if evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
540+
return ret, ErrMaxCodeSizeExceeded
527541
}
528542

529543
// Reject code starting with 0xEF if EIP-3541 is enabled.
530-
if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {
531-
err = ErrInvalidCode
532-
}
533-
534-
// if the contract creation ran successfully and no errors were returned
535-
// calculate the gas required to store the code. If the code could not
536-
// be stored due to not enough gas set an error and let it be handled
537-
// by the error checking condition below.
538-
if err == nil {
539-
if !evm.chainRules.IsEIP4762 {
540-
createDataGas := uint64(len(ret)) * params.CreateDataGas
541-
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
542-
err = ErrCodeStoreOutOfGas
543-
}
544-
} else {
545-
// Contract creation completed, touch the missing fields in the contract
546-
if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) {
547-
err = ErrCodeStoreOutOfGas
548-
}
544+
if len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {
545+
return ret, ErrInvalidCode
546+
}
549547

550-
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) {
551-
err = ErrCodeStoreOutOfGas
552-
}
548+
if !evm.chainRules.IsEIP4762 {
549+
createDataGas := uint64(len(ret)) * params.CreateDataGas
550+
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
551+
return ret, ErrCodeStoreOutOfGas
553552
}
554-
555-
if err == nil {
556-
evm.StateDB.SetCode(address, ret)
553+
} else {
554+
// Contract creation completed, touch the missing fields in the contract
555+
if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) {
556+
return ret, ErrCodeStoreOutOfGas
557557
}
558-
}
559558

560-
// When an error was returned by the EVM or when setting the creation code
561-
// above we revert to the snapshot and consume any gas remaining. Additionally,
562-
// when we're in homestead this also counts for code storage gas errors.
563-
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
564-
evm.StateDB.RevertToSnapshot(snapshot)
565-
if err != ErrExecutionReverted {
566-
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
559+
if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
560+
return ret, ErrCodeStoreOutOfGas
567561
}
568562
}
569563

570-
return ret, address, contract.Gas, err
564+
evm.StateDB.SetCode(address, ret)
565+
return ret, nil
571566
}
572567

573568
// Create creates a new contract using code as deployment code.

0 commit comments

Comments
 (0)