|  | 
|  | 1 | +#![doc = include_str!("../README.md")] | 
|  | 2 | +#![warn( | 
|  | 3 | +    missing_copy_implementations, | 
|  | 4 | +    missing_debug_implementations, | 
|  | 5 | +    missing_docs, | 
|  | 6 | +    unreachable_pub, | 
|  | 7 | +    clippy::missing_const_for_fn, | 
|  | 8 | +    rustdoc::all | 
|  | 9 | +)] | 
|  | 10 | +#![cfg_attr(not(test), warn(unused_crate_dependencies))] | 
|  | 11 | +#![deny(unused_must_use, rust_2018_idioms)] | 
|  | 12 | +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] | 
|  | 13 | + | 
|  | 14 | +use alloy::genesis::Genesis; | 
|  | 15 | +use init4_bin_base::utils::from_env::{ | 
|  | 16 | +    EnvItemInfo, FromEnv, FromEnvErr, FromEnvVar, parse_env_if_present, | 
|  | 17 | +}; | 
|  | 18 | +use signet_constants::KnownChains; | 
|  | 19 | +use std::{borrow::Cow, path::PathBuf, str::FromStr, sync::LazyLock}; | 
|  | 20 | + | 
|  | 21 | +/// Pecorino genesis file. | 
|  | 22 | +pub const PECORINO_GENESIS_JSON: &str = include_str!("./pecorino.genesis.json"); | 
|  | 23 | + | 
|  | 24 | +/// Local genesis file for testing purposes. | 
|  | 25 | +pub const TEST_GENESIS_JSON: &str = include_str!("./local.genesis.json"); | 
|  | 26 | + | 
|  | 27 | +/// Genesis for the Pecorino testnet. | 
|  | 28 | +pub static PECORINO_GENESIS: LazyLock<Genesis> = LazyLock::new(|| { | 
|  | 29 | +    serde_json::from_str(PECORINO_GENESIS_JSON).expect("Failed to parse pecorino genesis") | 
|  | 30 | +}); | 
|  | 31 | + | 
|  | 32 | +/// Test genesis for local testing. | 
|  | 33 | +pub static TEST_GENESIS: LazyLock<Genesis> = LazyLock::new(|| { | 
|  | 34 | +    serde_json::from_str(TEST_GENESIS_JSON).expect("Failed to parse test genesis") | 
|  | 35 | +}); | 
|  | 36 | + | 
|  | 37 | +/// Environment variable for specifying the genesis JSON file path. | 
|  | 38 | +const GENSIS_JSON_PATH: &str = "GENSIS_JSON_PATH"; | 
|  | 39 | + | 
|  | 40 | +/// Result type for genesis operations. | 
|  | 41 | +pub type Result<T, E = GenesisError> = std::result::Result<T, E>; | 
|  | 42 | + | 
|  | 43 | +/// Errors that can occur when loading the genesis file. | 
|  | 44 | +#[derive(Debug, thiserror::Error)] | 
|  | 45 | +pub enum GenesisError { | 
|  | 46 | +    /// IO error when reading the genesis file. | 
|  | 47 | +    #[error(transparent)] | 
|  | 48 | +    Io(#[from] std::io::Error), | 
|  | 49 | +    /// JSON parsing error when parsing the genesis file. | 
|  | 50 | +    #[error(transparent)] | 
|  | 51 | +    Json(#[from] serde_json::Error), | 
|  | 52 | +} | 
|  | 53 | + | 
|  | 54 | +/// Different genesis configurations available. | 
|  | 55 | +#[derive(Debug, Clone, serde::Deserialize)] | 
|  | 56 | +#[serde(untagged)] | 
|  | 57 | +pub enum GenesisSpec { | 
|  | 58 | +    /// Pecorino testnet. | 
|  | 59 | +    Pecorino, | 
|  | 60 | +    /// Local testnet. | 
|  | 61 | +    Test, | 
|  | 62 | +    /// Custom path to a genesis file. | 
|  | 63 | +    Path(PathBuf), | 
|  | 64 | +} | 
|  | 65 | + | 
|  | 66 | +impl GenesisSpec { | 
|  | 67 | +    /// Load the genesis JSON from the specified source. | 
|  | 68 | +    /// | 
|  | 69 | +    /// This will alwys return a valid string for [`KnownChains`]. | 
|  | 70 | +    pub fn load_raw_genesis(&self) -> Result<Cow<'static, str>> { | 
|  | 71 | +        match self { | 
|  | 72 | +            GenesisSpec::Pecorino => Ok(Cow::Borrowed(PECORINO_GENESIS_JSON)), | 
|  | 73 | +            GenesisSpec::Test => Ok(Cow::Borrowed(TEST_GENESIS_JSON)), | 
|  | 74 | +            GenesisSpec::Path(path) => { | 
|  | 75 | +                std::fs::read_to_string(path).map(Cow::Owned).map_err(Into::into) | 
|  | 76 | +            } | 
|  | 77 | +        } | 
|  | 78 | +    } | 
|  | 79 | + | 
|  | 80 | +    /// Load the genesis from the specified source. | 
|  | 81 | +    /// | 
|  | 82 | +    /// This will always return a valid genesis for [`KnownChains`]. | 
|  | 83 | +    pub fn load_genesis(&self) -> Result<alloy::genesis::Genesis> { | 
|  | 84 | +        match self { | 
|  | 85 | +            GenesisSpec::Pecorino => Ok(PECORINO_GENESIS.clone()), | 
|  | 86 | +            GenesisSpec::Test => Ok(TEST_GENESIS.clone()), | 
|  | 87 | +            GenesisSpec::Path(_) => self | 
|  | 88 | +                .load_raw_genesis() | 
|  | 89 | +                .and_then(|raw| serde_json::from_str(&raw).map_err(Into::into)), | 
|  | 90 | +        } | 
|  | 91 | +    } | 
|  | 92 | +} | 
|  | 93 | + | 
|  | 94 | +impl FromStr for GenesisSpec { | 
|  | 95 | +    type Err = <PathBuf as FromStr>::Err; | 
|  | 96 | + | 
|  | 97 | +    fn from_str(s: &str) -> Result<Self, Self::Err> { | 
|  | 98 | +        if let Ok(known) = KnownChains::from_str(s) { | 
|  | 99 | +            return Ok(known.into()); | 
|  | 100 | +        } | 
|  | 101 | + | 
|  | 102 | +        Ok(GenesisSpec::Path(s.parse()?)) | 
|  | 103 | +    } | 
|  | 104 | +} | 
|  | 105 | + | 
|  | 106 | +impl FromEnvVar for GenesisSpec { | 
|  | 107 | +    type Error = <GenesisSpec as FromStr>::Err; | 
|  | 108 | + | 
|  | 109 | +    fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> { | 
|  | 110 | +        parse_env_if_present(env_var) | 
|  | 111 | +    } | 
|  | 112 | +} | 
|  | 113 | + | 
|  | 114 | +impl FromEnv for GenesisSpec { | 
|  | 115 | +    type Error = <GenesisSpec as FromStr>::Err; | 
|  | 116 | + | 
|  | 117 | +    fn inventory() -> Vec<&'static init4_bin_base::utils::from_env::EnvItemInfo> { | 
|  | 118 | +        vec![ | 
|  | 119 | +            &EnvItemInfo { | 
|  | 120 | +                var: "CHAIN_NAME", | 
|  | 121 | +                description: "The name of the chain. If set, the other environment variables are ignored.", | 
|  | 122 | +                optional: true, | 
|  | 123 | +            }, | 
|  | 124 | +            &EnvItemInfo { | 
|  | 125 | +                var: GENSIS_JSON_PATH, | 
|  | 126 | +                description: "A filepath to the genesis JSON file. Required if CHAIN_NAME is not set.", | 
|  | 127 | +                optional: true, | 
|  | 128 | +            }, | 
|  | 129 | +        ] | 
|  | 130 | +    } | 
|  | 131 | + | 
|  | 132 | +    fn from_env() -> Result<Self, FromEnvErr<Self::Error>> { | 
|  | 133 | +        parse_env_if_present::<KnownChains>("CHAIN_NAME") | 
|  | 134 | +            .map(Into::into) | 
|  | 135 | +            .or_else(|_| parse_env_if_present::<PathBuf>(GENSIS_JSON_PATH).map(Into::into)) | 
|  | 136 | +    } | 
|  | 137 | +} | 
|  | 138 | + | 
|  | 139 | +impl From<KnownChains> for GenesisSpec { | 
|  | 140 | +    fn from(known: KnownChains) -> Self { | 
|  | 141 | +        match known { | 
|  | 142 | +            KnownChains::Pecorino => GenesisSpec::Pecorino, | 
|  | 143 | +            KnownChains::Test => GenesisSpec::Test, | 
|  | 144 | +        } | 
|  | 145 | +    } | 
|  | 146 | +} | 
|  | 147 | + | 
|  | 148 | +impl From<PathBuf> for GenesisSpec { | 
|  | 149 | +    fn from(path: PathBuf) -> Self { | 
|  | 150 | +        GenesisSpec::Path(path) | 
|  | 151 | +    } | 
|  | 152 | +} | 
0 commit comments