use crate::transaction::TransactionError;
use core::fmt::{self, Debug};
use database_interface::DBErrorMarker;
use primitives::{Address, Bytes, Log, U256};
use specification::eip7702::InvalidAuthorization;
use state::EvmState;
use std::{boxed::Box, string::String, vec::Vec};
pub trait HaltReasonTrait: Clone + Debug + PartialEq + Eq + From<HaltReason> {}
impl<HaltReasonT> HaltReasonTrait for HaltReasonT where
HaltReasonT: Clone + Debug + PartialEq + Eq + From<HaltReason>
{
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ResultAndState<HaltReasonT: HaltReasonTrait> {
pub result: ExecutionResult<HaltReasonT>,
pub state: EvmState,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ExecutionResult<HaltReasonT: HaltReasonTrait> {
Success {
reason: SuccessReason,
gas_used: u64,
gas_refunded: u64,
logs: Vec<Log>,
output: Output,
},
Revert { gas_used: u64, output: Bytes },
Halt {
reason: HaltReasonT,
gas_used: u64,
},
}
impl<HaltReasonT: HaltReasonTrait> ExecutionResult<HaltReasonT> {
pub fn is_success(&self) -> bool {
matches!(self, Self::Success { .. })
}
pub fn is_halt(&self) -> bool {
matches!(self, Self::Halt { .. })
}
pub fn output(&self) -> Option<&Bytes> {
match self {
Self::Success { output, .. } => Some(output.data()),
Self::Revert { output, .. } => Some(output),
_ => None,
}
}
pub fn into_output(self) -> Option<Bytes> {
match self {
Self::Success { output, .. } => Some(output.into_data()),
Self::Revert { output, .. } => Some(output),
_ => None,
}
}
pub fn logs(&self) -> &[Log] {
match self {
Self::Success { logs, .. } => logs.as_slice(),
_ => &[],
}
}
pub fn into_logs(self) -> Vec<Log> {
match self {
Self::Success { logs, .. } => logs,
_ => Vec::new(),
}
}
pub fn gas_used(&self) -> u64 {
match *self {
Self::Success { gas_used, .. }
| Self::Revert { gas_used, .. }
| Self::Halt { gas_used, .. } => gas_used,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Output {
Call(Bytes),
Create(Bytes, Option<Address>),
}
impl Output {
pub fn into_data(self) -> Bytes {
match self {
Output::Call(data) => data,
Output::Create(data, _) => data,
}
}
pub fn data(&self) -> &Bytes {
match self {
Output::Call(data) => data,
Output::Create(data, _) => data,
}
}
pub fn address(&self) -> Option<&Address> {
match self {
Output::Call(_) => None,
Output::Create(_, address) => address.as_ref(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EVMError<DBError, TransactionError> {
Transaction(TransactionError),
Header(InvalidHeader),
Database(DBError),
Custom(String),
Precompile(String),
}
impl<DBError: DBErrorMarker, TX> From<DBError> for EVMError<DBError, TX> {
fn from(value: DBError) -> Self {
Self::Database(value)
}
}
pub trait FromStringError {
fn from_string(value: String) -> Self;
}
impl<DB, TX> FromStringError for EVMError<DB, TX> {
fn from_string(value: String) -> Self {
Self::Custom(value)
}
}
impl<DB, TXERROR: From<InvalidTransaction>> From<InvalidTransaction> for EVMError<DB, TXERROR> {
fn from(value: InvalidTransaction) -> Self {
Self::Transaction(value.into())
}
}
impl<DBError, TransactionValidationErrorT> EVMError<DBError, TransactionValidationErrorT> {
pub fn map_db_err<F, E>(self, op: F) -> EVMError<E, TransactionValidationErrorT>
where
F: FnOnce(DBError) -> E,
{
match self {
Self::Transaction(e) => EVMError::Transaction(e),
Self::Header(e) => EVMError::Header(e),
Self::Database(e) => EVMError::Database(op(e)),
Self::Precompile(e) => EVMError::Precompile(e),
Self::Custom(e) => EVMError::Custom(e),
}
}
}
impl<DBError, TransactionValidationErrorT> core::error::Error
for EVMError<DBError, TransactionValidationErrorT>
where
DBError: core::error::Error + 'static,
TransactionValidationErrorT: core::error::Error + 'static,
{
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Transaction(e) => Some(e),
Self::Header(e) => Some(e),
Self::Database(e) => Some(e),
Self::Precompile(_) | Self::Custom(_) => None,
}
}
}
impl<DBError, TransactionValidationErrorT> fmt::Display
for EVMError<DBError, TransactionValidationErrorT>
where
DBError: fmt::Display,
TransactionValidationErrorT: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Transaction(e) => write!(f, "transaction validation error: {e}"),
Self::Header(e) => write!(f, "header validation error: {e}"),
Self::Database(e) => write!(f, "database error: {e}"),
Self::Precompile(e) | Self::Custom(e) => f.write_str(e),
}
}
}
impl<DBError, TransactionValidationErrorT> From<InvalidHeader>
for EVMError<DBError, TransactionValidationErrorT>
{
fn from(value: InvalidHeader) -> Self {
Self::Header(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum InvalidTransaction {
PriorityFeeGreaterThanMaxFee,
GasPriceLessThanBasefee,
CallerGasLimitMoreThanBlock,
CallGasCostMoreThanGasLimit,
RejectCallerWithCode,
LackOfFundForMaxFee {
fee: Box<U256>,
balance: Box<U256>,
},
OverflowPaymentInTransaction,
NonceOverflowInTransaction,
NonceTooHigh {
tx: u64,
state: u64,
},
NonceTooLow {
tx: u64,
state: u64,
},
CreateInitCodeSizeLimit,
InvalidChainId,
AccessListNotSupported,
MaxFeePerBlobGasNotSupported,
BlobVersionedHashesNotSupported,
BlobGasPriceGreaterThanMax,
EmptyBlobs,
BlobCreateTransaction,
TooManyBlobs {
max: usize,
have: usize,
},
BlobVersionNotSupported,
EofCrateShouldHaveToAddress,
AuthorizationListNotSupported,
AuthorizationListInvalidFields,
EmptyAuthorizationList,
InvalidAuthorizationList(InvalidAuthorization),
Eip2930NotSupported,
Eip1559NotSupported,
Eip4844NotSupported,
Eip7702NotSupported,
}
impl TransactionError for InvalidTransaction {}
impl From<InvalidAuthorization> for InvalidTransaction {
fn from(value: InvalidAuthorization) -> Self {
Self::InvalidAuthorizationList(value)
}
}
impl core::error::Error for InvalidTransaction {}
impl fmt::Display for InvalidTransaction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::PriorityFeeGreaterThanMaxFee => {
write!(f, "priority fee is greater than max fee")
}
Self::GasPriceLessThanBasefee => {
write!(f, "gas price is less than basefee")
}
Self::CallerGasLimitMoreThanBlock => {
write!(f, "caller gas limit exceeds the block gas limit")
}
Self::CallGasCostMoreThanGasLimit => {
write!(f, "call gas cost exceeds the gas limit")
}
Self::RejectCallerWithCode => {
write!(f, "reject transactions from senders with deployed code")
}
Self::LackOfFundForMaxFee { fee, balance } => {
write!(f, "lack of funds ({balance}) for max fee ({fee})")
}
Self::OverflowPaymentInTransaction => {
write!(f, "overflow payment in transaction")
}
Self::NonceOverflowInTransaction => {
write!(f, "nonce overflow in transaction")
}
Self::NonceTooHigh { tx, state } => {
write!(f, "nonce {tx} too high, expected {state}")
}
Self::NonceTooLow { tx, state } => {
write!(f, "nonce {tx} too low, expected {state}")
}
Self::CreateInitCodeSizeLimit => {
write!(f, "create initcode size limit")
}
Self::InvalidChainId => write!(f, "invalid chain ID"),
Self::AccessListNotSupported => write!(f, "access list not supported"),
Self::MaxFeePerBlobGasNotSupported => {
write!(f, "max fee per blob gas not supported")
}
Self::BlobVersionedHashesNotSupported => {
write!(f, "blob versioned hashes not supported")
}
Self::BlobGasPriceGreaterThanMax => {
write!(f, "blob gas price is greater than max fee per blob gas")
}
Self::EmptyBlobs => write!(f, "empty blobs"),
Self::BlobCreateTransaction => write!(f, "blob create transaction"),
Self::TooManyBlobs { max, have } => {
write!(f, "too many blobs, have {have}, max {max}")
}
Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
Self::EofCrateShouldHaveToAddress => write!(f, "EOF crate should have `to` address"),
Self::AuthorizationListNotSupported => write!(f, "authorization list not supported"),
Self::AuthorizationListInvalidFields => {
write!(f, "authorization list tx has invalid fields")
}
Self::EmptyAuthorizationList => write!(f, "empty authorization list"),
Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"),
Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"),
Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"),
Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"),
Self::InvalidAuthorizationList(i) => fmt::Display::fmt(i, f),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum InvalidHeader {
PrevrandaoNotSet,
ExcessBlobGasNotSet,
}
impl core::error::Error for InvalidHeader {}
impl fmt::Display for InvalidHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::PrevrandaoNotSet => write!(f, "`prevrandao` not set"),
Self::ExcessBlobGasNotSet => write!(f, "`excess_blob_gas` not set"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SuccessReason {
Stop,
Return,
SelfDestruct,
EofReturnContract,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum HaltReason {
OutOfGas(OutOfGasError),
OpcodeNotFound,
InvalidFEOpcode,
InvalidJump,
NotActivated,
StackUnderflow,
StackOverflow,
OutOfOffset,
CreateCollision,
PrecompileError,
NonceOverflow,
CreateContractSizeLimit,
CreateContractStartingWithEF,
CreateInitCodeSizeLimit,
OverflowPayment,
StateChangeDuringStaticCall,
CallNotAllowedInsideStatic,
OutOfFunds,
CallTooDeep,
EofAuxDataOverflow,
EofAuxDataTooSmall,
SubRoutineStackOverflow,
InvalidEXTCALLTarget,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OutOfGasError {
Basic,
MemoryLimit,
Memory,
Precompile,
InvalidOperand,
ReentrancySentry,
}