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