diff --git a/config.example.toml b/config.example.toml index 4da68ff2..93b9e11f 100644 --- a/config.example.toml +++ b/config.example.toml @@ -1,7 +1,10 @@ # The main configuration file for the Commit-Boost sidecar. # Some fields are optional and can be omitted, in which case the default value, if present, will be used. -# Chain spec id. Supported values: Mainnet, Holesky, Helder +# Chain spec ID. Supported values: +# A network ID. Supported values: Mainnet, Holesky, Helder. +# A path to a chain spec file, either in .json format (e.g., as returned by the beacon endpoint /eth/v1/config/spec), or in .yml format (see examples in tests/data). +# A custom object, e.g., chain = { genesis_time_secs = 1695902400, slot_time_secs = 12, genesis_fork_version = "0x01017000" }. chain = "Holesky" # Configuration for the PBS module diff --git a/crates/cli/src/docker_init.rs b/crates/cli/src/docker_init.rs index c0769710..9e1809b9 100644 --- a/crates/cli/src/docker_init.rs +++ b/crates/cli/src/docker_init.rs @@ -3,10 +3,10 @@ use std::{path::Path, vec}; use cb_common::{ config::{ CommitBoostConfig, LogsSettings, ModuleKind, BUILDER_PORT_ENV, BUILDER_URLS_ENV, - CONFIG_DEFAULT, CONFIG_ENV, JWTS_ENV, LOGS_DIR_DEFAULT, LOGS_DIR_ENV, METRICS_PORT_ENV, - MODULE_ID_ENV, MODULE_JWT_ENV, PBS_MODULE_NAME, SIGNER_DEFAULT, SIGNER_DIR_KEYS_DEFAULT, - SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS, SIGNER_DIR_SECRETS_ENV, SIGNER_KEYS_ENV, - SIGNER_MODULE_NAME, SIGNER_PORT_ENV, SIGNER_URL_ENV, + CHAIN_SPEC_ENV, CONFIG_DEFAULT, CONFIG_ENV, JWTS_ENV, LOGS_DIR_DEFAULT, LOGS_DIR_ENV, + METRICS_PORT_ENV, MODULE_ID_ENV, MODULE_JWT_ENV, PBS_MODULE_NAME, SIGNER_DEFAULT, + SIGNER_DIR_KEYS_DEFAULT, SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS, SIGNER_DIR_SECRETS_ENV, + SIGNER_KEYS_ENV, SIGNER_MODULE_NAME, SIGNER_PORT_ENV, SIGNER_URL_ENV, }, loader::SignerLoader, types::ModuleId, @@ -37,6 +37,7 @@ const SIGNER_NETWORK: &str = "signer_network"; pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> { println!("Initializing Commit-Boost with config file: {}", config_path); let cb_config = CommitBoostConfig::from_file(&config_path)?; + let chain_spec_path = CommitBoostConfig::chain_spec_file(&config_path); let metrics_enabled = cb_config.metrics.is_some(); let log_to_file = cb_config.logs.is_some(); @@ -46,6 +47,17 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> // config volume to pass to all services let config_volume = Volumes::Simple(format!("./{}:{}:ro", config_path, CONFIG_DEFAULT)); + let chain_spec_volume = chain_spec_path.as_ref().and_then(|p| { + // this is ok since the config has already been loaded once + let file_name = p.file_name()?.to_str()?; + Some(Volumes::Simple(format!("{}:/{}:ro", p.display(), file_name))) + }); + + let chain_spec_env = chain_spec_path.and_then(|p| { + // this is ok since the config has already been loaded once + let file_name = p.file_name()?.to_str()?; + Some(get_env_val(CHAIN_SPEC_ENV, &format!("/{file_name}"))) + }); let mut jwts = IndexMap::new(); // envs to write in .env file @@ -105,6 +117,9 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> // Set environment file let env_file = module.env_file.map(EnvFile::Simple); + if let Some((key, val)) = chain_spec_env.clone() { + module_envs.insert(key, val); + } if metrics_enabled { let (key, val) = get_env_uval(METRICS_PORT_ENV, metrics_port as u64); module_envs.insert(key, val); @@ -125,6 +140,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> // volumes let mut module_volumes = vec![config_volume.clone()]; + module_volumes.extend(chain_spec_volume.clone()); module_volumes.extend(get_log_volume(&cb_config.logs, &module.id)); Service { @@ -151,6 +167,9 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> get_env_uval(BUILDER_PORT_ENV, builder_events_port), ]); + if let Some((key, val)) = chain_spec_env.clone() { + module_envs.insert(key, val); + } if metrics_enabled { let (key, val) = get_env_uval(METRICS_PORT_ENV, metrics_port as u64); module_envs.insert(key, val); @@ -169,6 +188,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> // volumes let mut module_volumes = vec![config_volume.clone()]; + module_volumes.extend(chain_spec_volume.clone()); module_volumes.extend(get_log_volume(&cb_config.logs, &module.id)); Service { @@ -197,6 +217,9 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> let mut pbs_envs = IndexMap::from([get_env_val(CONFIG_ENV, CONFIG_DEFAULT)]); + if let Some((key, val)) = chain_spec_env.clone() { + pbs_envs.insert(key, val); + } if metrics_enabled { let (key, val) = get_env_uval(METRICS_PORT_ENV, metrics_port as u64); pbs_envs.insert(key, val); @@ -213,6 +236,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> // volumes let mut pbs_volumes = vec![config_volume.clone()]; + pbs_volumes.extend(chain_spec_volume.clone()); pbs_volumes.extend(get_log_volume(&cb_config.logs, PBS_MODULE_NAME)); // networks @@ -256,6 +280,9 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> get_env_uval(SIGNER_PORT_ENV, signer_port as u64), ]); + if let Some((key, val)) = chain_spec_env.clone() { + signer_envs.insert(key, val); + } if metrics_enabled { let (key, val) = get_env_uval(METRICS_PORT_ENV, metrics_port as u64); signer_envs.insert(key, val); @@ -270,6 +297,7 @@ pub fn handle_docker_init(config_path: String, output_dir: String) -> Result<()> // volumes let mut volumes = vec![config_volume.clone()]; + volumes.extend(chain_spec_volume.clone()); // TODO: generalize this, different loaders may not need volumes but eg ports match signer_config.loader { diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 24ab4a54..90e5df64 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -22,6 +22,7 @@ tokio.workspace = true toml.workspace = true serde.workspace = true serde_json.workspace = true +serde_yaml.workspace = true # telemetry tracing.workspace = true diff --git a/crates/common/src/config/constants.rs b/crates/common/src/config/constants.rs index 4db80013..a51b3bb8 100644 --- a/crates/common/src/config/constants.rs +++ b/crates/common/src/config/constants.rs @@ -4,6 +4,9 @@ pub const CONFIG_ENV: &str = "CB_CONFIG"; pub const CONFIG_DEFAULT: &str = "/cb-config.toml"; +/// Path to the chain spec file +pub const CHAIN_SPEC_ENV: &str = "CB_CHAIN_SPEC"; + /// Where to receive scrape requests from Prometheus pub const METRICS_PORT_ENV: &str = "CB_METRICS_PORT"; diff --git a/crates/common/src/config/mod.rs b/crates/common/src/config/mod.rs index 0205f451..a025740f 100644 --- a/crates/common/src/config/mod.rs +++ b/crates/common/src/config/mod.rs @@ -1,17 +1,18 @@ +use std::path::PathBuf; + use eyre::Result; use serde::{Deserialize, Serialize}; -use crate::types::Chain; +use crate::types::{load_chain_from_file, Chain}; mod constants; +mod log; mod metrics; mod module; mod pbs; mod signer; mod utils; -mod log; - pub use constants::*; pub use log::*; pub use metrics::*; @@ -22,7 +23,6 @@ pub use utils::*; #[derive(Debug, Deserialize, Serialize)] pub struct CommitBoostConfig { - // TODO: generalize this with a spec file pub chain: Chain, pub relays: Vec, pub pbs: StaticPbsConfig, @@ -45,9 +45,53 @@ impl CommitBoostConfig { Ok(config) } + // When loading the config from the environment, it's important that every path + // is replaced with the correct value if the config is loaded inside a container pub fn from_env_path() -> Result { - let config: Self = load_file_from_env(CONFIG_ENV)?; + let config = if let Ok(path) = std::env::var(CHAIN_SPEC_ENV) { + // if the chain spec file is set, load it separately + let chain: Chain = load_chain_from_file(path.parse()?)?; + let rest_config: HelperConfig = load_file_from_env(CONFIG_ENV)?; + + CommitBoostConfig { + chain, + relays: rest_config.relays, + pbs: rest_config.pbs, + modules: rest_config.modules, + signer: rest_config.signer, + metrics: rest_config.metrics, + logs: rest_config.logs, + } + } else { + load_file_from_env(CONFIG_ENV)? + }; + config.validate()?; Ok(config) } + + /// Returns the path to the chain spec file if any + pub fn chain_spec_file(path: &str) -> Option { + match load_from_file::(path) { + Ok(config) => Some(config.chain), + Err(_) => None, + } + } +} + +/// Helper struct to load the chain spec file +#[derive(Deserialize)] +struct ChainConfig { + chain: PathBuf, +} + +/// Helper struct to load the rest of the config +#[derive(Deserialize)] +struct HelperConfig { + relays: Vec, + pbs: StaticPbsConfig, + modules: Option>, + signer: Option, + metrics: Option, + logs: Option, } diff --git a/crates/common/src/constants.rs b/crates/common/src/constants.rs index ee75909f..721e9e84 100644 --- a/crates/common/src/constants.rs +++ b/crates/common/src/constants.rs @@ -1,29 +1,3 @@ -// TODO: replace with full chain spec, allow loading from file - pub const APPLICATION_BUILDER_DOMAIN: [u8; 4] = [0, 0, 0, 1]; pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = [0; 32]; pub const COMMIT_BOOST_DOMAIN: [u8; 4] = [109, 109, 111, 67]; - -// MAINNET -pub const MAINNET_FORK_VERSION: [u8; 4] = [0u8; 4]; -pub const MAINNET_BUILDER_DOMAIN: [u8; 32] = [ - 0, 0, 0, 1, 245, 165, 253, 66, 209, 106, 32, 48, 39, 152, 239, 110, 211, 9, 151, 155, 67, 0, - 61, 35, 32, 217, 240, 232, 234, 152, 49, 169, -]; -pub const MAINNET_GENESIS_TIME_SECONDS: u64 = 1606824023; - -// HOLESKY -pub const HOLESKY_FORK_VERSION: [u8; 4] = [1, 1, 112, 0]; -pub const HOLESKY_BUILDER_DOMAIN: [u8; 32] = [ - 0, 0, 0, 1, 91, 131, 162, 55, 89, 197, 96, 178, 208, 198, 69, 118, 225, 220, 252, 52, 234, 148, - 196, 152, 143, 62, 13, 159, 119, 240, 83, 135, -]; -pub const HOLESKY_GENESIS_TIME_SECONDS: u64 = 1695902400; - -// HELDER -pub const HELDER_FORK_VERSION: [u8; 4] = [16, 0, 0, 0]; -pub const HELDER_BUILDER_DOMAIN: [u8; 32] = [ - 0, 0, 0, 1, 148, 196, 26, 244, 132, 255, 247, 150, 73, 105, 224, 189, 217, 34, 248, 45, 255, - 15, 75, 232, 122, 96, 208, 102, 76, 201, 209, 255, -]; -pub const HELDER_GENESIS_TIME_SECONDS: u64 = 1718967660; diff --git a/crates/common/src/signature.rs b/crates/common/src/signature.rs index de9f477b..49f4ddb9 100644 --- a/crates/common/src/signature.rs +++ b/crates/common/src/signature.rs @@ -25,6 +25,9 @@ pub fn compute_signing_root(object_root: [u8; 32], signing_domain: [u8; 32]) -> signing_data.tree_hash_root().0 } +// NOTE: this currently works only for builder domain signatures and +// verifications +// ref: https://github.com/ralexstokes/ethereum-consensus/blob/cf3c404043230559660810bc0c9d6d5a8498d819/ethereum-consensus/src/builder/mod.rs#L26-L29 pub fn compute_domain(chain: Chain, domain_mask: [u8; 4]) -> [u8; 32] { #[derive(Debug, TreeHash)] struct ForkData { @@ -35,7 +38,7 @@ pub fn compute_domain(chain: Chain, domain_mask: [u8; 4]) -> [u8; 32] { let mut domain = [0u8; 32]; domain[..4].copy_from_slice(&domain_mask); - let fork_version = chain.fork_version(); + let fork_version = chain.genesis_fork_version(); let fd = ForkData { fork_version, genesis_validators_root: GENESIS_VALIDATORS_ROOT }; let fork_data_root = fd.tree_hash_root(); diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs index f570da66..7d40f295 100644 --- a/crates/common/src/types.rs +++ b/crates/common/src/types.rs @@ -1,52 +1,347 @@ +use std::path::PathBuf; + +use alloy::primitives::{hex, Bytes}; use derive_more::{Deref, Display, From, Into}; +use eyre::{bail, Context}; use serde::{Deserialize, Serialize}; -use crate::constants::{ - HELDER_BUILDER_DOMAIN, HELDER_FORK_VERSION, HELDER_GENESIS_TIME_SECONDS, - HOLESKY_BUILDER_DOMAIN, HOLESKY_FORK_VERSION, HOLESKY_GENESIS_TIME_SECONDS, - MAINNET_BUILDER_DOMAIN, MAINNET_FORK_VERSION, MAINNET_GENESIS_TIME_SECONDS, -}; +use crate::{constants::APPLICATION_BUILDER_DOMAIN, signature::compute_domain}; + +#[derive(Clone, Debug, Display, PartialEq, Eq, Hash, Deref, From, Into, Serialize, Deserialize)] +#[into(owned, ref, ref_mut)] +#[serde(transparent)] +pub struct ModuleId(pub String); + +#[derive(Clone, Debug, Display, PartialEq, Eq, Hash, Deref, From, Into, Serialize, Deserialize)] +#[into(owned, ref, ref_mut)] +#[serde(transparent)] +pub struct Jwt(pub String); -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum Chain { Mainnet, Holesky, - Helder, + Custom { genesis_time_secs: u64, slot_time_secs: u64, genesis_fork_version: [u8; 4] }, +} + +impl std::fmt::Debug for Chain { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Mainnet => write!(f, "Mainnet"), + Self::Holesky => write!(f, "Holesky"), + Self::Helder => write!(f, "Helder"), + Self::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version } => f + .debug_struct("Custom") + .field("genesis_time_secs", genesis_time_secs) + .field("slot_time_secs", slot_time_secs) + .field("genesis_fork_version", &hex::encode_prefixed(genesis_fork_version)) + .finish(), + } + } } impl Chain { pub fn builder_domain(&self) -> [u8; 32] { match self { - Chain::Mainnet => MAINNET_BUILDER_DOMAIN, - Chain::Holesky => HOLESKY_BUILDER_DOMAIN, - Chain::Helder => HELDER_BUILDER_DOMAIN, + Chain::Mainnet => KnownChain::Mainnet.builder_domain(), + Chain::Holesky => KnownChain::Holesky.builder_domain(), + Chain::Helder => KnownChain::Helder.builder_domain(), + Chain::Custom { .. } => compute_domain(*self, APPLICATION_BUILDER_DOMAIN), } } - pub fn fork_version(&self) -> [u8; 4] { + pub fn genesis_fork_version(&self) -> [u8; 4] { match self { - Chain::Mainnet => MAINNET_FORK_VERSION, - Chain::Holesky => HOLESKY_FORK_VERSION, - Chain::Helder => HELDER_FORK_VERSION, + Chain::Mainnet => KnownChain::Mainnet.genesis_fork_version(), + Chain::Holesky => KnownChain::Holesky.genesis_fork_version(), + Chain::Helder => KnownChain::Helder.genesis_fork_version(), + Chain::Custom { genesis_fork_version, .. } => *genesis_fork_version, } } pub fn genesis_time_sec(&self) -> u64 { match self { - Chain::Mainnet => MAINNET_GENESIS_TIME_SECONDS, - Chain::Holesky => HOLESKY_GENESIS_TIME_SECONDS, - Chain::Helder => HELDER_GENESIS_TIME_SECONDS, + Chain::Mainnet => KnownChain::Mainnet.genesis_time_sec(), + Chain::Holesky => KnownChain::Holesky.genesis_time_sec(), + Chain::Helder => KnownChain::Helder.genesis_time_sec(), + Chain::Custom { genesis_time_secs, .. } => *genesis_time_secs, + } + } + + pub fn slot_time_sec(&self) -> u64 { + match self { + Chain::Mainnet => KnownChain::Mainnet.slot_time_sec(), + Chain::Holesky => KnownChain::Holesky.slot_time_sec(), + Chain::Helder => KnownChain::Helder.slot_time_sec(), + Chain::Custom { slot_time_secs, .. } => *slot_time_secs, } } } -#[derive(Clone, Debug, Display, PartialEq, Eq, Hash, Deref, From, Into, Serialize, Deserialize)] -#[into(owned, ref, ref_mut)] -#[serde(transparent)] -pub struct ModuleId(pub String); +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum KnownChain { + #[serde(alias = "mainnet")] + Mainnet, + #[serde(alias = "holesky")] + Holesky, + #[serde(alias = "helder")] + Helder, +} -#[derive(Clone, Debug, Display, PartialEq, Eq, Hash, Deref, From, Into, Serialize, Deserialize)] -#[into(owned, ref, ref_mut)] -#[serde(transparent)] -pub struct Jwt(pub String); +// Constants +impl KnownChain { + pub fn builder_domain(&self) -> [u8; 32] { + match self { + KnownChain::Mainnet => [ + 0, 0, 0, 1, 245, 165, 253, 66, 209, 106, 32, 48, 39, 152, 239, 110, 211, 9, 151, + 155, 67, 0, 61, 35, 32, 217, 240, 232, 234, 152, 49, 169, + ], + KnownChain::Holesky => [ + 0, 0, 0, 1, 91, 131, 162, 55, 89, 197, 96, 178, 208, 198, 69, 118, 225, 220, 252, + 52, 234, 148, 196, 152, 143, 62, 13, 159, 119, 240, 83, 135, + ], + KnownChain::Helder => [ + 0, 0, 0, 1, 148, 196, 26, 244, 132, 255, 247, 150, 73, 105, 224, 189, 217, 34, 248, + 45, 255, 15, 75, 232, 122, 96, 208, 102, 76, 201, 209, 255, + ], + } + } + + pub fn genesis_fork_version(&self) -> [u8; 4] { + match self { + KnownChain::Mainnet => [0u8; 4], + KnownChain::Holesky => [1, 1, 112, 0], + KnownChain::Helder => [16, 0, 0, 0], + } + } + + fn genesis_time_sec(&self) -> u64 { + match self { + KnownChain::Mainnet => 1606824023, + KnownChain::Holesky => 1695902400, + KnownChain::Helder => 1718967660, + } + } + + pub fn slot_time_sec(&self) -> u64 { + match self { + KnownChain::Mainnet | KnownChain::Holesky | KnownChain::Helder => 12, + } + } +} + +impl From for Chain { + fn from(value: KnownChain) -> Self { + match value { + KnownChain::Mainnet => Chain::Mainnet, + KnownChain::Holesky => Chain::Holesky, + KnownChain::Helder => Chain::Helder, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +enum ChainLoader { + Known(KnownChain), + Path(PathBuf), + Custom { genesis_time_secs: u64, slot_time_secs: u64, genesis_fork_version: Bytes }, +} + +impl Serialize for Chain { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let loader = match self { + Chain::Mainnet => ChainLoader::Known(KnownChain::Mainnet), + Chain::Holesky => ChainLoader::Known(KnownChain::Holesky), + Chain::Helder => ChainLoader::Known(KnownChain::Helder), + Chain::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version } => { + ChainLoader::Custom { + genesis_time_secs: *genesis_time_secs, + slot_time_secs: *slot_time_secs, + genesis_fork_version: Bytes::from(*genesis_fork_version), + } + } + }; + + loader.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Chain { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let loader = ChainLoader::deserialize(deserializer)?; + + match loader { + ChainLoader::Known(known) => Ok(Chain::from(known)), + ChainLoader::Path(path) => load_chain_from_file(path).map_err(serde::de::Error::custom), + ChainLoader::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version } => { + let genesis_fork_version: [u8; 4] = + genesis_fork_version.as_ref().try_into().map_err(serde::de::Error::custom)?; + Ok(Chain::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version }) + } + } + } +} + +/// Load a chain config from a spec file, such as returned by +/// /eth/v1/config/spec ref: https://ethereum.github.io/beacon-APIs/#/Config/getSpec +/// Try to load two formats: +/// - JSON as return the getSpec endpoint, either with or without the `data` +/// field +/// - YAML as used e.g. in Kurtosis/Ethereum Package +pub fn load_chain_from_file(path: PathBuf) -> eyre::Result { + #[derive(Deserialize)] + #[serde(rename_all = "UPPERCASE")] + struct QuotedSpecFile { + #[serde(with = "serde_utils::quoted_u64")] + min_genesis_time: u64, + #[serde(with = "serde_utils::quoted_u64")] + genesis_delay: u64, + #[serde(with = "serde_utils::quoted_u64")] + seconds_per_slot: u64, + genesis_fork_version: Bytes, + } + + impl QuotedSpecFile { + fn to_chain(&self) -> eyre::Result { + let genesis_fork_version: [u8; 4] = self.genesis_fork_version.as_ref().try_into()?; + + Ok(Chain::Custom { + genesis_time_secs: self.min_genesis_time + self.genesis_delay, + slot_time_secs: self.seconds_per_slot, + genesis_fork_version, + }) + } + } + + #[derive(Deserialize)] + struct SpecFileJson { + data: QuotedSpecFile, + } + + #[derive(Deserialize)] + #[serde(rename_all = "UPPERCASE")] + struct SpecFile { + min_genesis_time: u64, + genesis_delay: u64, + seconds_per_slot: u64, + genesis_fork_version: u32, + } + + impl SpecFile { + fn to_chain(&self) -> Chain { + let genesis_fork_version: [u8; 4] = self.genesis_fork_version.to_be_bytes(); + + Chain::Custom { + genesis_time_secs: self.min_genesis_time + self.genesis_delay, + slot_time_secs: self.seconds_per_slot, + genesis_fork_version, + } + } + } + + let file = + std::fs::read(&path).wrap_err(format!("Unable to find chain spec file: {path:?}"))?; + + if let Ok(decoded) = serde_json::from_slice::(&file) { + decoded.data.to_chain() + } else if let Ok(decoded) = serde_json::from_slice::(&file) { + decoded.to_chain() + } else if let Ok(decoded) = serde_yaml::from_slice::(&file) { + Ok(decoded.to_chain()) + } else { + bail!("unable to decode file: {path:?}, accepted formats are: json or yml") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug, Serialize, Deserialize)] + struct MockConfig { + chain: Chain, + } + + #[test] + fn test_load_known() { + let s = r#"chain = "Mainnet""#; + let decoded: MockConfig = toml::from_str(s).unwrap(); + assert_eq!(decoded.chain, Chain::Mainnet); + } + + #[test] + fn test_load_custom() { + let s = r#"chain = { genesis_time_secs = 1, slot_time_secs = 2, genesis_fork_version = "0x01000000" }"#; + let decoded: MockConfig = toml::from_str(s).unwrap(); + assert_eq!(decoded.chain, Chain::Custom { + genesis_time_secs: 1, + slot_time_secs: 2, + genesis_fork_version: [1, 0, 0, 0] + }) + } + + #[test] + fn test_load_file_data_json() { + let a = env!("CARGO_MANIFEST_DIR"); + let mut path = PathBuf::from(a); + + path.pop(); + path.pop(); + path.push("tests/data/holesky_spec_data.json"); + + let s = format!("chain = {path:?}"); + + let decoded: MockConfig = toml::from_str(&s).unwrap(); + assert_eq!(decoded.chain, Chain::Custom { + genesis_time_secs: KnownChain::Holesky.genesis_time_sec(), + slot_time_secs: KnownChain::Holesky.slot_time_sec(), + genesis_fork_version: KnownChain::Holesky.genesis_fork_version() + }) + } + + #[test] + fn test_load_file_json() { + let a = env!("CARGO_MANIFEST_DIR"); + let mut path = PathBuf::from(a); + + path.pop(); + path.pop(); + path.push("tests/data/holesky_spec.json"); + + let s = format!("chain = {path:?}"); + + let decoded: MockConfig = toml::from_str(&s).unwrap(); + assert_eq!(decoded.chain, Chain::Custom { + genesis_time_secs: KnownChain::Holesky.genesis_time_sec(), + slot_time_secs: KnownChain::Holesky.slot_time_sec(), + genesis_fork_version: KnownChain::Holesky.genesis_fork_version() + }) + } + + #[test] + fn test_load_file_yml() { + let a = env!("CARGO_MANIFEST_DIR"); + let mut path = PathBuf::from(a); + + path.pop(); + path.pop(); + path.push("tests/data/helder_spec.yml"); + + let s = format!("chain = {path:?}"); + + let decoded: MockConfig = toml::from_str(&s).unwrap(); + assert_eq!(decoded.chain, Chain::Custom { + genesis_time_secs: KnownChain::Helder.genesis_time_sec(), + slot_time_secs: KnownChain::Helder.slot_time_sec(), + genesis_fork_version: KnownChain::Helder.genesis_fork_version() + }) + } +} diff --git a/crates/common/src/utils.rs b/crates/common/src/utils.rs index 8ea63014..d2c26ed6 100644 --- a/crates/common/src/utils.rs +++ b/crates/common/src/utils.rs @@ -20,11 +20,10 @@ use crate::{ types::Chain, }; -const SECONDS_PER_SLOT: u64 = 12; const MILLIS_PER_SECOND: u64 = 1_000; pub fn timestamp_of_slot_start_millis(slot: u64, chain: Chain) -> u64 { - let seconds_since_genesis = chain.genesis_time_sec() + slot * SECONDS_PER_SLOT; + let seconds_since_genesis = chain.genesis_time_sec() + slot * chain.slot_time_sec(); seconds_since_genesis * MILLIS_PER_SECOND } pub fn ms_into_slot(slot: u64, chain: Chain) -> u64 { diff --git a/crates/pbs/src/service.rs b/crates/pbs/src/service.rs index 6d1f1804..cf916e0b 100644 --- a/crates/pbs/src/service.rs +++ b/crates/pbs/src/service.rs @@ -22,10 +22,9 @@ impl PbsService { let address = SocketAddr::from(([0, 0, 0, 0], state.config.pbs_config.port)); let events_subs = state.config.event_publiher.as_ref().map(|e| e.n_subscribers()).unwrap_or_default(); - let app = create_app_router::(state); - - info!(?address, events_subs, "Starting PBS service"); + info!(?address, events_subs, chain =? state.config.chain, "Starting PBS service"); + let app = create_app_router::(state); let listener = TcpListener::bind(address).await.expect("failed tcp binding"); axum::serve(listener, app).await.wrap_err("PBS server exited") diff --git a/docs/docs/get_started/running/binary.md b/docs/docs/get_started/running/binary.md index 1268af0f..28a22a6e 100644 --- a/docs/docs/get_started/running/binary.md +++ b/docs/docs/get_started/running/binary.md @@ -16,6 +16,7 @@ Modules need some environment variables to work correctly. ### Common - `CB_CONFIG`: required, path to the `toml` config file +- `CHAIN_SPEC_ENV`: optional, path to a chain spec file. This will override the `[chain]` field in the `toml` config - `CB_METRICS_PORT`: optional, port where to expose the `/metrics` endpoint for Prometheus - `CB_LOGS_DIR`: optional, directory to store logs. This will override the directory in the `toml` config diff --git a/tests/data/helder_spec.yml b/tests/data/helder_spec.yml new file mode 100644 index 00000000..1c1a9549 --- /dev/null +++ b/tests/data/helder_spec.yml @@ -0,0 +1,148 @@ +# Extends the mainnet preset +PRESET_BASE: mainnet +CONFIG_NAME: testnet # needs to exist because of Prysm. Otherwise it conflicts with mainnet genesis + +# Genesis +# --------------------------------------------------------------- +# `2**14` (= 16,384) +MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 1000 +# Mar-01-2021 08:53:32 AM +UTC +# This is an invalid valid and should be updated when you create the genesis +MIN_GENESIS_TIME: 1718967600 +GENESIS_FORK_VERSION: 0x10000000 +GENESIS_DELAY: 60 + +# Forking +# --------------------------------------------------------------- +# Some forks are disabled for now: +# - These may be re-assigned to another fork-version later +# - Temporarily set to max uint64 value: 2**64 - 1 + +# Altair +ALTAIR_FORK_VERSION: 0x20000000 +ALTAIR_FORK_EPOCH: 0 +# Merge +BELLATRIX_FORK_VERSION: 0x30000000 +BELLATRIX_FORK_EPOCH: 0 +TERMINAL_TOTAL_DIFFICULTY: 0 +TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000 +TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615 + +# Capella +CAPELLA_FORK_VERSION: 0x40000000 +CAPELLA_FORK_EPOCH: 0 + +# DENEB +DENEB_FORK_VERSION: 0x50132736 +DENEB_FORK_EPOCH: 0 + +# Electra +ELECTRA_FORK_VERSION: 0x60132736 +ELECTRA_FORK_EPOCH: 999999 + +# EIP7594 - Peerdas +EIP7594_FORK_VERSION: 0x70132736 +EIP7594_FORK_EPOCH: 999999 + +# Time parameters +# --------------------------------------------------------------- +# 12 seconds +SECONDS_PER_SLOT: 12 +# 14 (estimate from Eth1 mainnet) +SECONDS_PER_ETH1_BLOCK: 12 +# 2**8 (= 256) epochs ~27 hours +MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 +# 2**8 (= 256) epochs ~27 hours +SHARD_COMMITTEE_PERIOD: 256 +# 2**11 (= 2,048) Eth1 blocks ~8 hours +ETH1_FOLLOW_DISTANCE: 2048 + +# Validator cycle +# --------------------------------------------------------------- +# 2**2 (= 4) +INACTIVITY_SCORE_BIAS: 4 +# 2**4 (= 16) +INACTIVITY_SCORE_RECOVERY_RATE: 16 +# 2**4 * 10**9 (= 16,000,000,000) Gwei +EJECTION_BALANCE: 16000000000 +# 2**2 (= 4) +MIN_PER_EPOCH_CHURN_LIMIT: 4 +# 2**16 (= 65,536) +CHURN_LIMIT_QUOTIENT: 65536 +# [New in Deneb:EIP7514] 2**3 (= 8) +MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 + +# Fork choice +# --------------------------------------------------------------- +# 40% +PROPOSER_SCORE_BOOST: 40 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 + +# Deposit contract +# --------------------------------------------------------------- +DEPOSIT_CHAIN_ID: 7014190335 +DEPOSIT_NETWORK_ID: 7014190335 +DEPOSIT_CONTRACT_ADDRESS: 0x4242424242424242424242424242424242424242 + +# Networking +# --------------------------------------------------------------- +# `10 * 2**20` (= 10485760, 10 MiB) +GOSSIP_MAX_SIZE: 10485760 +# `2**10` (= 1024) +MAX_REQUEST_BLOCKS: 1024 +# `2**8` (= 256) +EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 +# `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 33024, ~5 months) +MIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024 +# `10 * 2**20` (=10485760, 10 MiB) +MAX_CHUNK_SIZE: 10485760 +# 5s +TTFB_TIMEOUT: 5 +# 10s +RESP_TIMEOUT: 10 +ATTESTATION_PROPAGATION_SLOT_RANGE: 32 +# 500ms +MAXIMUM_GOSSIP_CLOCK_DISPARITY: 500 +MESSAGE_DOMAIN_INVALID_SNAPPY: 0x00000000 +MESSAGE_DOMAIN_VALID_SNAPPY: 0x01000000 +# 2 subnets per node +SUBNETS_PER_NODE: 2 +# 2**8 (= 64) +ATTESTATION_SUBNET_COUNT: 64 +ATTESTATION_SUBNET_EXTRA_BITS: 0 +# ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS +ATTESTATION_SUBNET_PREFIX_BITS: 6 + +# Deneb +# `2**7` (=128) +MAX_REQUEST_BLOCKS_DENEB: 128 +# MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK +MAX_REQUEST_BLOB_SIDECARS: 768 +# `2**12` (= 4096 epochs, ~18 days) +MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096 +# `6` +BLOB_SIDECAR_SUBNET_COUNT: 6 + +# Whisk +# `Epoch(2**8)` +WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256 +# `Epoch(2)` +WHISK_PROPOSER_SELECTION_GAP: 2 + +# EIP7594 +NUMBER_OF_COLUMNS: 128 +MAX_CELLS_IN_EXTENDED_MATRIX: 768 +DATA_COLUMN_SIDECAR_SUBNET_COUNT: 32 +MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384 +SAMPLES_PER_SLOT: 8 +CUSTODY_REQUIREMENT: 1 +TARGET_NUMBER_OF_PEERS: 70 + +# [New in Electra:EIP7251] +MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000 # 2**7 * 10**9 (= 128,000,000,000) +MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 256000000000 # 2**8 * 10**9 (= 256,000,000,000) diff --git a/tests/data/holesky_spec.json b/tests/data/holesky_spec.json new file mode 100644 index 00000000..5e19d62a --- /dev/null +++ b/tests/data/holesky_spec.json @@ -0,0 +1,141 @@ +{ + "SLOTS_PER_EPOCH": "32", + "PRESET_BASE": "mainnet", + "TERMINAL_TOTAL_DIFFICULTY": "0", + "INACTIVITY_SCORE_BIAS": "4", + "PENDING_BALANCE_DEPOSITS_LIMIT": "134217728", + "MAX_ATTESTER_SLASHINGS": "2", + "MAX_WITHDRAWALS_PER_PAYLOAD": "16", + "INACTIVITY_PENALTY_QUOTIENT_BELLATRIX": "16777216", + "PENDING_PARTIAL_WITHDRAWALS_LIMIT": "134217728", + "INACTIVITY_PENALTY_QUOTIENT": "67108864", + "SAFE_SLOTS_TO_UPDATE_JUSTIFIED": "8", + "SECONDS_PER_ETH1_BLOCK": "14", + "MIN_SEED_LOOKAHEAD": "1", + "VALIDATOR_REGISTRY_LIMIT": "1099511627776", + "REORG_MAX_EPOCHS_SINCE_FINALIZATION": "2", + "SLOTS_PER_HISTORICAL_ROOT": "8192", + "RESP_TIMEOUT": "10", + "DOMAIN_VOLUNTARY_EXIT": "0x04000000", + "MAX_VALIDATORS_PER_COMMITTEE": "2048", + "MIN_GENESIS_TIME": "1695902100", + "ALTAIR_FORK_EPOCH": "0", + "HYSTERESIS_QUOTIENT": "4", + "ALTAIR_FORK_VERSION": "0x02017000", + "MAX_BYTES_PER_TRANSACTION": "1073741824", + "MAX_CHUNK_SIZE": "10485760", + "TTFB_TIMEOUT": "5", + "WHISTLEBLOWER_REWARD_QUOTIENT": "512", + "PROPOSER_REWARD_QUOTIENT": "8", + "MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP": "16384", + "EPOCHS_PER_HISTORICAL_VECTOR": "65536", + "MIN_PER_EPOCH_CHURN_LIMIT": "4", + "MAX_ATTESTER_SLASHINGS_ELECTRA": "1", + "TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE": "16", + "MAX_DEPOSITS": "16", + "BELLATRIX_FORK_EPOCH": "0", + "MAX_REQUEST_BLOB_SIDECARS": "768", + "REORG_HEAD_WEIGHT_THRESHOLD": "20", + "TARGET_AGGREGATORS_PER_COMMITTEE": "16", + "DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF": "0x08000000", + "MESSAGE_DOMAIN_INVALID_SNAPPY": "0x00000000", + "EPOCHS_PER_SLASHINGS_VECTOR": "8192", + "MIN_SLASHING_PENALTY_QUOTIENT": "128", + "MAX_BLS_TO_EXECUTION_CHANGES": "16", + "GOSSIP_MAX_SIZE": "10485760", + "DOMAIN_BEACON_ATTESTER": "0x01000000", + "EPOCHS_PER_SUBNET_SUBSCRIPTION": "256", + "MAX_ATTESTATIONS_ELECTRA": "8", + "ATTESTATION_SUBNET_COUNT": "64", + "GENESIS_DELAY": "300", + "MAX_SEED_LOOKAHEAD": "4", + "ETH1_FOLLOW_DISTANCE": "2048", + "SECONDS_PER_SLOT": "12", + "REORG_PARENT_WEIGHT_THRESHOLD": "160", + "MIN_SYNC_COMMITTEE_PARTICIPANTS": "1", + "BELLATRIX_FORK_VERSION": "0x03017000", + "PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX": "3", + "EFFECTIVE_BALANCE_INCREMENT": "1000000000", + "FIELD_ELEMENTS_PER_BLOB": "4096", + "MIN_EPOCHS_TO_INACTIVITY_PENALTY": "4", + "BASE_REWARD_FACTOR": "64", + "MAX_EXTRA_DATA_BYTES": "32", + "CONFIG_NAME": "holesky", + "MAX_PROPOSER_SLASHINGS": "16", + "INACTIVITY_SCORE_RECOVERY_RATE": "16", + "MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS": "4096", + "MAX_TRANSACTIONS_PER_PAYLOAD": "1048576", + "DEPOSIT_CONTRACT_ADDRESS": "0x4242424242424242424242424242424242424242", + "MIN_ATTESTATION_INCLUSION_DELAY": "1", + "SHUFFLE_ROUND_COUNT": "90", + "TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH": "18446744073709551615", + "MAX_EFFECTIVE_BALANCE": "32000000000", + "DOMAIN_BEACON_PROPOSER": "0x00000000", + "DENEB_FORK_EPOCH": "29696", + "DOMAIN_SYNC_COMMITTEE": "0x07000000", + "PROPOSER_SCORE_BOOST": "40", + "DOMAIN_SELECTION_PROOF": "0x05000000", + "MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX": "32", + "MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT": "8", + "HYSTERESIS_UPWARD_MULTIPLIER": "5", + "SUBNETS_PER_NODE": "2", + "MIN_DEPOSIT_AMOUNT": "1000000000", + "MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA": "4096", + "PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR": "2", + "MAX_BLOBS_PER_BLOCK": "6", + "MIN_VALIDATOR_WITHDRAWABILITY_DELAY": "256", + "MAXIMUM_GOSSIP_CLOCK_DISPARITY": "500", + "TARGET_COMMITTEE_SIZE": "128", + "TERMINAL_BLOCK_HASH": "0x0000000000000000000000000000000000000000000000000000000000000000", + "DOMAIN_DEPOSIT": "0x03000000", + "DOMAIN_CONTRIBUTION_AND_PROOF": "0x09000000", + "UPDATE_TIMEOUT": "8192", + "ELECTRA_FORK_EPOCH": "18446744073709551615", + "SYNC_COMMITTEE_BRANCH_LENGTH": "5", + "DEPOSIT_CHAIN_ID": "17000", + "MAX_BLOB_COMMITMENTS_PER_BLOCK": "4096", + "DOMAIN_RANDAO": "0x02000000", + "CAPELLA_FORK_VERSION": "0x04017000", + "MAX_EFFECTIVE_BALANCE_ELECTRA": "2048000000000", + "MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR": "64", + "EPOCHS_PER_ETH1_VOTING_PERIOD": "64", + "WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA": "4096", + "HISTORICAL_ROOTS_LIMIT": "16777216", + "ATTESTATION_PROPAGATION_SLOT_RANGE": "32", + "SYNC_COMMITTEE_SIZE": "512", + "ATTESTATION_SUBNET_PREFIX_BITS": "6", + "PROPORTIONAL_SLASHING_MULTIPLIER": "1", + "MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD": "16", + "MESSAGE_DOMAIN_VALID_SNAPPY": "0x01000000", + "MAX_VOLUNTARY_EXITS": "16", + "PENDING_CONSOLIDATIONS_LIMIT": "262144", + "HYSTERESIS_DOWNWARD_MULTIPLIER": "1", + "DOMAIN_APPLICATION_BUILDER": "0x00000001", + "MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP": "8", + "EPOCHS_PER_SYNC_COMMITTEE_PERIOD": "256", + "BYTES_PER_LOGS_BLOOM": "256", + "MAX_DEPOSIT_REQUESTS_PER_PAYLOAD": "8192", + "MIN_GENESIS_ACTIVE_VALIDATOR_COUNT": "16384", + "MAX_ATTESTATIONS": "128", + "MIN_EPOCHS_FOR_BLOCK_REQUESTS": "33024", + "DENEB_FORK_VERSION": "0x05017000", + "ELECTRA_FORK_VERSION": "0x06017000", + "MAX_REQUEST_BLOCKS": "1024", + "GENESIS_FORK_VERSION": "0x01017000", + "KZG_COMMITMENT_INCLUSION_PROOF_DEPTH": "17", + "DEPOSIT_NETWORK_ID": "17000", + "MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD": "1", + "MAX_REQUEST_BLOCKS_DENEB": "128", + "BLOB_SIDECAR_SUBNET_COUNT": "6", + "SYNC_COMMITTEE_SUBNET_COUNT": "4", + "CAPELLA_FORK_EPOCH": "256", + "EJECTION_BALANCE": "28000000000", + "ATTESTATION_SUBNET_EXTRA_BITS": "0", + "MAX_COMMITTEES_PER_SLOT": "64", + "SHARD_COMMITTEE_PERIOD": "256", + "INACTIVITY_PENALTY_QUOTIENT_ALTAIR": "50331648", + "DOMAIN_AGGREGATE_AND_PROOF": "0x06000000", + "CHURN_LIMIT_QUOTIENT": "65536", + "BLS_WITHDRAWAL_PREFIX": "0x00", + "MIN_ACTIVATION_BALANCE": "32000000000" +} \ No newline at end of file diff --git a/tests/data/holesky_spec_data.json b/tests/data/holesky_spec_data.json new file mode 100644 index 00000000..a7bde18d --- /dev/null +++ b/tests/data/holesky_spec_data.json @@ -0,0 +1,143 @@ +{ + "data": { + "SLOTS_PER_EPOCH": "32", + "PRESET_BASE": "mainnet", + "TERMINAL_TOTAL_DIFFICULTY": "0", + "INACTIVITY_SCORE_BIAS": "4", + "PENDING_BALANCE_DEPOSITS_LIMIT": "134217728", + "MAX_ATTESTER_SLASHINGS": "2", + "MAX_WITHDRAWALS_PER_PAYLOAD": "16", + "INACTIVITY_PENALTY_QUOTIENT_BELLATRIX": "16777216", + "PENDING_PARTIAL_WITHDRAWALS_LIMIT": "134217728", + "INACTIVITY_PENALTY_QUOTIENT": "67108864", + "SAFE_SLOTS_TO_UPDATE_JUSTIFIED": "8", + "SECONDS_PER_ETH1_BLOCK": "14", + "MIN_SEED_LOOKAHEAD": "1", + "VALIDATOR_REGISTRY_LIMIT": "1099511627776", + "REORG_MAX_EPOCHS_SINCE_FINALIZATION": "2", + "SLOTS_PER_HISTORICAL_ROOT": "8192", + "RESP_TIMEOUT": "10", + "DOMAIN_VOLUNTARY_EXIT": "0x04000000", + "MAX_VALIDATORS_PER_COMMITTEE": "2048", + "MIN_GENESIS_TIME": "1695902100", + "ALTAIR_FORK_EPOCH": "0", + "HYSTERESIS_QUOTIENT": "4", + "ALTAIR_FORK_VERSION": "0x02017000", + "MAX_BYTES_PER_TRANSACTION": "1073741824", + "MAX_CHUNK_SIZE": "10485760", + "TTFB_TIMEOUT": "5", + "WHISTLEBLOWER_REWARD_QUOTIENT": "512", + "PROPOSER_REWARD_QUOTIENT": "8", + "MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP": "16384", + "EPOCHS_PER_HISTORICAL_VECTOR": "65536", + "MIN_PER_EPOCH_CHURN_LIMIT": "4", + "MAX_ATTESTER_SLASHINGS_ELECTRA": "1", + "TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE": "16", + "MAX_DEPOSITS": "16", + "BELLATRIX_FORK_EPOCH": "0", + "MAX_REQUEST_BLOB_SIDECARS": "768", + "REORG_HEAD_WEIGHT_THRESHOLD": "20", + "TARGET_AGGREGATORS_PER_COMMITTEE": "16", + "DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF": "0x08000000", + "MESSAGE_DOMAIN_INVALID_SNAPPY": "0x00000000", + "EPOCHS_PER_SLASHINGS_VECTOR": "8192", + "MIN_SLASHING_PENALTY_QUOTIENT": "128", + "MAX_BLS_TO_EXECUTION_CHANGES": "16", + "GOSSIP_MAX_SIZE": "10485760", + "DOMAIN_BEACON_ATTESTER": "0x01000000", + "EPOCHS_PER_SUBNET_SUBSCRIPTION": "256", + "MAX_ATTESTATIONS_ELECTRA": "8", + "ATTESTATION_SUBNET_COUNT": "64", + "GENESIS_DELAY": "300", + "MAX_SEED_LOOKAHEAD": "4", + "ETH1_FOLLOW_DISTANCE": "2048", + "SECONDS_PER_SLOT": "12", + "REORG_PARENT_WEIGHT_THRESHOLD": "160", + "MIN_SYNC_COMMITTEE_PARTICIPANTS": "1", + "BELLATRIX_FORK_VERSION": "0x03017000", + "PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX": "3", + "EFFECTIVE_BALANCE_INCREMENT": "1000000000", + "FIELD_ELEMENTS_PER_BLOB": "4096", + "MIN_EPOCHS_TO_INACTIVITY_PENALTY": "4", + "BASE_REWARD_FACTOR": "64", + "MAX_EXTRA_DATA_BYTES": "32", + "CONFIG_NAME": "holesky", + "MAX_PROPOSER_SLASHINGS": "16", + "INACTIVITY_SCORE_RECOVERY_RATE": "16", + "MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS": "4096", + "MAX_TRANSACTIONS_PER_PAYLOAD": "1048576", + "DEPOSIT_CONTRACT_ADDRESS": "0x4242424242424242424242424242424242424242", + "MIN_ATTESTATION_INCLUSION_DELAY": "1", + "SHUFFLE_ROUND_COUNT": "90", + "TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH": "18446744073709551615", + "MAX_EFFECTIVE_BALANCE": "32000000000", + "DOMAIN_BEACON_PROPOSER": "0x00000000", + "DENEB_FORK_EPOCH": "29696", + "DOMAIN_SYNC_COMMITTEE": "0x07000000", + "PROPOSER_SCORE_BOOST": "40", + "DOMAIN_SELECTION_PROOF": "0x05000000", + "MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX": "32", + "MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT": "8", + "HYSTERESIS_UPWARD_MULTIPLIER": "5", + "SUBNETS_PER_NODE": "2", + "MIN_DEPOSIT_AMOUNT": "1000000000", + "MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA": "4096", + "PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR": "2", + "MAX_BLOBS_PER_BLOCK": "6", + "MIN_VALIDATOR_WITHDRAWABILITY_DELAY": "256", + "MAXIMUM_GOSSIP_CLOCK_DISPARITY": "500", + "TARGET_COMMITTEE_SIZE": "128", + "TERMINAL_BLOCK_HASH": "0x0000000000000000000000000000000000000000000000000000000000000000", + "DOMAIN_DEPOSIT": "0x03000000", + "DOMAIN_CONTRIBUTION_AND_PROOF": "0x09000000", + "UPDATE_TIMEOUT": "8192", + "ELECTRA_FORK_EPOCH": "18446744073709551615", + "SYNC_COMMITTEE_BRANCH_LENGTH": "5", + "DEPOSIT_CHAIN_ID": "17000", + "MAX_BLOB_COMMITMENTS_PER_BLOCK": "4096", + "DOMAIN_RANDAO": "0x02000000", + "CAPELLA_FORK_VERSION": "0x04017000", + "MAX_EFFECTIVE_BALANCE_ELECTRA": "2048000000000", + "MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR": "64", + "EPOCHS_PER_ETH1_VOTING_PERIOD": "64", + "WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA": "4096", + "HISTORICAL_ROOTS_LIMIT": "16777216", + "ATTESTATION_PROPAGATION_SLOT_RANGE": "32", + "SYNC_COMMITTEE_SIZE": "512", + "ATTESTATION_SUBNET_PREFIX_BITS": "6", + "PROPORTIONAL_SLASHING_MULTIPLIER": "1", + "MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD": "16", + "MESSAGE_DOMAIN_VALID_SNAPPY": "0x01000000", + "MAX_VOLUNTARY_EXITS": "16", + "PENDING_CONSOLIDATIONS_LIMIT": "262144", + "HYSTERESIS_DOWNWARD_MULTIPLIER": "1", + "DOMAIN_APPLICATION_BUILDER": "0x00000001", + "MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP": "8", + "EPOCHS_PER_SYNC_COMMITTEE_PERIOD": "256", + "BYTES_PER_LOGS_BLOOM": "256", + "MAX_DEPOSIT_REQUESTS_PER_PAYLOAD": "8192", + "MIN_GENESIS_ACTIVE_VALIDATOR_COUNT": "16384", + "MAX_ATTESTATIONS": "128", + "MIN_EPOCHS_FOR_BLOCK_REQUESTS": "33024", + "DENEB_FORK_VERSION": "0x05017000", + "ELECTRA_FORK_VERSION": "0x06017000", + "MAX_REQUEST_BLOCKS": "1024", + "GENESIS_FORK_VERSION": "0x01017000", + "KZG_COMMITMENT_INCLUSION_PROOF_DEPTH": "17", + "DEPOSIT_NETWORK_ID": "17000", + "MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD": "1", + "MAX_REQUEST_BLOCKS_DENEB": "128", + "BLOB_SIDECAR_SUBNET_COUNT": "6", + "SYNC_COMMITTEE_SUBNET_COUNT": "4", + "CAPELLA_FORK_EPOCH": "256", + "EJECTION_BALANCE": "28000000000", + "ATTESTATION_SUBNET_EXTRA_BITS": "0", + "MAX_COMMITTEES_PER_SLOT": "64", + "SHARD_COMMITTEE_PERIOD": "256", + "INACTIVITY_PENALTY_QUOTIENT_ALTAIR": "50331648", + "DOMAIN_AGGREGATE_AND_PROOF": "0x06000000", + "CHURN_LIMIT_QUOTIENT": "65536", + "BLS_WITHDRAWAL_PREFIX": "0x00", + "MIN_ACTIVATION_BALANCE": "32000000000" + } +} \ No newline at end of file diff --git a/tests/tests/config.rs b/tests/tests/config.rs index b7775f45..684a2b84 100644 --- a/tests/tests/config.rs +++ b/tests/tests/config.rs @@ -1,12 +1,12 @@ use cb_common::{config::CommitBoostConfig, types::Chain}; use eyre::Result; -#[tokio::test] -async fn test_load_config() -> Result<()> { +#[test] +fn test_load_config() -> Result<()> { let config = CommitBoostConfig::from_file("../config.example.toml")?; assert_eq!(config.chain, Chain::Holesky); assert!(config.relays[0].headers.is_some()); - // TODO: add more + Ok(()) }