From d4341c2d7a8f0676e36520d3e76b789fd99b438d Mon Sep 17 00:00:00 2001 From: JasonVranek Date: Wed, 25 Feb 2026 12:07:38 -0800 Subject: [PATCH 1/4] add chain_id to Custom chains + remove helder --- config.example.toml | 2 +- crates/common/src/config/mod.rs | 5 +- crates/common/src/signature.rs | 4 - crates/common/src/types.rs | 81 ++++++----------- tests/data/helder_spec.yml | 150 -------------------------------- 5 files changed, 32 insertions(+), 210 deletions(-) delete mode 100644 tests/data/helder_spec.yml diff --git a/config.example.toml b/config.example.toml index 08fe8a0b..680aaa99 100644 --- a/config.example.toml +++ b/config.example.toml @@ -2,7 +2,7 @@ # Some fields are optional and can be omitted, in which case the default value, if present, will be used. # Chain spec ID. Supported values: -# A network ID. Supported values: Mainnet, Holesky, Sepolia, Helder, Hoodi. Lower case values e.g. "mainnet" are also accepted +# A network ID. Supported values: Mainnet, Holesky, Sepolia, Hoodi. Lower case values e.g. "mainnet" are also accepted # A custom object, e.g., chain = { genesis_time_secs = 1695902400, path = "/path/to/spec.json" }, with 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" diff --git a/crates/common/src/config/mod.rs b/crates/common/src/config/mod.rs index c833d8e3..8ba35f42 100644 --- a/crates/common/src/config/mod.rs +++ b/crates/common/src/config/mod.rs @@ -68,7 +68,7 @@ impl CommitBoostConfig { let chain = match helper_config.chain { ChainLoader::Path { path, genesis_time_secs } => { // check if the file path is overridden by env var - let (slot_time_secs, genesis_fork_version, fulu_fork_slot) = + let (slot_time_secs, genesis_fork_version, fulu_fork_slot, chain_id) = if let Some(path) = load_optional_env_var(CHAIN_SPEC_ENV) { load_chain_from_file(path.parse()?)? } else { @@ -79,6 +79,7 @@ impl CommitBoostConfig { slot_time_secs, genesis_fork_version, fulu_fork_slot, + chain_id, } } ChainLoader::Known(known) => Chain::from(known), @@ -87,6 +88,7 @@ impl CommitBoostConfig { slot_time_secs, genesis_fork_version, fulu_fork_slot, + chain_id, } => { let genesis_fork_version: ForkVersion = genesis_fork_version.as_ref().try_into()?; Chain::Custom { @@ -94,6 +96,7 @@ impl CommitBoostConfig { slot_time_secs, genesis_fork_version, fulu_fork_slot, + chain_id, } } }; diff --git a/crates/common/src/signature.rs b/crates/common/src/signature.rs index 9753a9a1..d899842b 100644 --- a/crates/common/src/signature.rs +++ b/crates/common/src/signature.rs @@ -106,10 +106,6 @@ mod tests { compute_domain(Chain::Sepolia, APPLICATION_BUILDER_DOMAIN), Chain::Sepolia.builder_domain() ); - assert_eq!( - compute_domain(Chain::Helder, APPLICATION_BUILDER_DOMAIN), - Chain::Helder.builder_domain() - ); assert_eq!( compute_domain(Chain::Hoodi, APPLICATION_BUILDER_DOMAIN), Chain::Hoodi.builder_domain() diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs index 89934471..40d99438 100644 --- a/crates/common/src/types.rs +++ b/crates/common/src/types.rs @@ -34,13 +34,13 @@ pub enum Chain { Mainnet, Holesky, Sepolia, - Helder, Hoodi, Custom { genesis_time_secs: u64, slot_time_secs: u64, genesis_fork_version: ForkVersion, fulu_fork_slot: u64, + chain_id: u64, }, } @@ -69,7 +69,7 @@ pub type ForkVersion = [u8; 4]; impl std::fmt::Display for Chain { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Mainnet | Self::Holesky | Self::Sepolia | Self::Helder | Self::Hoodi => { + Self::Mainnet | Self::Holesky | Self::Sepolia | Self::Hoodi => { write!(f, "{self:?}") } Self::Custom { .. } => write!(f, "Custom"), @@ -83,19 +83,20 @@ impl std::fmt::Debug for Chain { Self::Mainnet => write!(f, "Mainnet"), Self::Holesky => write!(f, "Holesky"), Self::Sepolia => write!(f, "Sepolia"), - Self::Helder => write!(f, "Helder"), Self::Hoodi => write!(f, "Hoodi"), Self::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version, fulu_fork_slot, + chain_id, } => 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)) .field("fulu_fork_slot", fulu_fork_slot) + .field("chain_id", chain_id) .finish(), } } @@ -107,11 +108,8 @@ impl Chain { Chain::Mainnet => KnownChain::Mainnet.id(), Chain::Holesky => KnownChain::Holesky.id(), Chain::Sepolia => KnownChain::Sepolia.id(), - Chain::Helder => KnownChain::Helder.id(), Chain::Hoodi => KnownChain::Hoodi.id(), - Chain::Custom { .. } => { - unimplemented!("chain id is not supported on custom chains, please file an issue") - } + Chain::Custom { chain_id, .. } => *chain_id, } } @@ -120,7 +118,6 @@ impl Chain { Chain::Mainnet => KnownChain::Mainnet.builder_domain(), Chain::Holesky => KnownChain::Holesky.builder_domain(), Chain::Sepolia => KnownChain::Sepolia.builder_domain(), - Chain::Helder => KnownChain::Helder.builder_domain(), Chain::Hoodi => KnownChain::Hoodi.builder_domain(), Chain::Custom { .. } => compute_domain(*self, APPLICATION_BUILDER_DOMAIN), } @@ -131,7 +128,6 @@ impl Chain { Chain::Mainnet => KnownChain::Mainnet.genesis_fork_version(), Chain::Holesky => KnownChain::Holesky.genesis_fork_version(), Chain::Sepolia => KnownChain::Sepolia.genesis_fork_version(), - Chain::Helder => KnownChain::Helder.genesis_fork_version(), Chain::Hoodi => KnownChain::Hoodi.genesis_fork_version(), Chain::Custom { genesis_fork_version, .. } => *genesis_fork_version, } @@ -142,7 +138,6 @@ impl Chain { Chain::Mainnet => KnownChain::Mainnet.genesis_time_sec(), Chain::Holesky => KnownChain::Holesky.genesis_time_sec(), Chain::Sepolia => KnownChain::Sepolia.genesis_time_sec(), - Chain::Helder => KnownChain::Helder.genesis_time_sec(), Chain::Hoodi => KnownChain::Hoodi.genesis_time_sec(), Chain::Custom { genesis_time_secs, .. } => *genesis_time_secs, } @@ -153,7 +148,6 @@ impl Chain { Chain::Mainnet => KnownChain::Mainnet.slot_time_sec(), Chain::Holesky => KnownChain::Holesky.slot_time_sec(), Chain::Sepolia => KnownChain::Sepolia.slot_time_sec(), - Chain::Helder => KnownChain::Helder.slot_time_sec(), Chain::Hoodi => KnownChain::Hoodi.slot_time_sec(), Chain::Custom { slot_time_secs, .. } => *slot_time_secs, } @@ -164,7 +158,6 @@ impl Chain { Chain::Mainnet => KnownChain::Mainnet.fulu_fork_slot(), Chain::Holesky => KnownChain::Holesky.fulu_fork_slot(), Chain::Sepolia => KnownChain::Sepolia.fulu_fork_slot(), - Chain::Helder => KnownChain::Helder.fulu_fork_slot(), Chain::Hoodi => KnownChain::Hoodi.fulu_fork_slot(), Chain::Custom { slot_time_secs, .. } => *slot_time_secs, } @@ -183,8 +176,6 @@ pub enum KnownChain { Holesky, #[serde(alias = "sepolia")] Sepolia, - #[serde(alias = "helder")] - Helder, #[serde(alias = "hoodi")] Hoodi, } @@ -196,7 +187,6 @@ impl KnownChain { KnownChain::Mainnet => 1, KnownChain::Holesky => 17000, KnownChain::Sepolia => 11155111, - KnownChain::Helder => 167000, KnownChain::Hoodi => 560048, } } @@ -212,9 +202,6 @@ impl KnownChain { KnownChain::Sepolia => { b256!("0x00000001d3010778cd08ee514b08fe67b6c503b510987a4ce43f42306d97c67c") } - KnownChain::Helder => { - b256!("0x0000000194c41af484fff7964969e0bdd922f82dff0f4be87a60d0664cc9d1ff") - } KnownChain::Hoodi => { b256!("0x00000001719103511efa4f1362ff2a50996cccf329cc84cb410c5e5c7d351d03") } @@ -226,7 +213,6 @@ impl KnownChain { KnownChain::Mainnet => hex!("00000000"), KnownChain::Holesky => hex!("01017000"), KnownChain::Sepolia => hex!("90000069"), - KnownChain::Helder => hex!("10000000"), KnownChain::Hoodi => hex!("10000910"), } } @@ -236,7 +222,6 @@ impl KnownChain { KnownChain::Mainnet => 1606824023, KnownChain::Holesky => 1695902400, KnownChain::Sepolia => 1655733600, - KnownChain::Helder => 1718967660, KnownChain::Hoodi => 1742213400, } } @@ -246,7 +231,6 @@ impl KnownChain { KnownChain::Mainnet | KnownChain::Holesky | KnownChain::Sepolia | - KnownChain::Helder | KnownChain::Hoodi => 12, } } @@ -254,7 +238,6 @@ impl KnownChain { pub fn fulu_fork_slot(&self) -> u64 { match self { KnownChain::Mainnet => 13164544, - KnownChain::Helder => u64::MAX, KnownChain::Holesky => 5283840, KnownChain::Sepolia => 8724480, KnownChain::Hoodi => 1622016, @@ -268,7 +251,6 @@ impl From for Chain { KnownChain::Mainnet => Chain::Mainnet, KnownChain::Holesky => Chain::Holesky, KnownChain::Sepolia => Chain::Sepolia, - KnownChain::Helder => Chain::Helder, KnownChain::Hoodi => Chain::Hoodi, } } @@ -291,6 +273,7 @@ pub enum ChainLoader { slot_time_secs: u64, genesis_fork_version: Bytes, fulu_fork_slot: u64, + chain_id: u64, }, } @@ -303,18 +286,19 @@ impl Serialize for Chain { Chain::Mainnet => ChainLoader::Known(KnownChain::Mainnet), Chain::Holesky => ChainLoader::Known(KnownChain::Holesky), Chain::Sepolia => ChainLoader::Known(KnownChain::Sepolia), - Chain::Helder => ChainLoader::Known(KnownChain::Helder), Chain::Hoodi => ChainLoader::Known(KnownChain::Hoodi), Chain::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version, fulu_fork_slot, + chain_id, } => ChainLoader::Custom { genesis_time_secs: *genesis_time_secs, slot_time_secs: *slot_time_secs, genesis_fork_version: Bytes::from(*genesis_fork_version), fulu_fork_slot: *fulu_fork_slot, + chain_id: *chain_id, }, }; @@ -332,13 +316,14 @@ impl<'de> Deserialize<'de> for Chain { match loader { ChainLoader::Known(known) => Ok(Chain::from(known)), ChainLoader::Path { genesis_time_secs, path } => { - let (slot_time_secs, genesis_fork_version, fulu_fork_slot) = + let (slot_time_secs, genesis_fork_version, fulu_fork_slot, chain_id) = load_chain_from_file(path).map_err(serde::de::Error::custom)?; Ok(Chain::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version, fulu_fork_slot, + chain_id, }) } ChainLoader::Custom { @@ -346,6 +331,7 @@ impl<'de> Deserialize<'de> for Chain { slot_time_secs, genesis_fork_version, fulu_fork_slot, + chain_id, } => { let genesis_fork_version: ForkVersion = genesis_fork_version.as_ref().try_into().map_err(serde::de::Error::custom)?; @@ -354,6 +340,7 @@ impl<'de> Deserialize<'de> for Chain { slot_time_secs, genesis_fork_version, fulu_fork_slot, + chain_id, }) } } @@ -366,7 +353,7 @@ impl<'de> Deserialize<'de> for Chain { /// - 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<(u64, ForkVersion, u64)> { +pub fn load_chain_from_file(path: PathBuf) -> eyre::Result<(u64, ForkVersion, u64, u64)> { #[derive(Deserialize)] #[serde(rename_all = "UPPERCASE")] struct QuotedSpecFile { @@ -377,14 +364,16 @@ pub fn load_chain_from_file(path: PathBuf) -> eyre::Result<(u64, ForkVersion, u6 slots_per_epoch: u64, #[serde(with = "serde_utils::quoted_u64")] fulu_fork_epoch: u64, + #[serde(with = "serde_utils::quoted_u64")] + deposit_chain_id: u64, } impl QuotedSpecFile { - fn to_chain(&self) -> eyre::Result<(u64, ForkVersion, u64)> { + fn to_chain(&self) -> eyre::Result<(u64, ForkVersion, u64, u64)> { let genesis_fork_version: ForkVersion = self.genesis_fork_version.as_ref().try_into()?; let fulu_fork_slot = self.fulu_fork_epoch.saturating_mul(self.slots_per_epoch); - Ok((self.seconds_per_slot, genesis_fork_version, fulu_fork_slot)) + Ok((self.seconds_per_slot, genesis_fork_version, fulu_fork_slot, self.deposit_chain_id)) } } @@ -400,14 +389,15 @@ pub fn load_chain_from_file(path: PathBuf) -> eyre::Result<(u64, ForkVersion, u6 genesis_fork_version: u32, slots_per_epoch: Option, fulu_fork_epoch: u64, + deposit_chain_id: u64, } impl SpecFile { - fn to_chain(&self) -> (u64, ForkVersion, u64) { + fn to_chain(&self) -> (u64, ForkVersion, u64, u64) { let genesis_fork_version: ForkVersion = self.genesis_fork_version.to_be_bytes(); let fulu_fork_slot = self.fulu_fork_epoch.saturating_mul(self.slots_per_epoch.unwrap_or(32)); - (self.seconds_per_slot, genesis_fork_version, fulu_fork_slot) + (self.seconds_per_slot, genesis_fork_version, fulu_fork_slot, self.deposit_chain_id) } } @@ -443,13 +433,14 @@ mod tests { #[test] fn test_load_custom() { - let s = r#"chain = { genesis_time_secs = 1, slot_time_secs = 2, genesis_fork_version = "0x01000000", fulu_fork_slot = 1 }"#; + let s = r#"chain = { genesis_time_secs = 1, slot_time_secs = 2, genesis_fork_version = "0x01000000", fulu_fork_slot = 1, chain_id = 123 }"#; 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], - fulu_fork_slot: 1 + fulu_fork_slot: 1, + chain_id: 123, }) } @@ -492,6 +483,7 @@ mod tests { slot_time_secs: KnownChain::Holesky.slot_time_sec(), genesis_fork_version: KnownChain::Holesky.genesis_fork_version(), fulu_fork_slot: KnownChain::Holesky.fulu_fork_slot(), + chain_id: KnownChain::Holesky.id(), }) } @@ -507,12 +499,13 @@ mod tests { let s = format!("chain = {{ genesis_time_secs = 1, path = {path:?}}}"); let decoded: MockConfig = toml::from_str(&s).unwrap(); - assert_eq!(decoded.chain.slot_time_sec(), KnownChain::Helder.slot_time_sec()); + assert_eq!(decoded.chain.slot_time_sec(), KnownChain::Sepolia.slot_time_sec()); assert_eq!(decoded.chain, Chain::Custom { genesis_time_secs: 1, slot_time_secs: KnownChain::Sepolia.slot_time_sec(), genesis_fork_version: KnownChain::Sepolia.genesis_fork_version(), fulu_fork_slot: KnownChain::Sepolia.fulu_fork_slot(), + chain_id: KnownChain::Sepolia.id(), }) } @@ -534,27 +527,7 @@ mod tests { slot_time_secs: KnownChain::Hoodi.slot_time_sec(), genesis_fork_version: KnownChain::Hoodi.genesis_fork_version(), fulu_fork_slot: KnownChain::Hoodi.fulu_fork_slot(), - }) - } - - #[test] - fn test_spec_helder_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 = {{ genesis_time_secs = 1, path = {path:?}}}"); - - let decoded: MockConfig = toml::from_str(&s).unwrap(); - assert_eq!(decoded.chain.slot_time_sec(), KnownChain::Helder.slot_time_sec()); - assert_eq!(decoded.chain, Chain::Custom { - genesis_time_secs: 1, - slot_time_secs: KnownChain::Helder.slot_time_sec(), - genesis_fork_version: KnownChain::Helder.genesis_fork_version(), - fulu_fork_slot: KnownChain::Helder.fulu_fork_slot(), + chain_id: KnownChain::Hoodi.id(), }) } } diff --git a/tests/data/helder_spec.yml b/tests/data/helder_spec.yml deleted file mode 100644 index 7d3f9b04..00000000 --- a/tests/data/helder_spec.yml +++ /dev/null @@ -1,150 +0,0 @@ -# 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 - -FULU_FORK_EPOCH: 18446744073709551615 - -# 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) From f55ae3d8a2c61b29705ed9c481c42ad125d2d618 Mon Sep 17 00:00:00 2001 From: JasonVranek Date: Wed, 25 Feb 2026 12:12:53 -0800 Subject: [PATCH 2/4] fix broken pbs config tests --- tests/tests/pbs_cfg_file_update.rs | 3 ++- tests/tests/pbs_mux.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/tests/pbs_cfg_file_update.rs b/tests/tests/pbs_cfg_file_update.rs index 0c4e8e47..0699eb27 100644 --- a/tests/tests/pbs_cfg_file_update.rs +++ b/tests/tests/pbs_cfg_file_update.rs @@ -59,7 +59,8 @@ async fn test_cfg_file_update() -> Result<()> { * or anything close to it */ extra_validation_enabled: false, rpc_url: None, - ssv_api_url: Url::parse("http://example.com").unwrap(), + ssv_node_api_url: Url::parse("http://example.com").unwrap(), + ssv_public_api_url: Url::parse("http://example.com").unwrap(), http_timeout_seconds: 10, register_validator_retry_limit: 3, validator_registration_batch_size: None, diff --git a/tests/tests/pbs_mux.rs b/tests/tests/pbs_mux.rs index 85c84a55..34da1dc7 100644 --- a/tests/tests/pbs_mux.rs +++ b/tests/tests/pbs_mux.rs @@ -345,7 +345,7 @@ async fn test_ssv_multi_with_node() -> Result<()> { config.registry_muxes = Some(registry_muxes); // Run PBS service - let state = PbsState::new(config); + let state = PbsState::new(config, PathBuf::new()); let pbs_server = tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); info!("Started PBS server with pubkey {pubkey}"); @@ -441,7 +441,7 @@ async fn test_ssv_multi_with_public() -> Result<()> { config.registry_muxes = Some(registry_muxes); // Run PBS service - let state = PbsState::new(config); + let state = PbsState::new(config, PathBuf::new()); let pbs_server = tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state)); info!("Started PBS server with pubkey {pubkey}"); From 122644c871bc136b5a8240257395faf63d67a3eb Mon Sep 17 00:00:00 2001 From: JasonVranek Date: Wed, 25 Feb 2026 12:15:31 -0800 Subject: [PATCH 3/4] just fmt --- crates/common/src/types.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs index 40d99438..b82866f3 100644 --- a/crates/common/src/types.rs +++ b/crates/common/src/types.rs @@ -228,10 +228,9 @@ impl KnownChain { pub fn slot_time_sec(&self) -> u64 { match self { - KnownChain::Mainnet | - KnownChain::Holesky | - KnownChain::Sepolia | - KnownChain::Hoodi => 12, + KnownChain::Mainnet | KnownChain::Holesky | KnownChain::Sepolia | KnownChain::Hoodi => { + 12 + } } } From 96d8640e88ca33e4eef7c35749a0835dd142a379 Mon Sep 17 00:00:00 2001 From: JasonVranek Date: Thu, 26 Feb 2026 10:38:23 -0800 Subject: [PATCH 4/4] add a custom kurtosis spec file to test against to replace helder --- config.example.toml | 2 +- crates/common/src/types.rs | 26 +++++- tests/data/kurtosis_spec.json | 168 ++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 tests/data/kurtosis_spec.json diff --git a/config.example.toml b/config.example.toml index 680aaa99..cc065442 100644 --- a/config.example.toml +++ b/config.example.toml @@ -4,7 +4,7 @@ # Chain spec ID. Supported values: # A network ID. Supported values: Mainnet, Holesky, Sepolia, Hoodi. Lower case values e.g. "mainnet" are also accepted # A custom object, e.g., chain = { genesis_time_secs = 1695902400, path = "/path/to/spec.json" }, with 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" }. +# A custom object, e.g., chain = { genesis_time_secs = 1695902400, slot_time_secs = 12, genesis_fork_version = "0x01017000", chain_id = 17000 }. chain = "Holesky" # Configuration for the PBS module diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs index b82866f3..695681e0 100644 --- a/crates/common/src/types.rs +++ b/crates/common/src/types.rs @@ -346,8 +346,8 @@ impl<'de> Deserialize<'de> for Chain { } } -/// Returns seconds_per_slot and genesis_fork_version from a spec, such as -/// returned by /eth/v1/config/spec ref: https://ethereum.github.io/beacon-APIs/#/Config/getSpec +/// Returns seconds_per_slot, genesis_fork_version, fulu_fork_epoch, and +/// deposit_chain_id from a spec, 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 @@ -529,4 +529,26 @@ mod tests { chain_id: KnownChain::Hoodi.id(), }) } + + #[test] + fn test_spec_kurtosis_data_json() { + let a = env!("CARGO_MANIFEST_DIR"); + let mut path = PathBuf::from(a); + + path.pop(); + path.pop(); + path.push("tests/data/kurtosis_spec.json"); + + let s = format!("chain = {{ genesis_time_secs = 1, path = {path:?}}}"); + + let decoded: MockConfig = toml::from_str(&s).unwrap(); + assert_eq!(decoded.chain.slot_time_sec(), 12); + assert_eq!(decoded.chain, Chain::Custom { + genesis_time_secs: 1, + slot_time_secs: 12, + genesis_fork_version: hex!("0x10000038"), + fulu_fork_slot: 0, + chain_id: 3151908, + }) + } } diff --git a/tests/data/kurtosis_spec.json b/tests/data/kurtosis_spec.json new file mode 100644 index 00000000..9129f573 --- /dev/null +++ b/tests/data/kurtosis_spec.json @@ -0,0 +1,168 @@ +{ + "data": { + "CONFIG_NAME": "testnet", + "PRESET_BASE": "mainnet", + "TERMINAL_TOTAL_DIFFICULTY": "0", + "TERMINAL_BLOCK_HASH": "0x0000000000000000000000000000000000000000000000000000000000000000", + "TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH": "18446744073709551615", + "MIN_GENESIS_ACTIVE_VALIDATOR_COUNT": "4", + "MIN_GENESIS_TIME": "1772130224", + "GENESIS_FORK_VERSION": "0x10000038", + "GENESIS_DELAY": "0", + "ALTAIR_FORK_VERSION": "0x20000038", + "ALTAIR_FORK_EPOCH": "0", + "BELLATRIX_FORK_VERSION": "0x30000038", + "BELLATRIX_FORK_EPOCH": "0", + "CAPELLA_FORK_VERSION": "0x40000038", + "CAPELLA_FORK_EPOCH": "0", + "DENEB_FORK_VERSION": "0x50000038", + "DENEB_FORK_EPOCH": "0", + "ELECTRA_FORK_VERSION": "0x60000038", + "ELECTRA_FORK_EPOCH": "0", + "FULU_FORK_VERSION": "0x70000038", + "FULU_FORK_EPOCH": "0", + "GLOAS_FORK_VERSION": "0x80000038", + "GLOAS_FORK_EPOCH": "18446744073709551615", + "SECONDS_PER_SLOT": "12", + "SECONDS_PER_ETH1_BLOCK": "12", + "MIN_VALIDATOR_WITHDRAWABILITY_DELAY": "256", + "SHARD_COMMITTEE_PERIOD": "256", + "ETH1_FOLLOW_DISTANCE": "2048", + "SUBNETS_PER_NODE": "2", + "INACTIVITY_SCORE_BIAS": "4", + "INACTIVITY_SCORE_RECOVERY_RATE": "16", + "EJECTION_BALANCE": "16000000000", + "MIN_PER_EPOCH_CHURN_LIMIT": "4", + "MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT": "8", + "CHURN_LIMIT_QUOTIENT": "65536", + "PROPOSER_SCORE_BOOST": "40", + "DEPOSIT_CHAIN_ID": "3151908", + "DEPOSIT_NETWORK_ID": "3151908", + "DEPOSIT_CONTRACT_ADDRESS": "0x00000000219ab540356cbb839cbe05303d7705fa", + "GAS_LIMIT_ADJUSTMENT_FACTOR": "1024", + "MAX_PAYLOAD_SIZE": "10485760", + "MAX_REQUEST_BLOCKS": "1024", + "MIN_EPOCHS_FOR_BLOCK_REQUESTS": "33024", + "TTFB_TIMEOUT": "5", + "RESP_TIMEOUT": "10", + "ATTESTATION_PROPAGATION_SLOT_RANGE": "32", + "MAXIMUM_GOSSIP_CLOCK_DISPARITY": "500", + "MESSAGE_DOMAIN_INVALID_SNAPPY": "0x00000000", + "MESSAGE_DOMAIN_VALID_SNAPPY": "0x01000000", + "ATTESTATION_SUBNET_PREFIX_BITS": "6", + "MAX_REQUEST_BLOCKS_DENEB": "128", + "MAX_REQUEST_BLOB_SIDECARS": "768", + "MAX_REQUEST_DATA_COLUMN_SIDECARS": "16384", + "MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS": "4096", + "BLOB_SIDECAR_SUBNET_COUNT": "6", + "MAX_BLOBS_PER_BLOCK": "6", + "MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA": "128000000000", + "MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT": "256000000000", + "MAX_BLOBS_PER_BLOCK_ELECTRA": "9", + "BLOB_SIDECAR_SUBNET_COUNT_ELECTRA": "9", + "MAX_REQUEST_BLOB_SIDECARS_ELECTRA": "1152", + "NUMBER_OF_CUSTODY_GROUPS": "128", + "DATA_COLUMN_SIDECAR_SUBNET_COUNT": "128", + "SAMPLES_PER_SLOT": "8", + "CUSTODY_REQUIREMENT": "4", + "BLOB_SCHEDULE": [ + { + "EPOCH": "0", + "MAX_BLOBS_PER_BLOCK": "15" + } + ], + "VALIDATOR_CUSTODY_REQUIREMENT": "8", + "BALANCE_PER_ADDITIONAL_CUSTODY_GROUP": "32000000000", + "MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS": "4096", + "MAX_COMMITTEES_PER_SLOT": "64", + "TARGET_COMMITTEE_SIZE": "128", + "MAX_VALIDATORS_PER_COMMITTEE": "2048", + "SHUFFLE_ROUND_COUNT": "90", + "HYSTERESIS_QUOTIENT": "4", + "HYSTERESIS_DOWNWARD_MULTIPLIER": "1", + "HYSTERESIS_UPWARD_MULTIPLIER": "5", + "MIN_DEPOSIT_AMOUNT": "1000000000", + "MAX_EFFECTIVE_BALANCE": "32000000000", + "EFFECTIVE_BALANCE_INCREMENT": "1000000000", + "MIN_ATTESTATION_INCLUSION_DELAY": "1", + "SLOTS_PER_EPOCH": "32", + "MIN_SEED_LOOKAHEAD": "1", + "MAX_SEED_LOOKAHEAD": "4", + "EPOCHS_PER_ETH1_VOTING_PERIOD": "64", + "SLOTS_PER_HISTORICAL_ROOT": "8192", + "MIN_EPOCHS_TO_INACTIVITY_PENALTY": "4", + "EPOCHS_PER_HISTORICAL_VECTOR": "65536", + "EPOCHS_PER_SLASHINGS_VECTOR": "8192", + "HISTORICAL_ROOTS_LIMIT": "16777216", + "VALIDATOR_REGISTRY_LIMIT": "1099511627776", + "BASE_REWARD_FACTOR": "64", + "WHISTLEBLOWER_REWARD_QUOTIENT": "512", + "PROPOSER_REWARD_QUOTIENT": "8", + "INACTIVITY_PENALTY_QUOTIENT": "67108864", + "MIN_SLASHING_PENALTY_QUOTIENT": "128", + "PROPORTIONAL_SLASHING_MULTIPLIER": "1", + "MAX_PROPOSER_SLASHINGS": "16", + "MAX_ATTESTER_SLASHINGS": "2", + "MAX_ATTESTATIONS": "128", + "MAX_DEPOSITS": "16", + "MAX_VOLUNTARY_EXITS": "16", + "INACTIVITY_PENALTY_QUOTIENT_ALTAIR": "50331648", + "MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR": "64", + "PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR": "2", + "SYNC_COMMITTEE_SIZE": "512", + "EPOCHS_PER_SYNC_COMMITTEE_PERIOD": "256", + "MIN_SYNC_COMMITTEE_PARTICIPANTS": "1", + "INACTIVITY_PENALTY_QUOTIENT_BELLATRIX": "16777216", + "MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX": "32", + "PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX": "3", + "MAX_BYTES_PER_TRANSACTION": "1073741824", + "MAX_TRANSACTIONS_PER_PAYLOAD": "1048576", + "BYTES_PER_LOGS_BLOOM": "256", + "MAX_EXTRA_DATA_BYTES": "32", + "MAX_BLS_TO_EXECUTION_CHANGES": "16", + "MAX_WITHDRAWALS_PER_PAYLOAD": "16", + "MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP": "16384", + "MAX_BLOB_COMMITMENTS_PER_BLOCK": "4096", + "KZG_COMMITMENT_INCLUSION_PROOF_DEPTH": "17", + "FIELD_ELEMENTS_PER_BLOB": "4096", + "MIN_ACTIVATION_BALANCE": "32000000000", + "MAX_EFFECTIVE_BALANCE_ELECTRA": "2048000000000", + "MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA": "4096", + "WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA": "4096", + "PENDING_DEPOSITS_LIMIT": "134217728", + "PENDING_PARTIAL_WITHDRAWALS_LIMIT": "134217728", + "PENDING_CONSOLIDATIONS_LIMIT": "262144", + "MAX_ATTESTER_SLASHINGS_ELECTRA": "1", + "MAX_ATTESTATIONS_ELECTRA": "8", + "MAX_DEPOSIT_REQUESTS_PER_PAYLOAD": "8192", + "MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD": "16", + "MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD": "2", + "MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP": "8", + "MAX_PENDING_DEPOSITS_PER_EPOCH": "16", + "FIELD_ELEMENTS_PER_CELL": "64", + "FIELD_ELEMENTS_PER_EXT_BLOB": "8192", + "KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH": "4", + "CELLS_PER_EXT_BLOB": "128", + "NUMBER_OF_COLUMNS": "128", + "DOMAIN_DEPOSIT": "0x03000000", + "DOMAIN_BEACON_ATTESTER": "0x01000000", + "ETH1_ADDRESS_WITHDRAWAL_PREFIX": "0x01", + "BLS_WITHDRAWAL_PREFIX": "0x00", + "DOMAIN_AGGREGATE_AND_PROOF": "0x06000000", + "DOMAIN_SELECTION_PROOF": "0x05000000", + "TARGET_AGGREGATORS_PER_COMMITTEE": "16", + "DOMAIN_CONTRIBUTION_AND_PROOF": "0x09000000", + "SYNC_COMMITTEE_SUBNET_COUNT": "4", + "UNSET_DEPOSIT_REQUESTS_START_INDEX": "18446744073709551615", + "DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF": "0x08000000", + "DOMAIN_APPLICATION_MASK": "0x00000001", + "DOMAIN_VOLUNTARY_EXIT": "0x04000000", + "DOMAIN_BEACON_PROPOSER": "0x00000000", + "VERSIONED_HASH_VERSION_KZG": "1", + "COMPOUNDING_WITHDRAWAL_PREFIX": "0x02", + "DOMAIN_SYNC_COMMITTEE": "0x07000000", + "FULL_EXIT_REQUEST_AMOUNT": "0", + "DOMAIN_RANDAO": "0x02000000", + "TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE": "16" + } +} \ No newline at end of file