revm_interpreter/
instruction_result.rs

1use crate::primitives::{HaltReason, OutOfGasError, SuccessReason};
2
3#[repr(u8)]
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub enum InstructionResult {
7    // Success Codes
8    #[default]
9    /// Execution should continue to the next one.
10    Continue = 0x00,
11    /// Encountered a `STOP` opcode
12    Stop,
13    /// Return from the current call.
14    Return,
15    /// Self-destruct the current contract.
16    SelfDestruct,
17    /// Return a contract (used in contract creation).
18    ReturnContract,
19
20    // Revert Codes
21    /// Revert the transaction.
22    Revert = 0x10,
23    /// Exceeded maximum call depth.
24    CallTooDeep,
25    /// Insufficient funds for transfer.
26    OutOfFunds,
27    /// Revert if `CREATE`/`CREATE2` starts with `0xEF00`.
28    CreateInitCodeStartingEF00,
29    /// Invalid EVM Object Format (EOF) init code.
30    InvalidEOFInitCode,
31    /// `ExtDelegateCall` calling a non EOF contract.
32    InvalidExtDelegateCallTarget,
33
34    // Action Codes
35    /// Indicates a call or contract creation.
36    CallOrCreate = 0x20,
37
38    // Error Codes
39    /// Out of gas error.
40    OutOfGas = 0x50,
41    /// Out of gas error encountered during memory expansion.
42    MemoryOOG,
43    /// The memory limit of the EVM has been exceeded.
44    MemoryLimitOOG,
45    /// Out of gas error encountered during the execution of a precompiled contract.
46    PrecompileOOG,
47    /// Out of gas error encountered while calling an invalid operand.
48    InvalidOperandOOG,
49    /// Unknown or invalid opcode.
50    OpcodeNotFound,
51    /// Invalid `CALL` with value transfer in static context.
52    CallNotAllowedInsideStatic,
53    /// Invalid state modification in static call.
54    StateChangeDuringStaticCall,
55    /// An undefined bytecode value encountered during execution.
56    InvalidFEOpcode,
57    /// Invalid jump destination. Dynamic jumps points to invalid not jumpdest opcode.
58    InvalidJump,
59    /// The feature or opcode is not activated in this version of the EVM.
60    NotActivated,
61    /// Attempting to pop a value from an empty stack.
62    StackUnderflow,
63    /// Attempting to push a value onto a full stack.
64    StackOverflow,
65    /// Invalid memory or storage offset.
66    OutOfOffset,
67    /// Address collision during contract creation.
68    CreateCollision,
69    /// Payment amount overflow.
70    OverflowPayment,
71    /// Error in precompiled contract execution.
72    PrecompileError,
73    /// Nonce overflow.
74    NonceOverflow,
75    /// Exceeded contract size limit during creation.
76    CreateContractSizeLimit,
77    /// Created contract starts with invalid bytes (`0xEF`).
78    CreateContractStartingWithEF,
79    /// Exceeded init code size limit (EIP-3860:  Limit and meter initcode).
80    CreateInitCodeSizeLimit,
81    /// Fatal external error. Returned by database.
82    FatalExternalError,
83    /// `RETURNCONTRACT` called outside init EOF code.
84    ReturnContractInNotInitEOF,
85    /// Legacy contract is calling opcode that is enabled only in EOF.
86    EOFOpcodeDisabledInLegacy,
87    /// Stack overflow in EOF subroutine function calls.
88    EOFFunctionStackOverflow,
89    /// Aux data overflow, new aux data is larger than `u16` max size.
90    EofAuxDataOverflow,
91    /// Aux data is smaller then already present data size.
92    EofAuxDataTooSmall,
93    /// `EXT*CALL` target address needs to be padded with 0s.
94    InvalidEXTCALLTarget,
95}
96
97impl From<SuccessReason> for InstructionResult {
98    fn from(value: SuccessReason) -> Self {
99        match value {
100            SuccessReason::Return => InstructionResult::Return,
101            SuccessReason::Stop => InstructionResult::Stop,
102            SuccessReason::SelfDestruct => InstructionResult::SelfDestruct,
103            SuccessReason::EofReturnContract => InstructionResult::ReturnContract,
104        }
105    }
106}
107
108impl From<HaltReason> for InstructionResult {
109    fn from(value: HaltReason) -> Self {
110        match value {
111            HaltReason::OutOfGas(error) => match error {
112                OutOfGasError::Basic => Self::OutOfGas,
113                OutOfGasError::InvalidOperand => Self::InvalidOperandOOG,
114                OutOfGasError::Memory => Self::MemoryOOG,
115                OutOfGasError::MemoryLimit => Self::MemoryLimitOOG,
116                OutOfGasError::Precompile => Self::PrecompileOOG,
117            },
118            HaltReason::OpcodeNotFound => Self::OpcodeNotFound,
119            HaltReason::InvalidFEOpcode => Self::InvalidFEOpcode,
120            HaltReason::InvalidJump => Self::InvalidJump,
121            HaltReason::NotActivated => Self::NotActivated,
122            HaltReason::StackOverflow => Self::StackOverflow,
123            HaltReason::StackUnderflow => Self::StackUnderflow,
124            HaltReason::OutOfOffset => Self::OutOfOffset,
125            HaltReason::CreateCollision => Self::CreateCollision,
126            HaltReason::PrecompileError => Self::PrecompileError,
127            HaltReason::NonceOverflow => Self::NonceOverflow,
128            HaltReason::CreateContractSizeLimit => Self::CreateContractSizeLimit,
129            HaltReason::CreateContractStartingWithEF => Self::CreateContractStartingWithEF,
130            HaltReason::CreateInitCodeSizeLimit => Self::CreateInitCodeSizeLimit,
131            HaltReason::OverflowPayment => Self::OverflowPayment,
132            HaltReason::StateChangeDuringStaticCall => Self::StateChangeDuringStaticCall,
133            HaltReason::CallNotAllowedInsideStatic => Self::CallNotAllowedInsideStatic,
134            HaltReason::OutOfFunds => Self::OutOfFunds,
135            HaltReason::CallTooDeep => Self::CallTooDeep,
136            HaltReason::EofAuxDataOverflow => Self::EofAuxDataOverflow,
137            HaltReason::EofAuxDataTooSmall => Self::EofAuxDataTooSmall,
138            HaltReason::EOFFunctionStackOverflow => Self::EOFFunctionStackOverflow,
139            HaltReason::InvalidEXTCALLTarget => Self::InvalidEXTCALLTarget,
140            #[cfg(feature = "optimism")]
141            HaltReason::FailedDeposit => Self::FatalExternalError,
142        }
143    }
144}
145
146#[macro_export]
147macro_rules! return_ok {
148    () => {
149        InstructionResult::Continue
150            | InstructionResult::Stop
151            | InstructionResult::Return
152            | InstructionResult::SelfDestruct
153            | InstructionResult::ReturnContract
154    };
155}
156
157#[macro_export]
158macro_rules! return_revert {
159    () => {
160        InstructionResult::Revert
161            | InstructionResult::CallTooDeep
162            | InstructionResult::OutOfFunds
163            | InstructionResult::InvalidEOFInitCode
164            | InstructionResult::CreateInitCodeStartingEF00
165            | InstructionResult::InvalidExtDelegateCallTarget
166    };
167}
168
169#[macro_export]
170macro_rules! return_error {
171    () => {
172        InstructionResult::OutOfGas
173            | InstructionResult::MemoryOOG
174            | InstructionResult::MemoryLimitOOG
175            | InstructionResult::PrecompileOOG
176            | InstructionResult::InvalidOperandOOG
177            | InstructionResult::OpcodeNotFound
178            | InstructionResult::CallNotAllowedInsideStatic
179            | InstructionResult::StateChangeDuringStaticCall
180            | InstructionResult::InvalidFEOpcode
181            | InstructionResult::InvalidJump
182            | InstructionResult::NotActivated
183            | InstructionResult::StackUnderflow
184            | InstructionResult::StackOverflow
185            | InstructionResult::OutOfOffset
186            | InstructionResult::CreateCollision
187            | InstructionResult::OverflowPayment
188            | InstructionResult::PrecompileError
189            | InstructionResult::NonceOverflow
190            | InstructionResult::CreateContractSizeLimit
191            | InstructionResult::CreateContractStartingWithEF
192            | InstructionResult::CreateInitCodeSizeLimit
193            | InstructionResult::FatalExternalError
194            | InstructionResult::ReturnContractInNotInitEOF
195            | InstructionResult::EOFOpcodeDisabledInLegacy
196            | InstructionResult::EOFFunctionStackOverflow
197            | InstructionResult::EofAuxDataTooSmall
198            | InstructionResult::EofAuxDataOverflow
199            | InstructionResult::InvalidEXTCALLTarget
200    };
201}
202
203impl InstructionResult {
204    /// Returns whether the result is a success.
205    #[inline]
206    pub const fn is_ok(self) -> bool {
207        matches!(self, crate::return_ok!())
208    }
209
210    /// Returns whether the result is a revert.
211    #[inline]
212    pub const fn is_revert(self) -> bool {
213        matches!(self, crate::return_revert!())
214    }
215
216    /// Returns whether the result is an error.
217    #[inline]
218    pub const fn is_error(self) -> bool {
219        matches!(self, return_error!())
220    }
221}
222
223/// Internal result that are not ex
224#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
225pub enum InternalResult {
226    /// Internal instruction that signals Interpreter should continue running.
227    InternalContinue,
228    /// Internal instruction that signals call or create.
229    InternalCallOrCreate,
230    /// Internal CREATE/CREATE starts with 0xEF00
231    CreateInitCodeStartingEF00,
232    /// Internal to ExtDelegateCall
233    InvalidExtDelegateCallTarget,
234}
235
236#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
237pub enum SuccessOrHalt {
238    Success(SuccessReason),
239    Revert,
240    Halt(HaltReason),
241    FatalExternalError,
242    Internal(InternalResult),
243}
244
245impl SuccessOrHalt {
246    /// Returns true if the transaction returned successfully without halts.
247    #[inline]
248    pub fn is_success(self) -> bool {
249        matches!(self, SuccessOrHalt::Success(_))
250    }
251
252    /// Returns the [SuccessReason] value if this a successful result
253    #[inline]
254    pub fn to_success(self) -> Option<SuccessReason> {
255        match self {
256            SuccessOrHalt::Success(reason) => Some(reason),
257            _ => None,
258        }
259    }
260
261    /// Returns true if the transaction reverted.
262    #[inline]
263    pub fn is_revert(self) -> bool {
264        matches!(self, SuccessOrHalt::Revert)
265    }
266
267    /// Returns true if the EVM has experienced an exceptional halt
268    #[inline]
269    pub fn is_halt(self) -> bool {
270        matches!(self, SuccessOrHalt::Halt(_))
271    }
272
273    /// Returns the [HaltReason] value the EVM has experienced an exceptional halt
274    #[inline]
275    pub fn to_halt(self) -> Option<HaltReason> {
276        match self {
277            SuccessOrHalt::Halt(reason) => Some(reason),
278            _ => None,
279        }
280    }
281}
282
283impl From<InstructionResult> for SuccessOrHalt {
284    fn from(result: InstructionResult) -> Self {
285        match result {
286            InstructionResult::Continue => Self::Internal(InternalResult::InternalContinue), // used only in interpreter loop
287            InstructionResult::Stop => Self::Success(SuccessReason::Stop),
288            InstructionResult::Return => Self::Success(SuccessReason::Return),
289            InstructionResult::SelfDestruct => Self::Success(SuccessReason::SelfDestruct),
290            InstructionResult::Revert => Self::Revert,
291            InstructionResult::CreateInitCodeStartingEF00 => Self::Revert,
292            InstructionResult::CallOrCreate => Self::Internal(InternalResult::InternalCallOrCreate), // used only in interpreter loop
293            InstructionResult::CallTooDeep => Self::Halt(HaltReason::CallTooDeep), // not gonna happen for first call
294            InstructionResult::OutOfFunds => Self::Halt(HaltReason::OutOfFunds), // Check for first call is done separately.
295            InstructionResult::OutOfGas => Self::Halt(HaltReason::OutOfGas(OutOfGasError::Basic)),
296            InstructionResult::MemoryLimitOOG => {
297                Self::Halt(HaltReason::OutOfGas(OutOfGasError::MemoryLimit))
298            }
299            InstructionResult::MemoryOOG => Self::Halt(HaltReason::OutOfGas(OutOfGasError::Memory)),
300            InstructionResult::PrecompileOOG => {
301                Self::Halt(HaltReason::OutOfGas(OutOfGasError::Precompile))
302            }
303            InstructionResult::InvalidOperandOOG => {
304                Self::Halt(HaltReason::OutOfGas(OutOfGasError::InvalidOperand))
305            }
306            InstructionResult::OpcodeNotFound | InstructionResult::ReturnContractInNotInitEOF => {
307                Self::Halt(HaltReason::OpcodeNotFound)
308            }
309            InstructionResult::CallNotAllowedInsideStatic => {
310                Self::Halt(HaltReason::CallNotAllowedInsideStatic)
311            } // first call is not static call
312            InstructionResult::StateChangeDuringStaticCall => {
313                Self::Halt(HaltReason::StateChangeDuringStaticCall)
314            }
315            InstructionResult::InvalidFEOpcode => Self::Halt(HaltReason::InvalidFEOpcode),
316            InstructionResult::InvalidJump => Self::Halt(HaltReason::InvalidJump),
317            InstructionResult::NotActivated => Self::Halt(HaltReason::NotActivated),
318            InstructionResult::StackUnderflow => Self::Halt(HaltReason::StackUnderflow),
319            InstructionResult::StackOverflow => Self::Halt(HaltReason::StackOverflow),
320            InstructionResult::OutOfOffset => Self::Halt(HaltReason::OutOfOffset),
321            InstructionResult::CreateCollision => Self::Halt(HaltReason::CreateCollision),
322            InstructionResult::OverflowPayment => Self::Halt(HaltReason::OverflowPayment), // Check for first call is done separately.
323            InstructionResult::PrecompileError => Self::Halt(HaltReason::PrecompileError),
324            InstructionResult::NonceOverflow => Self::Halt(HaltReason::NonceOverflow),
325            InstructionResult::CreateContractSizeLimit
326            | InstructionResult::CreateContractStartingWithEF => {
327                Self::Halt(HaltReason::CreateContractSizeLimit)
328            }
329            InstructionResult::CreateInitCodeSizeLimit => {
330                Self::Halt(HaltReason::CreateInitCodeSizeLimit)
331            }
332            // TODO (EOF) add proper Revert subtype.
333            InstructionResult::InvalidEOFInitCode => Self::Revert,
334            InstructionResult::FatalExternalError => Self::FatalExternalError,
335            InstructionResult::EOFOpcodeDisabledInLegacy => Self::Halt(HaltReason::OpcodeNotFound),
336            InstructionResult::EOFFunctionStackOverflow => {
337                Self::Halt(HaltReason::EOFFunctionStackOverflow)
338            }
339            InstructionResult::ReturnContract => Self::Success(SuccessReason::EofReturnContract),
340            InstructionResult::EofAuxDataOverflow => Self::Halt(HaltReason::EofAuxDataOverflow),
341            InstructionResult::EofAuxDataTooSmall => Self::Halt(HaltReason::EofAuxDataTooSmall),
342            InstructionResult::InvalidEXTCALLTarget => Self::Halt(HaltReason::InvalidEXTCALLTarget),
343            InstructionResult::InvalidExtDelegateCallTarget => {
344                Self::Internal(InternalResult::InvalidExtDelegateCallTarget)
345            }
346        }
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use crate::InstructionResult;
353
354    #[test]
355    fn all_results_are_covered() {
356        match InstructionResult::Continue {
357            return_error!() => {}
358            return_revert!() => {}
359            return_ok!() => {}
360            InstructionResult::CallOrCreate => {}
361        }
362    }
363
364    #[test]
365    fn test_results() {
366        let ok_results = vec![
367            InstructionResult::Continue,
368            InstructionResult::Stop,
369            InstructionResult::Return,
370            InstructionResult::SelfDestruct,
371        ];
372
373        for result in ok_results {
374            assert!(result.is_ok());
375            assert!(!result.is_revert());
376            assert!(!result.is_error());
377        }
378
379        let revert_results = vec![
380            InstructionResult::Revert,
381            InstructionResult::CallTooDeep,
382            InstructionResult::OutOfFunds,
383        ];
384
385        for result in revert_results {
386            assert!(!result.is_ok());
387            assert!(result.is_revert());
388            assert!(!result.is_error());
389        }
390
391        let error_results = vec![
392            InstructionResult::OutOfGas,
393            InstructionResult::MemoryOOG,
394            InstructionResult::MemoryLimitOOG,
395            InstructionResult::PrecompileOOG,
396            InstructionResult::InvalidOperandOOG,
397            InstructionResult::OpcodeNotFound,
398            InstructionResult::CallNotAllowedInsideStatic,
399            InstructionResult::StateChangeDuringStaticCall,
400            InstructionResult::InvalidFEOpcode,
401            InstructionResult::InvalidJump,
402            InstructionResult::NotActivated,
403            InstructionResult::StackUnderflow,
404            InstructionResult::StackOverflow,
405            InstructionResult::OutOfOffset,
406            InstructionResult::CreateCollision,
407            InstructionResult::OverflowPayment,
408            InstructionResult::PrecompileError,
409            InstructionResult::NonceOverflow,
410            InstructionResult::CreateContractSizeLimit,
411            InstructionResult::CreateContractStartingWithEF,
412            InstructionResult::CreateInitCodeSizeLimit,
413            InstructionResult::FatalExternalError,
414        ];
415
416        for result in error_results {
417            assert!(!result.is_ok());
418            assert!(!result.is_revert());
419            assert!(result.is_error());
420        }
421    }
422}