|
| 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