evm_interpreter/error/
exit.rs

1use alloc::borrow::Cow;
2use core::fmt;
3
4use crate::opcode::Opcode;
5
6/// Exit result.
7pub type ExitResult = Result<ExitSucceed, ExitError>;
8
9/// Exit reason.
10#[derive(Clone, Debug, Eq, PartialEq)]
11#[cfg_attr(
12	feature = "scale",
13	derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo)
14)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub enum ExitError {
17	/// Machine returns a normal EVM error.
18	Exception(ExitException),
19	/// Machine encountered an explicit revert.
20	Reverted,
21	/// Machine encountered an error that is not supposed to be normal EVM
22	/// errors, such as requiring too much memory to execute.
23	Fatal(ExitFatal),
24}
25
26impl From<ExitError> for ExitResult {
27	fn from(s: ExitError) -> Self {
28		Err(s)
29	}
30}
31
32#[cfg(feature = "std")]
33impl std::error::Error for ExitError {}
34
35impl fmt::Display for ExitError {
36	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37		match self {
38			Self::Exception(_) => f.write_str("EVM exit exception"),
39			Self::Reverted => f.write_str("EVM internal revert"),
40			Self::Fatal(_) => f.write_str("EVM fatal error"),
41		}
42	}
43}
44
45/// Exit succeed reason.
46#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47#[cfg_attr(
48	feature = "scale",
49	derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo)
50)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub enum ExitSucceed {
53	/// Machine encountered an explicit stop.
54	Stopped,
55	/// Machine encountered an explicit return.
56	Returned,
57	/// Machine encountered an explicit suicide.
58	Suicided,
59}
60
61impl From<ExitSucceed> for ExitResult {
62	fn from(s: ExitSucceed) -> Self {
63		Ok(s)
64	}
65}
66
67/// Exit error reason.
68#[derive(Clone, Debug, Eq, PartialEq)]
69#[cfg_attr(
70	feature = "scale",
71	derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo)
72)]
73#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
74pub enum ExitException {
75	/// Trying to pop from an empty stack.
76	#[cfg_attr(feature = "scale", codec(index = 0))]
77	StackUnderflow,
78	/// Trying to push into a stack over stack limit.
79	#[cfg_attr(feature = "scale", codec(index = 1))]
80	StackOverflow,
81	/// Jump destination is invalid.
82	#[cfg_attr(feature = "scale", codec(index = 2))]
83	InvalidJump,
84	/// An opcode accesses memory region, but the region is invalid.
85	#[cfg_attr(feature = "scale", codec(index = 3))]
86	InvalidRange,
87	/// Encountered the designated invalid opcode.
88	#[cfg_attr(feature = "scale", codec(index = 4))]
89	DesignatedInvalid,
90	/// Call stack is too deep (runtime).
91	#[cfg_attr(feature = "scale", codec(index = 5))]
92	CallTooDeep,
93	/// Create opcode encountered collision (runtime).
94	#[cfg_attr(feature = "scale", codec(index = 6))]
95	CreateCollision,
96	/// Create init code exceeds limit (runtime).
97	#[cfg_attr(feature = "scale", codec(index = 7))]
98	CreateContractLimit,
99
100	/// Invalid opcode during execution or starting byte is 0xef ([EIP-3541](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3541.md)).
101	#[cfg_attr(feature = "scale", codec(index = 15))]
102	InvalidOpcode(Opcode),
103
104	/// An opcode accesses external information, but the request is off offset
105	/// limit (runtime).
106	#[cfg_attr(feature = "scale", codec(index = 8))]
107	OutOfOffset,
108	/// Execution runs out of gas (runtime).
109	#[cfg_attr(feature = "scale", codec(index = 9))]
110	OutOfGas,
111	/// Not enough fund to start the execution (runtime).
112	#[cfg_attr(feature = "scale", codec(index = 10))]
113	OutOfFund,
114
115	/// PC underflowed (unused).
116	#[allow(clippy::upper_case_acronyms)]
117	#[cfg_attr(feature = "scale", codec(index = 11))]
118	PCUnderflow,
119
120	/// Attempt to create an empty account (runtime, unused).
121	#[cfg_attr(feature = "scale", codec(index = 12))]
122	CreateEmpty,
123
124	/// Nonce reached maximum value of 2^64-1
125	/// https://eips.ethereum.org/EIPS/eip-2681
126	#[cfg_attr(feature = "scale", codec(index = 14))]
127	MaxNonce,
128
129	/// Not EOA account called from transaction.
130	#[cfg_attr(feature = "scale", codec(index = 25))]
131	NotEOA,
132
133	/// Other normal errors.
134	#[cfg_attr(feature = "scale", codec(index = 13))]
135	Other(Cow<'static, str>),
136}
137
138impl From<ExitException> for ExitResult {
139	fn from(s: ExitException) -> Self {
140		Err(ExitError::Exception(s))
141	}
142}
143
144impl From<ExitException> for ExitError {
145	fn from(s: ExitException) -> Self {
146		Self::Exception(s)
147	}
148}
149
150/// Exit fatal reason.
151#[derive(Clone, Debug, Eq, PartialEq)]
152#[cfg_attr(
153	feature = "scale",
154	derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo)
155)]
156#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
157pub enum ExitFatal {
158	/// The operation is not supported.
159	NotSupported,
160	/// The trap (interrupt) is unhandled.
161	UnhandledInterrupt,
162	/// The environment explicitly set call errors as fatal error.
163	ExceptionAsFatal(ExitException),
164	/// Already exited.
165	AlreadyExited,
166	/// Unfinished execution.
167	Unfinished,
168	/// Uneven substate.
169	UnevenSubstate,
170	/// Invalid feedback.
171	InvalidFeedback,
172
173	/// Other fatal errors.
174	Other(Cow<'static, str>),
175}
176
177impl From<ExitFatal> for ExitResult {
178	fn from(s: ExitFatal) -> Self {
179		Err(ExitError::Fatal(s))
180	}
181}
182
183impl From<ExitFatal> for ExitError {
184	fn from(s: ExitFatal) -> Self {
185		Self::Fatal(s)
186	}
187}