Execution frame error handling

Design a better and simplified error handling model for Ethereum.

In current EVM, codes are ran in execution frames. When an execution frame returns, it will return one of the following status to the parent:

  • Succeed: The execution is successful.

  • Reverted: The execution has terminated due to an explicit REVERT opcode. All state changes are reverted, while the remaining gases are refunded.

  • Failed: The execution has terminated due to an "out-of-gas" error. All state changes are reverted, and the remaining gases will not be refunded.

  • Not Supported: Fatal EVM execution error that cannot be handled, usually due to attempts of creating memory region larger than the virtual machine can support. This is not expected to happen in a blockchain context.

Both Reverted and Failed are called Errors in EVM, and currently both of them are recoverable, in that they can be inspected by the parent execution frame, if there is any.

The current error handling semantics have several issues:

  • It makes gas cost observable as a side effect. To design a forward-compatible EVM and fix current backward compatibility issues, one of the important feature change needed is to make gas cost not observable. However, by observing the status of contracts executed in sub-execution frame (for example, contracts that only have one single opcode), the caller can derive the current gas cost schedule.

  • It complicates the threat model of smart contract developers. There are so many ways a contract can fail. It may be opcode parameter restrictions, out-of-bound memory access, actually running out-of-gas, or other subtle EVM execution details. This makes the threat model for smart contracts complicated.

Fatal error as default

Proposals: 39-UNGAS

The intention of this specification is so that we mark errors default as unrecoverable. This significantly simplifies the threat model of smart contract developers. It makes the only way to return a recoverable error through an explicit REVERT. The complete specification of this is described as part of Forward-compatible EVM.

Explicit checkpointing

This section describes a specification, with identifier 46-CHECKPOINT. (Discuss)

One of the overhead for running EVM is that checkpointing is always required for all execution frame invocations. This is the case because we still have recoverable errors. If, instead, the concept of recoverable error is dropped, and all errors are just fatal, we can remove all implicit checkpointing, which may speed up EVM execution.

In this case, contracts which still need to do checkpointing can do so explicitly, using two new opcodes CHECKPOINT and REVERTPOINT.

Specification

Define two new opcodes:

  • CHECKPOINT at 0xfc.

  • REVERTPOINT at 0xfd.

Add a new data structure item checkpoints to EVM, in execution frame level. CHECKPOINT creates a new checkpoint for current execution frame, and REVERTPOINT reverts the last checkpoint. Define constant CHECKPOINT_LIMIT as the limit of checkpoints an execution frame can create. If CHECKPOINT results in checkpointing length greater than CHECKPOINT_LIMIT or if REVERTPOINT operates on empty checkpointing stack, return fatal error.

Interaction

While this specification can be applied standalone, it’s usually only useful when recoverable errors are eliminated from the EVM.