1use context_interface::{
2 host::LoadError,
3 journaled_state::TransferError,
4 result::{HaltReason, OutOfGasError, SuccessReason},
5};
6use core::fmt::Debug;
7
8pub type InstructionExecResult<T = (), E = InstructionResult> = Result<T, E>;
13
14#[repr(u8)]
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub enum InstructionResult {
22 #[default]
24 Stop = 1, Return,
27 SelfDestruct,
29 Suspend,
31
32 Revert = 0x10,
35 CallTooDeep,
37 OutOfFunds,
39 CreateInitCodeStartingEF00,
41 InvalidEOFInitCode,
43 InvalidExtDelegateCallTarget,
45
46 OutOfGas = 0x20,
49 MemoryOOG,
51 MemoryLimitOOG,
53 PrecompileOOG,
55 InvalidOperandOOG,
57 ReentrancySentryOOG,
59 OpcodeNotFound,
61 CallNotAllowedInsideStatic,
63 StateChangeDuringStaticCall,
65 InvalidFEOpcode,
67 InvalidJump,
69 NotActivated,
71 StackUnderflow,
73 StackOverflow,
75 OutOfOffset,
77 CreateCollision,
79 OverflowPayment,
81 PrecompileError,
83 NonceOverflow,
85 CreateContractSizeLimit,
87 CreateContractStartingWithEF,
89 CreateInitCodeSizeLimit,
91 FatalExternalError,
93 InvalidImmediateEncoding,
95}
96
97impl From<TransferError> for InstructionResult {
98 fn from(e: TransferError) -> Self {
99 match e {
100 TransferError::OutOfFunds => InstructionResult::OutOfFunds,
101 TransferError::OverflowPayment => InstructionResult::OverflowPayment,
102 TransferError::CreateCollision => InstructionResult::CreateCollision,
103 }
104 }
105}
106
107impl From<SuccessReason> for InstructionResult {
108 fn from(value: SuccessReason) -> Self {
109 match value {
110 SuccessReason::Return => InstructionResult::Return,
111 SuccessReason::Stop => InstructionResult::Stop,
112 SuccessReason::SelfDestruct => InstructionResult::SelfDestruct,
113 }
114 }
115}
116
117impl From<HaltReason> for InstructionResult {
118 fn from(value: HaltReason) -> Self {
119 match value {
120 HaltReason::OutOfGas(error) => match error {
121 OutOfGasError::Basic => Self::OutOfGas,
122 OutOfGasError::InvalidOperand => Self::InvalidOperandOOG,
123 OutOfGasError::Memory => Self::MemoryOOG,
124 OutOfGasError::MemoryLimit => Self::MemoryLimitOOG,
125 OutOfGasError::Precompile => Self::PrecompileOOG,
126 OutOfGasError::ReentrancySentry => Self::ReentrancySentryOOG,
127 },
128 HaltReason::OpcodeNotFound => Self::OpcodeNotFound,
129 HaltReason::InvalidFEOpcode => Self::InvalidFEOpcode,
130 HaltReason::InvalidJump => Self::InvalidJump,
131 HaltReason::NotActivated => Self::NotActivated,
132 HaltReason::StackOverflow => Self::StackOverflow,
133 HaltReason::StackUnderflow => Self::StackUnderflow,
134 HaltReason::OutOfOffset => Self::OutOfOffset,
135 HaltReason::CreateCollision => Self::CreateCollision,
136 HaltReason::PrecompileError => Self::PrecompileError,
137 HaltReason::PrecompileErrorWithContext(_) => Self::PrecompileError,
138 HaltReason::NonceOverflow => Self::NonceOverflow,
139 HaltReason::CreateContractSizeLimit => Self::CreateContractSizeLimit,
140 HaltReason::CreateContractStartingWithEF => Self::CreateContractStartingWithEF,
141 HaltReason::CreateInitCodeSizeLimit => Self::CreateInitCodeSizeLimit,
142 HaltReason::OverflowPayment => Self::OverflowPayment,
143 HaltReason::StateChangeDuringStaticCall => Self::StateChangeDuringStaticCall,
144 HaltReason::CallNotAllowedInsideStatic => Self::CallNotAllowedInsideStatic,
145 HaltReason::OutOfFunds => Self::OutOfFunds,
146 HaltReason::CallTooDeep => Self::CallTooDeep,
147 }
148 }
149}
150
151impl From<LoadError> for InstructionResult {
152 fn from(error: LoadError) -> Self {
153 match error {
154 LoadError::ColdLoadSkipped => Self::OutOfGas,
155 LoadError::DBError => Self::FatalExternalError,
156 }
157 }
158}
159
160#[macro_export]
163macro_rules! return_ok {
164 () => {
165 $crate::InstructionResult::Stop
166 | $crate::InstructionResult::Return
167 | $crate::InstructionResult::SelfDestruct
168 | $crate::InstructionResult::Suspend
169 };
170}
171
172#[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]
189macro_rules! return_error {
190 () => {
191 $crate::InstructionResult::OutOfGas
192 | $crate::InstructionResult::MemoryOOG
193 | $crate::InstructionResult::MemoryLimitOOG
194 | $crate::InstructionResult::PrecompileOOG
195 | $crate::InstructionResult::InvalidOperandOOG
196 | $crate::InstructionResult::ReentrancySentryOOG
197 | $crate::InstructionResult::OpcodeNotFound
198 | $crate::InstructionResult::CallNotAllowedInsideStatic
199 | $crate::InstructionResult::StateChangeDuringStaticCall
200 | $crate::InstructionResult::InvalidFEOpcode
201 | $crate::InstructionResult::InvalidJump
202 | $crate::InstructionResult::NotActivated
203 | $crate::InstructionResult::StackUnderflow
204 | $crate::InstructionResult::StackOverflow
205 | $crate::InstructionResult::OutOfOffset
206 | $crate::InstructionResult::CreateCollision
207 | $crate::InstructionResult::OverflowPayment
208 | $crate::InstructionResult::PrecompileError
209 | $crate::InstructionResult::NonceOverflow
210 | $crate::InstructionResult::CreateContractSizeLimit
211 | $crate::InstructionResult::CreateContractStartingWithEF
212 | $crate::InstructionResult::CreateInitCodeSizeLimit
213 | $crate::InstructionResult::FatalExternalError
214 | $crate::InstructionResult::InvalidImmediateEncoding
215 };
216}
217
218impl InstructionResult {
219 #[inline]
221 pub const fn is_ok(self) -> bool {
222 matches!(self, return_ok!())
223 }
224
225 #[inline]
226 pub const fn is_ok_or_revert(self) -> bool {
228 matches!(self, return_ok!() | return_revert!())
229 }
230
231 #[inline]
233 pub const fn is_revert(self) -> bool {
234 matches!(self, return_revert!())
235 }
236
237 #[inline]
239 #[deprecated(note = "use `is_halt` instead")]
240 pub const fn is_error(self) -> bool {
241 self.is_halt()
242 }
243
244 #[inline]
246 pub const fn is_halt(self) -> bool {
247 matches!(self, return_error!())
248 }
249}
250
251#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
253pub enum InternalResult {
254 CreateInitCodeStartingEF00,
256 InvalidExtDelegateCallTarget,
258 Suspend,
260}
261
262#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
263pub enum SuccessOrHalt<HaltReasonTr> {
266 Success(SuccessReason),
268 Revert,
270 Halt(HaltReasonTr),
272 FatalExternalError,
274 Internal(InternalResult),
276}
277
278impl<HaltReasonTr> SuccessOrHalt<HaltReasonTr> {
279 #[inline]
281 pub fn is_success(self) -> bool {
282 matches!(self, SuccessOrHalt::Success(_))
283 }
284
285 #[inline]
287 pub fn to_success(self) -> Option<SuccessReason> {
288 match self {
289 SuccessOrHalt::Success(reason) => Some(reason),
290 _ => None,
291 }
292 }
293
294 #[inline]
296 pub fn is_revert(self) -> bool {
297 matches!(self, SuccessOrHalt::Revert)
298 }
299
300 #[inline]
302 pub fn is_halt(self) -> bool {
303 matches!(self, SuccessOrHalt::Halt(_))
304 }
305
306 #[inline]
308 pub fn to_halt(self) -> Option<HaltReasonTr> {
309 match self {
310 SuccessOrHalt::Halt(reason) => Some(reason),
311 _ => None,
312 }
313 }
314}
315
316impl<HALT: From<HaltReason>> From<HaltReason> for SuccessOrHalt<HALT> {
317 fn from(reason: HaltReason) -> Self {
318 SuccessOrHalt::Halt(reason.into())
319 }
320}
321
322impl<HaltReasonTr: From<HaltReason>> From<InstructionResult> for SuccessOrHalt<HaltReasonTr> {
323 fn from(result: InstructionResult) -> Self {
324 match result {
325 InstructionResult::Stop => Self::Success(SuccessReason::Stop),
326 InstructionResult::Return => Self::Success(SuccessReason::Return),
327 InstructionResult::SelfDestruct => Self::Success(SuccessReason::SelfDestruct),
328 InstructionResult::Suspend => Self::Internal(InternalResult::Suspend),
329 InstructionResult::Revert => Self::Revert,
330 InstructionResult::CreateInitCodeStartingEF00 => Self::Revert,
331 InstructionResult::CallTooDeep => Self::Halt(HaltReason::CallTooDeep.into()), InstructionResult::OutOfFunds => Self::Halt(HaltReason::OutOfFunds.into()), InstructionResult::OutOfGas => {
334 Self::Halt(HaltReason::OutOfGas(OutOfGasError::Basic).into())
335 }
336 InstructionResult::MemoryLimitOOG => {
337 Self::Halt(HaltReason::OutOfGas(OutOfGasError::MemoryLimit).into())
338 }
339 InstructionResult::MemoryOOG => {
340 Self::Halt(HaltReason::OutOfGas(OutOfGasError::Memory).into())
341 }
342 InstructionResult::PrecompileOOG => {
343 Self::Halt(HaltReason::OutOfGas(OutOfGasError::Precompile).into())
344 }
345 InstructionResult::InvalidOperandOOG => {
346 Self::Halt(HaltReason::OutOfGas(OutOfGasError::InvalidOperand).into())
347 }
348 InstructionResult::ReentrancySentryOOG => {
349 Self::Halt(HaltReason::OutOfGas(OutOfGasError::ReentrancySentry).into())
350 }
351 InstructionResult::OpcodeNotFound => Self::Halt(HaltReason::OpcodeNotFound.into()),
352 InstructionResult::CallNotAllowedInsideStatic => {
353 Self::Halt(HaltReason::CallNotAllowedInsideStatic.into())
354 } InstructionResult::StateChangeDuringStaticCall => {
356 Self::Halt(HaltReason::StateChangeDuringStaticCall.into())
357 }
358 InstructionResult::InvalidFEOpcode => Self::Halt(HaltReason::InvalidFEOpcode.into()),
359 InstructionResult::InvalidJump => Self::Halt(HaltReason::InvalidJump.into()),
360 InstructionResult::NotActivated => Self::Halt(HaltReason::NotActivated.into()),
361 InstructionResult::StackUnderflow => Self::Halt(HaltReason::StackUnderflow.into()),
362 InstructionResult::StackOverflow => Self::Halt(HaltReason::StackOverflow.into()),
363 InstructionResult::OutOfOffset => Self::Halt(HaltReason::OutOfOffset.into()),
364 InstructionResult::CreateCollision => Self::Halt(HaltReason::CreateCollision.into()),
365 InstructionResult::OverflowPayment => Self::Halt(HaltReason::OverflowPayment.into()), InstructionResult::PrecompileError => Self::Halt(HaltReason::PrecompileError.into()),
367 InstructionResult::NonceOverflow => Self::Halt(HaltReason::NonceOverflow.into()),
368 InstructionResult::CreateContractSizeLimit => {
369 Self::Halt(HaltReason::CreateContractSizeLimit.into())
370 }
371 InstructionResult::CreateContractStartingWithEF => {
372 Self::Halt(HaltReason::CreateContractStartingWithEF.into())
373 }
374 InstructionResult::CreateInitCodeSizeLimit => {
375 Self::Halt(HaltReason::CreateInitCodeSizeLimit.into())
376 }
377 InstructionResult::InvalidEOFInitCode => Self::Revert,
379 InstructionResult::FatalExternalError => Self::FatalExternalError,
380 InstructionResult::InvalidExtDelegateCallTarget => {
381 Self::Internal(InternalResult::InvalidExtDelegateCallTarget)
382 }
383 InstructionResult::InvalidImmediateEncoding => {
384 Self::Halt(HaltReason::OpcodeNotFound.into())
385 }
386 }
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use crate::InstructionResult;
393
394 #[test]
395 fn exhaustiveness() {
396 match InstructionResult::Stop {
397 return_error!() => {}
398 return_revert!() => {}
399 return_ok!() => {}
400 }
401 }
402
403 #[test]
404 fn test_results() {
405 let ok_results = [
406 InstructionResult::Stop,
407 InstructionResult::Return,
408 InstructionResult::SelfDestruct,
409 ];
410 for result in ok_results {
411 assert!(result.is_ok());
412 assert!(!result.is_revert());
413 assert!(!result.is_halt());
414 }
415
416 let revert_results = [
417 InstructionResult::Revert,
418 InstructionResult::CallTooDeep,
419 InstructionResult::OutOfFunds,
420 ];
421 for result in revert_results {
422 assert!(!result.is_ok());
423 assert!(result.is_revert());
424 assert!(!result.is_halt());
425 }
426
427 let error_results = [
428 InstructionResult::OutOfGas,
429 InstructionResult::MemoryOOG,
430 InstructionResult::MemoryLimitOOG,
431 InstructionResult::PrecompileOOG,
432 InstructionResult::InvalidOperandOOG,
433 InstructionResult::OpcodeNotFound,
434 InstructionResult::CallNotAllowedInsideStatic,
435 InstructionResult::StateChangeDuringStaticCall,
436 InstructionResult::InvalidFEOpcode,
437 InstructionResult::InvalidJump,
438 InstructionResult::NotActivated,
439 InstructionResult::StackUnderflow,
440 InstructionResult::StackOverflow,
441 InstructionResult::OutOfOffset,
442 InstructionResult::CreateCollision,
443 InstructionResult::OverflowPayment,
444 InstructionResult::PrecompileError,
445 InstructionResult::NonceOverflow,
446 InstructionResult::CreateContractSizeLimit,
447 InstructionResult::CreateContractStartingWithEF,
448 InstructionResult::CreateInitCodeSizeLimit,
449 InstructionResult::FatalExternalError,
450 ];
451 for result in error_results {
452 assert!(!result.is_ok());
453 assert!(!result.is_revert());
454 assert!(result.is_halt());
455 }
456 }
457}