use bytes::Bytes;
use derive_more::derive::Display;
use ethrex_common::{
Address, H256, U256,
types::{FakeExponentialError, Log},
};
use serde::{Deserialize, Serialize};
use thiserror;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize, Display)]
pub enum VMError {
Internal(#[from] InternalError),
TxValidation(#[from] TxValidationError),
ExceptionalHalt(#[from] ExceptionalHalt),
RevertOpcode,
}
impl VMError {
pub fn should_propagate(&self) -> bool {
matches!(self, VMError::Internal(_))
}
pub fn is_revert_opcode(&self) -> bool {
matches!(self, VMError::RevertOpcode)
}
}
impl From<DatabaseError> for VMError {
fn from(err: DatabaseError) -> Self {
VMError::Internal(InternalError::Database(err))
}
}
impl From<PrecompileError> for VMError {
fn from(err: PrecompileError) -> Self {
VMError::ExceptionalHalt(ExceptionalHalt::Precompile(err))
}
}
impl From<std::array::TryFromSliceError> for VMError {
fn from(_: std::array::TryFromSliceError) -> Self {
VMError::Internal(InternalError::TypeConversion)
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
pub enum ExceptionalHalt {
#[error("Stack Underflow")]
StackUnderflow,
#[error("Stack Overflow")]
StackOverflow,
#[error("Invalid Jump")]
InvalidJump,
#[error("Opcode Not Allowed In Static Context")]
OpcodeNotAllowedInStaticContext,
#[error("Invalid Contract Prefix")]
InvalidContractPrefix,
#[error("Very Large Number")]
VeryLargeNumber,
#[error("Invalid Opcode")]
InvalidOpcode,
#[error("Address Already Occupied")]
AddressAlreadyOccupied,
#[error("Contract Output Too Big")]
ContractOutputTooBig,
#[error("Offset out of bounds")]
OutOfBounds,
#[error("Out Of Gas")]
OutOfGas,
#[error("Precompile execution error: {0}")]
Precompile(#[from] PrecompileError),
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
pub enum TxValidationError {
#[error("Sender account {0} shouldn't be a contract")]
SenderNotEOA(Address),
#[error("Insufficient account funds")]
InsufficientAccountFunds,
#[error("Nonce is max")]
NonceIsMax,
#[error("Nonce mismatch: expected {expected}, got {actual}")]
NonceMismatch { expected: u64, actual: u64 },
#[error("Initcode size exceeded, max size: {max_size}, actual size: {actual_size}")]
InitcodeSizeExceeded { max_size: usize, actual_size: usize },
#[error("Priority fee {priority_fee} is greater than max fee per gas {max_fee_per_gas}")]
PriorityGreaterThanMaxFeePerGas {
priority_fee: U256,
max_fee_per_gas: U256,
},
#[error("Transaction gas limit lower than the minimum gas cost to execute the transaction")]
IntrinsicGasTooLow,
#[error("Transaction gas limit lower than the gas cost floor for calldata tokens")]
IntrinsicGasBelowFloorGasCost,
#[error(
"Gas allowance exceeded. Block gas limit: {block_gas_limit}, transaction gas limit: {tx_gas_limit}"
)]
GasAllowanceExceeded {
block_gas_limit: u64,
tx_gas_limit: u64,
},
#[error("Insufficient max fee per gas")]
InsufficientMaxFeePerGas,
#[error(
"Insufficient max fee per blob gas. Expected at least {base_fee_per_blob_gas}, got: {tx_max_fee_per_blob_gas}"
)]
InsufficientMaxFeePerBlobGas {
base_fee_per_blob_gas: U256,
tx_max_fee_per_blob_gas: U256,
},
#[error("Type 3 transactions are not supported before the Cancun fork")]
Type3TxPreFork,
#[error("Type 3 transaction without blobs")]
Type3TxZeroBlobs,
#[error("Invalid blob versioned hash")]
Type3TxInvalidBlobVersionedHash,
#[error(
"Blob count exceeded. Max blob count: {max_blob_count}, actual blob count: {actual_blob_count}"
)]
Type3TxBlobCountExceeded {
max_blob_count: usize,
actual_blob_count: usize,
},
#[error("Contract creation in blob transaction")]
Type3TxContractCreation,
#[error("Type 4 transactions are not supported before the Prague fork")]
Type4TxPreFork,
#[error("Empty authorization list in type 4 transaction")]
Type4TxAuthorizationListIsEmpty,
#[error("Contract creation in type 4 transaction")]
Type4TxContractCreation,
#[error("Gas limit price product overflow")]
GasLimitPriceProductOverflow,
#[error(
"Transaction gas limit exceeds maximum. Transaction hash: {tx_hash}, transaction gas limit: {tx_gas_limit}"
)]
TxMaxGasLimitExceeded { tx_hash: H256, tx_gas_limit: u64 },
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
pub enum InternalError {
#[error("Arithmetic operation overflowed")]
Overflow,
#[error("Arithmetic operation underflowed")]
Underflow,
#[error("Cannot divide by zero")]
DivisionByZero,
#[error("Tried to convert one type to another")]
TypeConversion,
#[error("CallFrame not found")]
CallFrame,
#[error("Tried to slice non-existing data")]
Slicing,
#[error("Account not found when it should've been in the cache.")]
AccountNotFound,
#[error("Invalid precompile address. Tried to execute a precompile that does not exist.")]
InvalidPrecompileAddress,
#[error("Invalid Fork")]
InvalidFork,
#[error("Account should had been delegated")]
AccountNotDelegated,
#[error("No recipient found for privileged transaction")]
RecipientNotFoundForPrivilegedTransaction,
#[error("Memory Size Sverflow")]
MemorySizeOverflow,
#[error("Custom error: {0}")]
Custom(String),
#[error("Database access error: {0}")]
Database(#[from] DatabaseError),
#[error("{0}")]
FakeExponentialError(#[from] FakeExponentialError),
}
impl InternalError {
pub fn msg(msg: &'static str) -> Self {
Self::Custom(msg.to_owned())
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
pub enum PrecompileError {
#[error("Error while parsing the calldata")]
ParsingInputError,
#[error("There is not enough gas to execute precompiled contract")]
NotEnoughGas,
#[error("Invalid point")]
InvalidPoint,
#[error("The point is not in the subgroup")]
PointNotInSubgroup,
#[error("The G1 point is not in the curve")]
BLS12381G1PointNotInCurve,
#[error("The G2 point is not in the curve")]
BLS12381G2PointNotInCurve,
#[error("Mod-exp base length is too large")]
ModExpBaseTooLarge,
#[error("Mod-exp exponent length is too large")]
ModExpExpTooLarge,
#[error("Mod-exp modulus length is too large")]
ModExpModulusTooLarge,
#[error("Coordinate Exceeds Field Modulus")]
CoordinateExceedsFieldModulus,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
pub enum DatabaseError {
#[error("{0}")]
Custom(String),
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum OpcodeResult {
Continue,
Halt,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TxResult {
Success,
Revert(VMError),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExecutionReport {
pub result: TxResult,
pub gas_used: u64,
pub gas_spent: u64,
pub gas_refunded: u64,
pub state_gas_used: u64,
pub output: Bytes,
pub logs: Vec<Log>,
}
impl ExecutionReport {
pub fn is_success(&self) -> bool {
matches!(self.result, TxResult::Success)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContextResult {
pub result: TxResult,
pub gas_used: u64,
pub gas_spent: u64,
pub output: Bytes,
}
impl ContextResult {
pub fn is_success(&self) -> bool {
matches!(self.result, TxResult::Success)
}
pub fn is_collision(&self) -> bool {
matches!(
self.result,
TxResult::Revert(VMError::ExceptionalHalt(
ExceptionalHalt::AddressAlreadyOccupied
))
)
}
pub fn is_revert_opcode(&self) -> bool {
matches!(self.result, TxResult::Revert(VMError::RevertOpcode))
}
}