1use bytes::Bytes;
2use derive_more::derive::Display;
3use ethrex_common::{
4 Address, H256, U256,
5 types::{FakeExponentialError, Log},
6};
7use serde::{Deserialize, Serialize};
8use thiserror;
9
10#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize, Display)]
11pub enum VMError {
12 Internal(#[from] InternalError),
14 TxValidation(#[from] TxValidationError),
16 ExceptionalHalt(#[from] ExceptionalHalt),
18 RevertOpcode,
20}
21
22impl VMError {
23 pub fn should_propagate(&self) -> bool {
26 matches!(self, VMError::Internal(_))
27 }
28
29 pub fn is_revert_opcode(&self) -> bool {
31 matches!(self, VMError::RevertOpcode)
32 }
33}
34
35impl From<DatabaseError> for VMError {
36 fn from(err: DatabaseError) -> Self {
37 VMError::Internal(InternalError::Database(err))
38 }
39}
40
41impl From<PrecompileError> for VMError {
42 fn from(err: PrecompileError) -> Self {
43 VMError::ExceptionalHalt(ExceptionalHalt::Precompile(err))
44 }
45}
46
47impl From<std::array::TryFromSliceError> for VMError {
50 fn from(_: std::array::TryFromSliceError) -> Self {
51 VMError::Internal(InternalError::TypeConversion)
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
56pub enum ExceptionalHalt {
57 #[error("Stack Underflow")]
58 StackUnderflow,
59 #[error("Stack Overflow")]
60 StackOverflow,
61 #[error("Invalid Jump")]
62 InvalidJump,
63 #[error("Opcode Not Allowed In Static Context")]
64 OpcodeNotAllowedInStaticContext,
65 #[error("Invalid Contract Prefix")]
66 InvalidContractPrefix,
67 #[error("Very Large Number")]
68 VeryLargeNumber,
69 #[error("Invalid Opcode")]
70 InvalidOpcode,
71 #[error("Address Already Occupied")]
72 AddressAlreadyOccupied,
73 #[error("Contract Output Too Big")]
74 ContractOutputTooBig,
75 #[error("Offset out of bounds")]
76 OutOfBounds,
77 #[error("Out Of Gas")]
78 OutOfGas,
79 #[error("Precompile execution error: {0}")]
80 Precompile(#[from] PrecompileError),
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
86pub enum TxValidationError {
87 #[error("Sender account {0} shouldn't be a contract")]
88 SenderNotEOA(Address),
89 #[error("Insufficient account funds")]
90 InsufficientAccountFunds,
91 #[error("Nonce is max")]
92 NonceIsMax,
93 #[error("Nonce mismatch: expected {expected}, got {actual}")]
94 NonceMismatch { expected: u64, actual: u64 },
95 #[error("Initcode size exceeded, max size: {max_size}, actual size: {actual_size}")]
96 InitcodeSizeExceeded { max_size: usize, actual_size: usize },
97 #[error("Priority fee {priority_fee} is greater than max fee per gas {max_fee_per_gas}")]
98 PriorityGreaterThanMaxFeePerGas {
99 priority_fee: U256,
100 max_fee_per_gas: U256,
101 },
102 #[error("Transaction gas limit lower than the minimum gas cost to execute the transaction")]
103 IntrinsicGasTooLow,
104 #[error("Transaction gas limit lower than the gas cost floor for calldata tokens")]
105 IntrinsicGasBelowFloorGasCost,
106 #[error("Transaction gas limit cannot cover the reserved L1 data availability gas")]
115 L1GasReservationTooLow,
116 #[error(
117 "Gas allowance exceeded. Block gas limit: {block_gas_limit}, transaction gas limit: {tx_gas_limit}"
118 )]
119 GasAllowanceExceeded {
120 block_gas_limit: u64,
121 tx_gas_limit: u64,
122 },
123 #[error("Insufficient max fee per gas")]
124 InsufficientMaxFeePerGas,
125 #[error(
126 "Insufficient max fee per blob gas. Expected at least {base_fee_per_blob_gas}, got: {tx_max_fee_per_blob_gas}"
127 )]
128 InsufficientMaxFeePerBlobGas {
129 base_fee_per_blob_gas: U256,
130 tx_max_fee_per_blob_gas: U256,
131 },
132 #[error("Type 3 transactions are not supported before the Cancun fork")]
133 Type3TxPreFork,
134 #[error("Type 3 transaction without blobs")]
135 Type3TxZeroBlobs,
136 #[error("Invalid blob versioned hash")]
137 Type3TxInvalidBlobVersionedHash,
138 #[error(
139 "Blob count exceeded. Max blob count: {max_blob_count}, actual blob count: {actual_blob_count}"
140 )]
141 Type3TxBlobCountExceeded {
142 max_blob_count: usize,
143 actual_blob_count: usize,
144 },
145 #[error("Contract creation in blob transaction")]
146 Type3TxContractCreation,
147 #[error("Type 4 transactions are not supported before the Prague fork")]
148 Type4TxPreFork,
149 #[error("Empty authorization list in type 4 transaction")]
150 Type4TxAuthorizationListIsEmpty,
151 #[error("Contract creation in type 4 transaction")]
152 Type4TxContractCreation,
153 #[error("Frame transactions (EIP-8141) are not supported before the Hegota fork")]
154 FrameTxPreFork,
155 #[error("Gas limit price product overflow")]
156 GasLimitPriceProductOverflow,
157 #[error(
158 "Transaction gas limit exceeds maximum. Transaction hash: {tx_hash}, transaction gas limit: {tx_gas_limit}"
159 )]
160 TxMaxGasLimitExceeded { tx_hash: H256, tx_gas_limit: u64 },
161 #[error("Invalid frame transaction: VERIFY frame did not call APPROVE or payer not approved")]
162 InvalidFrameTransaction,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
166pub enum InternalError {
167 #[error("Arithmetic operation overflowed")]
168 Overflow,
169 #[error("Arithmetic operation underflowed")]
170 Underflow,
171 #[error("Cannot divide by zero")]
172 DivisionByZero,
173 #[error("Tried to convert one type to another")]
174 TypeConversion,
175 #[error("CallFrame not found")]
176 CallFrame,
177 #[error("Tried to slice non-existing data")]
178 Slicing,
179 #[error("Account not found when it should've been in the cache.")]
180 AccountNotFound,
181 #[error("Invalid precompile address. Tried to execute a precompile that does not exist.")]
182 InvalidPrecompileAddress,
183 #[error("Invalid Fork")]
184 InvalidFork,
185 #[error("Account should had been delegated")]
186 AccountNotDelegated,
187 #[error("No recipient found for privileged transaction")]
188 RecipientNotFoundForPrivilegedTransaction,
189 #[error("Memory Size Sverflow")]
190 MemorySizeOverflow,
191 #[error("Custom error: {0}")]
192 Custom(String),
193 #[error("Database access error: {0}")]
195 Database(#[from] DatabaseError),
196 #[error("{0}")]
197 FakeExponentialError(#[from] FakeExponentialError),
198}
199
200impl InternalError {
201 pub fn msg(msg: &'static str) -> Self {
202 Self::Custom(msg.to_owned())
203 }
204}
205
206#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
207pub enum PrecompileError {
208 #[error("Error while parsing the calldata")]
209 ParsingInputError,
210 #[error("There is not enough gas to execute precompiled contract")]
211 NotEnoughGas,
212 #[error("Invalid point")]
213 InvalidPoint,
214 #[error("The point is not in the subgroup")]
215 PointNotInSubgroup,
216 #[error("The G1 point is not in the curve")]
217 BLS12381G1PointNotInCurve,
218 #[error("The G2 point is not in the curve")]
219 BLS12381G2PointNotInCurve,
220 #[error("Mod-exp base length is too large")]
221 ModExpBaseTooLarge,
222 #[error("Mod-exp exponent length is too large")]
223 ModExpExpTooLarge,
224 #[error("Mod-exp modulus length is too large")]
225 ModExpModulusTooLarge,
226 #[error("Coordinate Exceeds Field Modulus")]
227 CoordinateExceedsFieldModulus,
228 #[error("EXECUTE: stateless validation failed")]
231 ExecuteValidationFailed,
232 #[error("EXECUTE: invalid input for native-rollup execution")]
235 ExecuteInvalidInput,
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
239pub enum DatabaseError {
240 #[error("{0}")]
241 Custom(String),
242}
243
244#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
245pub enum OpcodeResult {
246 Continue,
247 Halt,
248}
249
250#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
251pub enum TxResult {
252 Success,
253 Revert(VMError),
254}
255
256#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
257pub struct ExecutionReport {
258 pub result: TxResult,
259 pub gas_used: u64,
263 pub gas_spent: u64,
268 pub gas_refunded: u64,
269 pub state_gas_used: u64,
272 pub output: Bytes,
273 pub logs: Vec<Log>,
274 pub payer_address: Option<Address>,
276 pub frame_results: Option<Vec<(u8, u64, Vec<Log>)>>,
280}
281
282impl ExecutionReport {
283 pub fn is_success(&self) -> bool {
284 matches!(self.result, TxResult::Success)
285 }
286}
287
288#[derive(Debug, Clone, PartialEq, Eq)]
289pub struct ContextResult {
290 pub result: TxResult,
291 pub gas_used: u64,
293 pub gas_spent: u64,
295 pub output: Bytes,
296}
297
298impl ContextResult {
299 pub fn is_success(&self) -> bool {
300 matches!(self.result, TxResult::Success)
301 }
302
303 pub fn is_collision(&self) -> bool {
305 matches!(
306 self.result,
307 TxResult::Revert(VMError::ExceptionalHalt(
308 ExceptionalHalt::AddressAlreadyOccupied
309 ))
310 )
311 }
312
313 pub fn is_revert_opcode(&self) -> bool {
317 matches!(self.result, TxResult::Revert(VMError::RevertOpcode))
318 }
319}