use alloy_evm::block::StateChangeSource;
pub use alloy_evm::InvalidTxError;
use alloy_primitives::Address;
pub use op_revm::{OpHaltReason, OpTransactionError};
use revm::{context::result::ExecutionResult, state::EvmState};
pub use revm::{
context::result::{EVMError, InvalidTransaction},
context_interface::{
result::HaltReason as EthHaltReason, transaction::TransactionError as TransactionErrorTr,
},
};
use serde::{Deserialize, Serialize};
use crate::VolatileDataAccess;
#[derive(Debug, Clone)]
pub struct MegaTransactionOutcome {
pub result: ExecutionResult<MegaHaltReason>,
pub state: EvmState,
pub data_size: u64,
pub kv_updates: u64,
pub compute_gas_used: u64,
pub state_growth_used: u64,
}
#[derive(Debug, Clone)]
pub struct MegaSystemCallOutcome {
pub source: StateChangeSource,
pub state: EvmState,
}
pub type MegaTransactionError = OpTransactionError;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MegaHaltReason {
Base(OpHaltReason),
DataLimitExceeded {
limit: u64,
actual: u64,
},
KVUpdateLimitExceeded {
limit: u64,
actual: u64,
},
ComputeGasLimitExceeded {
limit: u64,
actual: u64,
},
StateGrowthLimitExceeded {
limit: u64,
actual: u64,
},
SystemTxInvalidCallee {
callee: Address,
},
VolatileDataAccessOutOfGas {
access_type: VolatileDataAccess,
limit: u64,
actual: u64,
},
}
impl From<EthHaltReason> for MegaHaltReason {
fn from(value: EthHaltReason) -> Self {
Self::Base(OpHaltReason::Base(value))
}
}
impl From<OpHaltReason> for MegaHaltReason {
fn from(value: OpHaltReason) -> Self {
Self::Base(value)
}
}
impl TryFrom<MegaHaltReason> for EthHaltReason {
type Error = MegaHaltReason;
fn try_from(value: MegaHaltReason) -> Result<Self, Self::Error> {
match value {
MegaHaltReason::Base(reason) => Ok(reason.try_into()?),
MegaHaltReason::DataLimitExceeded { .. } |
MegaHaltReason::KVUpdateLimitExceeded { .. } |
MegaHaltReason::ComputeGasLimitExceeded { .. } |
MegaHaltReason::StateGrowthLimitExceeded { .. } |
MegaHaltReason::SystemTxInvalidCallee { .. } |
MegaHaltReason::VolatileDataAccessOutOfGas { .. } => Err(value),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use revm::context::result::OutOfGasError;
#[test]
fn test_base_halt_reasons_convert_roundtrip() {
let eth_reason = EthHaltReason::OutOfGas(OutOfGasError::Basic);
let mega_from_eth = MegaHaltReason::from(eth_reason);
let mega_from_op = MegaHaltReason::from(OpHaltReason::Base(eth_reason));
assert_eq!(mega_from_eth, MegaHaltReason::Base(OpHaltReason::Base(eth_reason)));
assert_eq!(mega_from_op, MegaHaltReason::Base(OpHaltReason::Base(eth_reason)));
assert_eq!(EthHaltReason::try_from(mega_from_eth).unwrap(), eth_reason);
assert_eq!(EthHaltReason::try_from(mega_from_op).unwrap(), eth_reason);
}
#[test]
fn test_mega_specific_halt_reasons_do_not_convert_to_eth() {
let reason = MegaHaltReason::VolatileDataAccessOutOfGas {
access_type: VolatileDataAccess::ORACLE,
limit: 100,
actual: 101,
};
assert_eq!(EthHaltReason::try_from(reason.clone()), Err(reason));
}
#[test]
fn test_op_specific_halt_reason_does_not_convert_to_eth() {
let op_reason = OpHaltReason::FailedDeposit;
let mega = MegaHaltReason::from(op_reason.clone());
assert_eq!(mega, MegaHaltReason::Base(op_reason));
assert!(EthHaltReason::try_from(mega).is_err());
}
#[test]
fn test_all_mega_specific_variants_fail_eth_conversion() {
let variants: Vec<MegaHaltReason> = vec![
MegaHaltReason::DataLimitExceeded { limit: 1, actual: 2 },
MegaHaltReason::KVUpdateLimitExceeded { limit: 1, actual: 2 },
MegaHaltReason::ComputeGasLimitExceeded { limit: 1, actual: 2 },
MegaHaltReason::StateGrowthLimitExceeded { limit: 1, actual: 2 },
MegaHaltReason::SystemTxInvalidCallee { callee: Address::ZERO },
];
for variant in variants {
assert!(
EthHaltReason::try_from(variant).is_err(),
"mega-specific variant should not convert to EthHaltReason"
);
}
}
}