rtvm_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    Continue = 0x00,
10    Stop,
11    Return,
12    SelfDestruct,
13    ReturnContract,
14
15    // revert codes
16    Revert = 0x10, // revert opcode
17    CallTooDeep,
18    OutOfFunds,
19
20    // Actions
21    CallOrCreate = 0x20,
22
23    // error codes
24    OutOfGas = 0x50,
25    MemoryOOG,
26    MemoryLimitOOG,
27    PrecompileOOG,
28    InvalidOperandOOG,
29    OpcodeNotFound,
30    CallNotAllowedInsideStatic,
31    StateChangeDuringStaticCall,
32    InvalidFEOpcode,
33    InvalidJump,
34    NotActivated,
35    StackUnderflow,
36    StackOverflow,
37    OutOfOffset,
38    CreateCollision,
39    OverflowPayment,
40    PrecompileError,
41    NonceOverflow,
42    /// Create init code size exceeds limit (runtime).
43    CreateContractSizeLimit,
44    /// Error on created contract that begins with EF
45    CreateContractStartingWithEF,
46    /// EIP-3860: Limit and meter initcode. Initcode size limit exceeded.
47    CreateInitCodeSizeLimit,
48    /// Fatal external error. Returned by database.
49    FatalExternalError,
50    /// RETURNCONTRACT called in not init eof code.
51    ReturnContractInNotInitEOF,
52    /// Legacy contract is calling opcode that is enabled only in EOF.
53    EOFOpcodeDisabledInLegacy,
54    /// EOF function stack overflow
55    EOFFunctionStackOverflow,
56}
57
58impl From<SuccessReason> for InstructionResult {
59    fn from(value: SuccessReason) -> Self {
60        match value {
61            SuccessReason::Return => InstructionResult::Return,
62            SuccessReason::Stop => InstructionResult::Stop,
63            SuccessReason::SelfDestruct => InstructionResult::SelfDestruct,
64        }
65    }
66}
67
68impl From<HaltReason> for InstructionResult {
69    fn from(value: HaltReason) -> Self {
70        match value {
71            HaltReason::OutOfGas(error) => match error {
72                OutOfGasError::Basic => Self::OutOfGas,
73                OutOfGasError::InvalidOperand => Self::InvalidOperandOOG,
74                OutOfGasError::Memory => Self::MemoryOOG,
75                OutOfGasError::MemoryLimit => Self::MemoryLimitOOG,
76                OutOfGasError::Precompile => Self::PrecompileOOG,
77            },
78            HaltReason::OpcodeNotFound => Self::OpcodeNotFound,
79            HaltReason::InvalidFEOpcode => Self::InvalidFEOpcode,
80            HaltReason::InvalidJump => Self::InvalidJump,
81            HaltReason::NotActivated => Self::NotActivated,
82            HaltReason::StackOverflow => Self::StackOverflow,
83            HaltReason::StackUnderflow => Self::StackUnderflow,
84            HaltReason::OutOfOffset => Self::OutOfOffset,
85            HaltReason::CreateCollision => Self::CreateCollision,
86            HaltReason::PrecompileError => Self::PrecompileError,
87            HaltReason::NonceOverflow => Self::NonceOverflow,
88            HaltReason::CreateContractSizeLimit => Self::CreateContractSizeLimit,
89            HaltReason::CreateContractStartingWithEF => Self::CreateContractStartingWithEF,
90            HaltReason::CreateInitCodeSizeLimit => Self::CreateInitCodeSizeLimit,
91            HaltReason::OverflowPayment => Self::OverflowPayment,
92            HaltReason::StateChangeDuringStaticCall => Self::StateChangeDuringStaticCall,
93            HaltReason::CallNotAllowedInsideStatic => Self::CallNotAllowedInsideStatic,
94            HaltReason::OutOfFunds => Self::OutOfFunds,
95            HaltReason::CallTooDeep => Self::CallTooDeep,
96            #[cfg(feature = "optimism")]
97            HaltReason::FailedDeposit => Self::FatalExternalError,
98        }
99    }
100}
101
102#[macro_export]
103macro_rules! return_ok {
104    () => {
105        InstructionResult::Continue
106            | InstructionResult::Stop
107            | InstructionResult::Return
108            | InstructionResult::SelfDestruct
109            | InstructionResult::ReturnContract
110    };
111}
112
113#[macro_export]
114macro_rules! return_revert {
115    () => {
116        InstructionResult::Revert | InstructionResult::CallTooDeep | InstructionResult::OutOfFunds
117    };
118}
119
120#[macro_export]
121macro_rules! return_error {
122    () => {
123        InstructionResult::OutOfGas
124            | InstructionResult::MemoryOOG
125            | InstructionResult::MemoryLimitOOG
126            | InstructionResult::PrecompileOOG
127            | InstructionResult::InvalidOperandOOG
128            | InstructionResult::OpcodeNotFound
129            | InstructionResult::CallNotAllowedInsideStatic
130            | InstructionResult::StateChangeDuringStaticCall
131            | InstructionResult::InvalidFEOpcode
132            | InstructionResult::InvalidJump
133            | InstructionResult::NotActivated
134            | InstructionResult::StackUnderflow
135            | InstructionResult::StackOverflow
136            | InstructionResult::OutOfOffset
137            | InstructionResult::CreateCollision
138            | InstructionResult::OverflowPayment
139            | InstructionResult::PrecompileError
140            | InstructionResult::NonceOverflow
141            | InstructionResult::CreateContractSizeLimit
142            | InstructionResult::CreateContractStartingWithEF
143            | InstructionResult::CreateInitCodeSizeLimit
144            | InstructionResult::FatalExternalError
145            | InstructionResult::ReturnContractInNotInitEOF
146            | InstructionResult::EOFOpcodeDisabledInLegacy
147            | InstructionResult::EOFFunctionStackOverflow
148    };
149}
150
151impl InstructionResult {
152    /// Returns whether the result is a success.
153    #[inline]
154    pub const fn is_ok(self) -> bool {
155        matches!(self, crate::return_ok!())
156    }
157
158    /// Returns whether the result is a revert.
159    #[inline]
160    pub const fn is_revert(self) -> bool {
161        matches!(self, crate::return_revert!())
162    }
163
164    /// Returns whether the result is an error.
165    #[inline]
166    pub const fn is_error(self) -> bool {
167        matches!(self, return_error!())
168    }
169}
170
171#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
172pub enum SuccessOrHalt {
173    Success(SuccessReason),
174    Revert,
175    Halt(HaltReason),
176    FatalExternalError,
177    /// Internal instruction that signals Interpreter should continue running.
178    InternalContinue,
179    /// Internal instruction that signals call or create.
180    InternalCallOrCreate,
181}
182
183impl SuccessOrHalt {
184    /// Returns true if the transaction returned successfully without halts.
185    #[inline]
186    pub fn is_success(self) -> bool {
187        matches!(self, SuccessOrHalt::Success(_))
188    }
189
190    /// Returns the [SuccessReason] value if this a successful result
191    #[inline]
192    pub fn to_success(self) -> Option<SuccessReason> {
193        match self {
194            SuccessOrHalt::Success(reason) => Some(reason),
195            _ => None,
196        }
197    }
198
199    /// Returns true if the transaction reverted.
200    #[inline]
201    pub fn is_revert(self) -> bool {
202        matches!(self, SuccessOrHalt::Revert)
203    }
204
205    /// Returns true if the EVM has experienced an exceptional halt
206    #[inline]
207    pub fn is_halt(self) -> bool {
208        matches!(self, SuccessOrHalt::Halt(_))
209    }
210
211    /// Returns the [HaltReason] value the EVM has experienced an exceptional halt
212    #[inline]
213    pub fn to_halt(self) -> Option<HaltReason> {
214        match self {
215            SuccessOrHalt::Halt(reason) => Some(reason),
216            _ => None,
217        }
218    }
219}
220
221impl From<InstructionResult> for SuccessOrHalt {
222    fn from(result: InstructionResult) -> Self {
223        match result {
224            InstructionResult::Continue => Self::InternalContinue, // used only in interpreter loop
225            InstructionResult::Stop => Self::Success(SuccessReason::Stop),
226            InstructionResult::Return => Self::Success(SuccessReason::Return),
227            InstructionResult::SelfDestruct => Self::Success(SuccessReason::SelfDestruct),
228            InstructionResult::Revert => Self::Revert,
229            InstructionResult::CallOrCreate => Self::InternalCallOrCreate, // used only in interpreter loop
230            InstructionResult::CallTooDeep => Self::Halt(HaltReason::CallTooDeep), // not gonna happen for first call
231            InstructionResult::OutOfFunds => Self::Halt(HaltReason::OutOfFunds), // Check for first call is done separately.
232            InstructionResult::OutOfGas => Self::Halt(HaltReason::OutOfGas(OutOfGasError::Basic)),
233            InstructionResult::MemoryLimitOOG => {
234                Self::Halt(HaltReason::OutOfGas(OutOfGasError::MemoryLimit))
235            }
236            InstructionResult::MemoryOOG => Self::Halt(HaltReason::OutOfGas(OutOfGasError::Memory)),
237            InstructionResult::PrecompileOOG => {
238                Self::Halt(HaltReason::OutOfGas(OutOfGasError::Precompile))
239            }
240            InstructionResult::InvalidOperandOOG => {
241                Self::Halt(HaltReason::OutOfGas(OutOfGasError::InvalidOperand))
242            }
243            InstructionResult::OpcodeNotFound | InstructionResult::ReturnContractInNotInitEOF => {
244                Self::Halt(HaltReason::OpcodeNotFound)
245            }
246            InstructionResult::CallNotAllowedInsideStatic => {
247                Self::Halt(HaltReason::CallNotAllowedInsideStatic)
248            } // first call is not static call
249            InstructionResult::StateChangeDuringStaticCall => {
250                Self::Halt(HaltReason::StateChangeDuringStaticCall)
251            }
252            InstructionResult::InvalidFEOpcode => Self::Halt(HaltReason::InvalidFEOpcode),
253            InstructionResult::InvalidJump => Self::Halt(HaltReason::InvalidJump),
254            InstructionResult::NotActivated => Self::Halt(HaltReason::NotActivated),
255            InstructionResult::StackUnderflow => Self::Halt(HaltReason::StackUnderflow),
256            InstructionResult::StackOverflow => Self::Halt(HaltReason::StackOverflow),
257            InstructionResult::OutOfOffset => Self::Halt(HaltReason::OutOfOffset),
258            InstructionResult::CreateCollision => Self::Halt(HaltReason::CreateCollision),
259            InstructionResult::OverflowPayment => Self::Halt(HaltReason::OverflowPayment), // Check for first call is done separately.
260            InstructionResult::PrecompileError => Self::Halt(HaltReason::PrecompileError),
261            InstructionResult::NonceOverflow => Self::Halt(HaltReason::NonceOverflow),
262            InstructionResult::CreateContractSizeLimit
263            | InstructionResult::CreateContractStartingWithEF => {
264                Self::Halt(HaltReason::CreateContractSizeLimit)
265            }
266            InstructionResult::CreateInitCodeSizeLimit => {
267                Self::Halt(HaltReason::CreateInitCodeSizeLimit)
268            }
269            InstructionResult::FatalExternalError => Self::FatalExternalError,
270            InstructionResult::EOFOpcodeDisabledInLegacy => Self::Halt(HaltReason::OpcodeNotFound),
271            InstructionResult::EOFFunctionStackOverflow => Self::FatalExternalError,
272            InstructionResult::ReturnContract => {
273                panic!("Unexpected EOF internal Return Contract")
274            }
275        }
276    }
277}
278
279#[cfg(test)]
280mod tests {
281    use crate::InstructionResult;
282
283    #[test]
284    fn all_results_are_covered() {
285        match InstructionResult::Continue {
286            return_error!() => {}
287            return_revert!() => {}
288            return_ok!() => {}
289            InstructionResult::CallOrCreate => {}
290        }
291    }
292
293    #[test]
294    fn test_results() {
295        let ok_results = vec![
296            InstructionResult::Continue,
297            InstructionResult::Stop,
298            InstructionResult::Return,
299            InstructionResult::SelfDestruct,
300        ];
301
302        for result in ok_results {
303            assert!(result.is_ok());
304            assert!(!result.is_revert());
305            assert!(!result.is_error());
306        }
307
308        let revert_results = vec![
309            InstructionResult::Revert,
310            InstructionResult::CallTooDeep,
311            InstructionResult::OutOfFunds,
312        ];
313
314        for result in revert_results {
315            assert!(!result.is_ok());
316            assert!(result.is_revert());
317            assert!(!result.is_error());
318        }
319
320        let error_results = vec![
321            InstructionResult::OutOfGas,
322            InstructionResult::MemoryOOG,
323            InstructionResult::MemoryLimitOOG,
324            InstructionResult::PrecompileOOG,
325            InstructionResult::InvalidOperandOOG,
326            InstructionResult::OpcodeNotFound,
327            InstructionResult::CallNotAllowedInsideStatic,
328            InstructionResult::StateChangeDuringStaticCall,
329            InstructionResult::InvalidFEOpcode,
330            InstructionResult::InvalidJump,
331            InstructionResult::NotActivated,
332            InstructionResult::StackUnderflow,
333            InstructionResult::StackOverflow,
334            InstructionResult::OutOfOffset,
335            InstructionResult::CreateCollision,
336            InstructionResult::OverflowPayment,
337            InstructionResult::PrecompileError,
338            InstructionResult::NonceOverflow,
339            InstructionResult::CreateContractSizeLimit,
340            InstructionResult::CreateContractStartingWithEF,
341            InstructionResult::CreateInitCodeSizeLimit,
342            InstructionResult::FatalExternalError,
343        ];
344
345        for result in error_results {
346            assert!(!result.is_ok());
347            assert!(!result.is_revert());
348            assert!(result.is_error());
349        }
350    }
351}