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(
107 "Gas allowance exceeded. Block gas limit: {block_gas_limit}, transaction gas limit: {tx_gas_limit}"
108 )]
109 GasAllowanceExceeded {
110 block_gas_limit: u64,
111 tx_gas_limit: u64,
112 },
113 #[error("Insufficient max fee per gas")]
114 InsufficientMaxFeePerGas,
115 #[error(
116 "Insufficient max fee per blob gas. Expected at least {base_fee_per_blob_gas}, got: {tx_max_fee_per_blob_gas}"
117 )]
118 InsufficientMaxFeePerBlobGas {
119 base_fee_per_blob_gas: U256,
120 tx_max_fee_per_blob_gas: U256,
121 },
122 #[error("Type 3 transactions are not supported before the Cancun fork")]
123 Type3TxPreFork,
124 #[error("Type 3 transaction without blobs")]
125 Type3TxZeroBlobs,
126 #[error("Invalid blob versioned hash")]
127 Type3TxInvalidBlobVersionedHash,
128 #[error(
129 "Blob count exceeded. Max blob count: {max_blob_count}, actual blob count: {actual_blob_count}"
130 )]
131 Type3TxBlobCountExceeded {
132 max_blob_count: usize,
133 actual_blob_count: usize,
134 },
135 #[error("Contract creation in blob transaction")]
136 Type3TxContractCreation,
137 #[error("Type 4 transactions are not supported before the Prague fork")]
138 Type4TxPreFork,
139 #[error("Empty authorization list in type 4 transaction")]
140 Type4TxAuthorizationListIsEmpty,
141 #[error("Contract creation in type 4 transaction")]
142 Type4TxContractCreation,
143 #[error("Frame transactions (EIP-8141) are not supported before the Hegota fork")]
144 FrameTxPreFork,
145 #[error("Gas limit price product overflow")]
146 GasLimitPriceProductOverflow,
147 #[error(
148 "Transaction gas limit exceeds maximum. Transaction hash: {tx_hash}, transaction gas limit: {tx_gas_limit}"
149 )]
150 TxMaxGasLimitExceeded { tx_hash: H256, tx_gas_limit: u64 },
151 #[error("Invalid frame transaction: VERIFY frame did not call APPROVE or payer not approved")]
152 InvalidFrameTransaction,
153}
154
155#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
156pub enum InternalError {
157 #[error("Arithmetic operation overflowed")]
158 Overflow,
159 #[error("Arithmetic operation underflowed")]
160 Underflow,
161 #[error("Cannot divide by zero")]
162 DivisionByZero,
163 #[error("Tried to convert one type to another")]
164 TypeConversion,
165 #[error("CallFrame not found")]
166 CallFrame,
167 #[error("Tried to slice non-existing data")]
168 Slicing,
169 #[error("Account not found when it should've been in the cache.")]
170 AccountNotFound,
171 #[error("Invalid precompile address. Tried to execute a precompile that does not exist.")]
172 InvalidPrecompileAddress,
173 #[error("Invalid Fork")]
174 InvalidFork,
175 #[error("Account should had been delegated")]
176 AccountNotDelegated,
177 #[error("No recipient found for privileged transaction")]
178 RecipientNotFoundForPrivilegedTransaction,
179 #[error("Memory Size Sverflow")]
180 MemorySizeOverflow,
181 #[error("Custom error: {0}")]
182 Custom(String),
183 #[error("Database access error: {0}")]
185 Database(#[from] DatabaseError),
186 #[error("{0}")]
187 FakeExponentialError(#[from] FakeExponentialError),
188}
189
190impl InternalError {
191 pub fn msg(msg: &'static str) -> Self {
192 Self::Custom(msg.to_owned())
193 }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
197pub enum PrecompileError {
198 #[error("Error while parsing the calldata")]
199 ParsingInputError,
200 #[error("There is not enough gas to execute precompiled contract")]
201 NotEnoughGas,
202 #[error("Invalid point")]
203 InvalidPoint,
204 #[error("The point is not in the subgroup")]
205 PointNotInSubgroup,
206 #[error("The G1 point is not in the curve")]
207 BLS12381G1PointNotInCurve,
208 #[error("The G2 point is not in the curve")]
209 BLS12381G2PointNotInCurve,
210 #[error("Mod-exp base length is too large")]
211 ModExpBaseTooLarge,
212 #[error("Mod-exp exponent length is too large")]
213 ModExpExpTooLarge,
214 #[error("Mod-exp modulus length is too large")]
215 ModExpModulusTooLarge,
216 #[error("Coordinate Exceeds Field Modulus")]
217 CoordinateExceedsFieldModulus,
218}
219
220#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
221pub enum DatabaseError {
222 #[error("{0}")]
223 Custom(String),
224}
225
226#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
227pub enum OpcodeResult {
228 Continue,
229 Halt,
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
233pub enum TxResult {
234 Success,
235 Revert(VMError),
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
239pub struct ExecutionReport {
240 pub result: TxResult,
241 pub gas_used: u64,
245 pub gas_spent: u64,
250 pub gas_refunded: u64,
251 pub state_gas_used: u64,
254 pub output: Bytes,
255 pub logs: Vec<Log>,
256 pub payer_address: Option<Address>,
258 pub frame_results: Option<Vec<(u8, u64, Vec<Log>)>>,
262}
263
264impl ExecutionReport {
265 pub fn is_success(&self) -> bool {
266 matches!(self.result, TxResult::Success)
267 }
268}
269
270#[derive(Debug, Clone, PartialEq, Eq)]
271pub struct ContextResult {
272 pub result: TxResult,
273 pub gas_used: u64,
275 pub gas_spent: u64,
277 pub output: Bytes,
278}
279
280impl ContextResult {
281 pub fn is_success(&self) -> bool {
282 matches!(self.result, TxResult::Success)
283 }
284
285 pub fn is_collision(&self) -> bool {
287 matches!(
288 self.result,
289 TxResult::Revert(VMError::ExceptionalHalt(
290 ExceptionalHalt::AddressAlreadyOccupied
291 ))
292 )
293 }
294
295 pub fn is_revert_opcode(&self) -> bool {
299 matches!(self.result, TxResult::Revert(VMError::RevertOpcode))
300 }
301}