revm_interpreter/
instruction_result.rs

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