use core::{
fmt::{self, Display},
str::FromStr,
};
pub use op_revm::OpSpecId;
pub use revm::primitives::hardfork::{SpecId as EthSpecId, UnknownHardfork};
use serde::{Deserialize, Serialize};
#[repr(u8)]
#[derive(
Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize,
)]
#[allow(non_camel_case_types, clippy::upper_case_acronyms, missing_docs)]
#[non_exhaustive]
pub enum MegaSpecId {
EQUIVALENCE,
MINI_REX,
REX,
REX1,
REX2,
REX3,
REX4,
#[default]
REX5,
}
#[allow(missing_docs)]
pub mod name {
pub const EQUIVALENCE: &str = "Equivalence";
pub const MINI_REX: &str = "MiniRex";
pub const REX: &str = "Rex";
pub const REX1: &str = "Rex1";
pub const REX2: &str = "Rex2";
pub const REX3: &str = "Rex3";
pub const REX4: &str = "Rex4";
pub const REX5: &str = "Rex5";
}
impl MegaSpecId {
pub const fn into_eth_spec(self) -> EthSpecId {
self.into_op_spec().into_eth_spec()
}
pub const fn into_op_spec(self) -> OpSpecId {
match self {
Self::MINI_REX |
Self::EQUIVALENCE |
Self::REX |
Self::REX1 |
Self::REX2 |
Self::REX3 |
Self::REX4 |
Self::REX5 => OpSpecId::ISTHMUS,
}
}
pub const fn is_enabled(self, other: Self) -> bool {
other as u8 <= self as u8
}
}
impl From<MegaSpecId> for &'static str {
fn from(spec_id: MegaSpecId) -> Self {
match spec_id {
MegaSpecId::EQUIVALENCE => name::EQUIVALENCE,
MegaSpecId::MINI_REX => name::MINI_REX,
MegaSpecId::REX => name::REX,
MegaSpecId::REX1 => name::REX1,
MegaSpecId::REX2 => name::REX2,
MegaSpecId::REX3 => name::REX3,
MegaSpecId::REX4 => name::REX4,
MegaSpecId::REX5 => name::REX5,
}
}
}
impl FromStr for MegaSpecId {
type Err = UnknownHardfork;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
name::EQUIVALENCE => Ok(Self::EQUIVALENCE),
name::MINI_REX => Ok(Self::MINI_REX),
name::REX => Ok(Self::REX),
name::REX1 => Ok(Self::REX1),
name::REX2 => Ok(Self::REX2),
name::REX3 => Ok(Self::REX3),
name::REX4 => Ok(Self::REX4),
name::REX5 => Ok(Self::REX5),
_ => Err(UnknownHardfork),
}
}
}
impl From<MegaSpecId> for revm::primitives::hardfork::SpecId {
fn from(spec_id: MegaSpecId) -> Self {
spec_id.into_eth_spec()
}
}
impl From<MegaSpecId> for OpSpecId {
fn from(spec_id: MegaSpecId) -> Self {
spec_id.into_op_spec()
}
}
impl Display for MegaSpecId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s: &'static str = (*self).into();
write!(f, "{}", s)
}
}
#[cfg(test)]
mod tests {
use super::*;
const ALL_SPECS: [(MegaSpecId, &str); 8] = [
(MegaSpecId::EQUIVALENCE, name::EQUIVALENCE),
(MegaSpecId::MINI_REX, name::MINI_REX),
(MegaSpecId::REX, name::REX),
(MegaSpecId::REX1, name::REX1),
(MegaSpecId::REX2, name::REX2),
(MegaSpecId::REX3, name::REX3),
(MegaSpecId::REX4, name::REX4),
(MegaSpecId::REX5, name::REX5),
];
#[test]
fn test_spec_names_roundtrip_and_display() {
for (spec, expected_name) in ALL_SPECS {
assert_eq!(<&'static str>::from(spec), expected_name);
assert_eq!(MegaSpecId::from_str(expected_name).unwrap(), spec);
assert_eq!(spec.to_string(), expected_name);
}
assert_eq!(MegaSpecId::default(), MegaSpecId::REX5);
assert_eq!(MegaSpecId::from_str("unknown"), Err(UnknownHardfork));
}
#[test]
fn test_all_specs_map_to_isthmus_and_prague() {
for (spec, _) in ALL_SPECS {
assert_eq!(spec.into_op_spec(), OpSpecId::ISTHMUS);
assert_eq!(spec.into_eth_spec(), EthSpecId::PRAGUE);
assert_eq!(revm::primitives::hardfork::SpecId::from(spec), EthSpecId::PRAGUE);
assert_eq!(OpSpecId::from(spec), OpSpecId::ISTHMUS);
}
}
#[test]
fn test_spec_order_is_backward_compatible() {
assert!(MegaSpecId::REX4.is_enabled(MegaSpecId::REX3));
assert!(MegaSpecId::REX4.is_enabled(MegaSpecId::EQUIVALENCE));
assert!(MegaSpecId::MINI_REX.is_enabled(MegaSpecId::EQUIVALENCE));
assert!(MegaSpecId::REX2.is_enabled(MegaSpecId::REX1));
assert!(MegaSpecId::REX5.is_enabled(MegaSpecId::REX4));
assert!(MegaSpecId::REX5.is_enabled(MegaSpecId::EQUIVALENCE));
assert!(!MegaSpecId::EQUIVALENCE.is_enabled(MegaSpecId::MINI_REX));
assert!(!MegaSpecId::REX1.is_enabled(MegaSpecId::REX2));
assert!(!MegaSpecId::REX3.is_enabled(MegaSpecId::REX4));
assert!(!MegaSpecId::REX4.is_enabled(MegaSpecId::REX5));
}
}