From 7ab39209b615a3cbadfe9fc9bcc6bd6a25fede2b Mon Sep 17 00:00:00 2001 From: pls148 <184445976+pls148@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:47:06 -0700 Subject: [PATCH 1/5] load da_committee from genesis file --- crates/builder/src/lib.rs | 1 + crates/builder/src/non_permissioned.rs | 2 +- crates/hotshot/testing/src/test_builder.rs | 1 + .../hotshot/types/src/hotshot_config_file.rs | 6 ++- crates/hotshot/types/src/lib.rs | 30 +++++++++++- .../src/testing/consensus.rs | 1 + sequencer/src/genesis.rs | 48 ++++++++++++++++++- sequencer/src/lib.rs | 24 +++++++++- types/src/v0/config.rs | 12 ++++- types/src/v0/impls/stake_table.rs | 9 +++- types/src/v0/v0_3/mod.rs | 2 +- 11 files changed, 126 insertions(+), 10 deletions(-) diff --git a/crates/builder/src/lib.rs b/crates/builder/src/lib.rs index 8c33e5f3a88..b8351d7b525 100755 --- a/crates/builder/src/lib.rs +++ b/crates/builder/src/lib.rs @@ -120,6 +120,7 @@ pub mod testing { let config: HotShotConfig = HotShotConfig { num_nodes_with_stake: NonZeroUsize::new(num_nodes_with_stake).unwrap(), known_da_nodes: known_nodes_with_stake.clone(), + da_committees: Default::default(), known_nodes_with_stake: known_nodes_with_stake.clone(), next_view_timeout: Duration::from_secs(5).as_millis() as u64, num_bootstrap: 1usize, diff --git a/crates/builder/src/non_permissioned.rs b/crates/builder/src/non_permissioned.rs index 489e1509ab4..517e2f67579 100644 --- a/crates/builder/src/non_permissioned.rs +++ b/crates/builder/src/non_permissioned.rs @@ -66,7 +66,7 @@ pub fn build_instance_state( let coordinator = EpochMembershipCoordinator::new( Arc::new(RwLock::new(EpochCommittees::new_stake( vec![], - vec![], + Default::default(), None, fetcher, genesis.epoch_height.unwrap_or_default(), diff --git a/crates/hotshot/testing/src/test_builder.rs b/crates/hotshot/testing/src/test_builder.rs index 669882d30d4..bc158d8acd0 100644 --- a/crates/hotshot/testing/src/test_builder.rs +++ b/crates/hotshot/testing/src/test_builder.rs @@ -70,6 +70,7 @@ pub fn default_hotshot_config( start_threshold: (1, 1), num_nodes_with_stake: NonZeroUsize::new(known_nodes_with_stake.len()).unwrap(), known_da_nodes: known_da_nodes.clone(), + da_committees: Default::default(), num_bootstrap: num_bootstrap_nodes, known_nodes_with_stake: known_nodes_with_stake.clone(), da_staked_committee_size: known_da_nodes.len(), diff --git a/crates/hotshot/types/src/hotshot_config_file.rs b/crates/hotshot/types/src/hotshot_config_file.rs index 31394ce48ab..54a8933766b 100644 --- a/crates/hotshot/types/src/hotshot_config_file.rs +++ b/crates/hotshot/types/src/hotshot_config_file.rs @@ -4,7 +4,7 @@ // You should have received a copy of the MIT License // along with the HotShot repository. If not, see . -use std::{num::NonZeroUsize, time::Duration}; +use std::{collections::BTreeMap, num::NonZeroUsize, time::Duration}; use alloy::primitives::U256; use url::Url; @@ -45,6 +45,8 @@ pub struct HotShotConfigFile { #[serde(skip)] /// The known DA nodes' public key and stake values pub known_da_nodes: Vec>, + /// The known DA nodes' public keys and stake values, by start epoch + pub da_committees: BTreeMap>>, /// Number of staking DA nodes pub staked_da_nodes: usize, /// Number of fixed leaders for GPU VID @@ -89,6 +91,7 @@ impl From> for HotShotConfig { start_threshold: val.start_threshold, num_nodes_with_stake: val.num_nodes_with_stake, known_da_nodes: val.known_da_nodes, + da_committees: val.da_committees, known_nodes_with_stake: val.known_nodes_with_stake, da_staked_committee_size: val.staked_da_nodes, fixed_leader_for_gpuvid: val.fixed_leader_for_gpuvid, @@ -154,6 +157,7 @@ impl HotShotConfigFile { known_nodes_with_stake: gen_known_nodes_with_stake, staked_da_nodes, known_da_nodes, + da_committees: Default::default(), fixed_leader_for_gpuvid: 1, next_view_timeout: 10000, view_sync_timeout: Duration::from_millis(1000), diff --git a/crates/hotshot/types/src/lib.rs b/crates/hotshot/types/src/lib.rs index 30762b036d1..b516ad19675 100644 --- a/crates/hotshot/types/src/lib.rs +++ b/crates/hotshot/types/src/lib.rs @@ -5,7 +5,9 @@ // along with the HotShot repository. If not, see . //! Types and Traits for the `HotShot` consensus module -use std::{fmt::Debug, future::Future, num::NonZeroUsize, pin::Pin, time::Duration}; +use std::{ + collections::BTreeMap, fmt::Debug, future::Future, num::NonZeroUsize, pin::Pin, time::Duration, +}; use alloy::primitives::U256; use bincode::Options; @@ -19,7 +21,7 @@ use traits::{ use url::Url; use vec1::Vec1; -use crate::utils::bincode_opts; +use crate::{traits::node_implementation::ConsensusTime, utils::bincode_opts}; pub mod bundle; pub mod consensus; pub mod constants; @@ -195,6 +197,8 @@ pub struct HotShotConfig { pub known_nodes_with_stake: Vec>, /// All public keys known to be DA nodes pub known_da_nodes: Vec>, + /// All public keys known to be DA nodes, by start epoch + pub da_committees: BTreeMap>>, /// List of DA committee (staking)nodes for static DA committee pub da_staked_committee_size: usize, /// Number of fixed leaders for GPU VID, normally it will be 0, it's only used when running GPU VID @@ -266,4 +270,26 @@ impl HotShotConfig { pub fn hotshot_stake_table(&self) -> HSStakeTable { self.known_nodes_with_stake.clone().into() } + + pub fn build_da_committees(&self) -> BTreeMap>> { + if self.da_committees.is_empty() { + tracing::warn!( + "da_committees is not set, falling back to known_da_nodes, which is deprecated." + ); + + [(TYPES::Epoch::new(0), self.known_da_nodes.clone())].into() + } else { + if !self.known_da_nodes.is_empty() { + tracing::warn!( + "Both known_da_nodes and da_committees are set, known_da_nodes is deprecated \ + and will be ignored." + ); + } + + self.da_committees + .iter() + .map(|(k, v)| (TYPES::Epoch::new(*k), v.clone())) + .collect() + } + } } diff --git a/hotshot-query-service/src/testing/consensus.rs b/hotshot-query-service/src/testing/consensus.rs index d1e71879072..eddd3664bc8 100644 --- a/hotshot-query-service/src/testing/consensus.rs +++ b/hotshot-query-service/src/testing/consensus.rs @@ -141,6 +141,7 @@ impl MockNetwork { num_bootstrap: 0, da_staked_committee_size: pub_keys.len(), known_da_nodes: known_nodes_with_stake.clone(), + da_committees: Default::default(), data_request_delay: Duration::from_millis(200), view_sync_timeout: Duration::from_millis(250), start_threshold: ( diff --git a/sequencer/src/genesis.rs b/sequencer/src/genesis.rs index a19948c9155..ea8e93a3f97 100644 --- a/sequencer/src/genesis.rs +++ b/sequencer/src/genesis.rs @@ -6,9 +6,10 @@ use std::{ use alloy::primitives::Address; use anyhow::{Context, Ok}; use espresso_types::{ - v0_3::ChainConfig, FeeAccount, FeeAmount, GenesisHeader, L1BlockInfo, L1Client, Timestamp, - Upgrade, + v0_3::ChainConfig, FeeAccount, FeeAmount, GenesisHeader, L1BlockInfo, L1Client, SeqTypes, + Timestamp, Upgrade, }; +use hotshot_types::traits::node_implementation::NodeType; use serde::{Deserialize, Serialize}; use vbs::version::Version; @@ -44,6 +45,48 @@ pub enum L1Finalized { Timestamp { timestamp: Timestamp }, } +/// Helper type to deal with TOML keys that are u64 but represented as strings +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TomlKeyU64(u64); + +impl<'de> Deserialize<'de> for TomlKeyU64 { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + tracing::warn!("Using TomlKeyU64::deserialize"); + let s = String::deserialize(deserializer)?; + + let n = s + .parse::() + .map_err(|_| serde::de::Error::custom("invalid epoch"))?; + + std::result::Result::Ok(TomlKeyU64(n)) + } +} + +impl Serialize for TomlKeyU64 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.0.to_string()) + } +} + +impl Into for &TomlKeyU64 { + fn into(self) -> u64 { + self.0 + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct PeerConfigData { + pub stake_table_key: ::SignatureKey, + pub state_ver_key: ::StateSignatureKey, + pub stake: u64, +} + /// Genesis of an Espresso chain. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Genesis { @@ -67,6 +110,7 @@ pub struct Genesis { #[serde(rename = "upgrade", with = "upgrade_ser")] #[serde(default)] pub upgrades: BTreeMap, + pub da_committees: Option>>, } impl Genesis { diff --git a/sequencer/src/lib.rs b/sequencer/src/lib.rs index 48f11eb3396..71dbd103af4 100644 --- a/sequencer/src/lib.rs +++ b/sequencer/src/lib.rs @@ -57,6 +57,7 @@ use hotshot_types::{ epoch_membership::EpochMembershipCoordinator, light_client::{StateKeyPair, StateSignKey}, signature_key::{BLSPrivKey, BLSPubKey}, + stake_table::StakeTableEntry, traits::{ metrics::{Metrics, NoMetrics}, network::ConnectedNetwork, @@ -390,6 +391,27 @@ where network_config.config.epoch_start_block = epoch_start_block; network_config.config.stake_table_capacity = stake_table_capacity; + if let Some(da_committees) = &genesis.da_committees { + tracing::warn!("setting da_committees from genesis"); + network_config.config.da_committees = da_committees + .iter() + .map(|(k, v)| { + ( + k.into(), + v.iter() + .map(|pcd| hotshot_types::PeerConfig { + stake_table_entry: StakeTableEntry { + stake_key: pcd.stake_table_key, + stake_amount: U256::from(pcd.stake), + }, + state_ver_key: pcd.state_ver_key.clone(), + }) + .collect(), + ) + }) + .collect(); + } + // If the `Libp2p` bootstrap nodes were supplied via the command line, override those // present in the config file. if let Some(bootstrap_nodes) = network_params.libp2p_bootstrap_nodes { @@ -529,7 +551,7 @@ where // Create the HotShot membership let mut membership = EpochCommittees::new_stake( network_config.config.known_nodes_with_stake.clone(), - network_config.config.known_da_nodes.clone(), + network_config.config.build_da_committees(), block_reward, fetcher, epoch_height, diff --git a/types/src/v0/config.rs b/types/src/v0/config.rs index ef718cbb82d..bc6bc172d03 100644 --- a/types/src/v0/config.rs +++ b/types/src/v0/config.rs @@ -1,4 +1,4 @@ -use std::{num::NonZeroUsize, time::Duration}; +use std::{collections::BTreeMap, num::NonZeroUsize, time::Duration}; use hotshot_types::{ network::{ @@ -55,6 +55,7 @@ pub struct PublicHotShotConfig { num_nodes_with_stake: NonZeroUsize, known_nodes_with_stake: Vec>, known_da_nodes: Vec>, + da_committees: BTreeMap>>, da_staked_committee_size: usize, fixed_leader_for_gpuvid: usize, next_view_timeout: u64, @@ -105,6 +106,7 @@ impl From> for PublicHotShotConfig { num_nodes_with_stake, known_nodes_with_stake, known_da_nodes, + da_committees, da_staked_committee_size, fixed_leader_for_gpuvid, next_view_timeout, @@ -133,6 +135,7 @@ impl From> for PublicHotShotConfig { num_nodes_with_stake, known_nodes_with_stake, known_da_nodes, + da_committees, da_staked_committee_size, fixed_leader_for_gpuvid, next_view_timeout, @@ -165,6 +168,7 @@ impl PublicHotShotConfig { num_nodes_with_stake: self.num_nodes_with_stake, known_nodes_with_stake: self.known_nodes_with_stake, known_da_nodes: self.known_da_nodes, + da_committees: self.da_committees, da_staked_committee_size: self.da_staked_committee_size, fixed_leader_for_gpuvid: self.fixed_leader_for_gpuvid, next_view_timeout: self.next_view_timeout, @@ -196,9 +200,15 @@ impl PublicHotShotConfig { pub fn known_da_nodes(&self) -> Vec> { self.known_da_nodes.clone() } + + pub fn da_committees(&self) -> BTreeMap>> { + self.da_committees.clone() + } + pub fn blocks_per_epoch(&self) -> u64 { self.epoch_height } + pub fn epoch_start_block(&self) -> u64 { self.epoch_start_block } diff --git a/types/src/v0/impls/stake_table.rs b/types/src/v0/impls/stake_table.rs index 47a6a5bd55d..b71db957684 100644 --- a/types/src/v0/impls/stake_table.rs +++ b/types/src/v0/impls/stake_table.rs @@ -1689,11 +1689,18 @@ impl EpochCommittees { // TODO remove `new` from trait and rename this to `new`. // https://github.com/EspressoSystems/HotShot/commit/fcb7d54a4443e29d643b3bbc53761856aef4de8b committee_members: Vec>, - da_members: Vec>, + da_committees: BTreeMap>>, fixed_block_reward: Option, fetcher: Fetcher, epoch_height: u64, ) -> Self { + // TODO: Store da_committees in EpochCommittees. Currently we just pull da_members back out, but we + // will need to figure out how to integrate da_committees into EpochCommittee and the like. + let da_members = da_committees + .get(&Epoch::new(0)) + .cloned() + .expect("Should always have an epoch 0 DA committee"); + // For each member, get the stake table entry let stake_table: Vec<_> = committee_members .iter() diff --git a/types/src/v0/v0_3/mod.rs b/types/src/v0/v0_3/mod.rs index 79a59569cf3..6061138f31f 100644 --- a/types/src/v0/v0_3/mod.rs +++ b/types/src/v0/v0_3/mod.rs @@ -29,4 +29,4 @@ pub use header::*; pub use nsproof::*; pub use stake_table::*; pub use state::*; -pub use txproof::*; +pub use txproof::*; \ No newline at end of file From 0f33ed37f554a94468f73882b3db6ad321d2fee8 Mon Sep 17 00:00:00 2001 From: pls148 <184445976+pls148@users.noreply.github.com> Date: Wed, 1 Oct 2025 16:10:03 -0700 Subject: [PATCH 2/5] fixes --- crates/hotshot/types/src/hotshot_config_file.rs | 1 + hotshot-query-service/examples/simple-server.rs | 1 + sequencer/src/genesis.rs | 7 ++++--- sequencer/src/lib.rs | 3 ++- sequencer/src/message_compat_tests.rs | 2 +- sequencer/src/restart_tests.rs | 1 + sequencer/src/run.rs | 1 + types/src/v0/impls/instance_state.rs | 8 ++++---- 8 files changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/hotshot/types/src/hotshot_config_file.rs b/crates/hotshot/types/src/hotshot_config_file.rs index 54a8933766b..d989f7d4d50 100644 --- a/crates/hotshot/types/src/hotshot_config_file.rs +++ b/crates/hotshot/types/src/hotshot_config_file.rs @@ -45,6 +45,7 @@ pub struct HotShotConfigFile { #[serde(skip)] /// The known DA nodes' public key and stake values pub known_da_nodes: Vec>, + #[serde(skip)] /// The known DA nodes' public keys and stake values, by start epoch pub da_committees: BTreeMap>>, /// Number of staking DA nodes diff --git a/hotshot-query-service/examples/simple-server.rs b/hotshot-query-service/examples/simple-server.rs index 8d45406e0a6..3bdb1137ed8 100644 --- a/hotshot-query-service/examples/simple-server.rs +++ b/hotshot-query-service/examples/simple-server.rs @@ -201,6 +201,7 @@ async fn init_consensus( next_view_timeout: 10000, num_bootstrap: 0, known_da_nodes: known_nodes_with_stake.clone(), + da_committees: Default::default(), da_staked_committee_size: pub_keys.len(), data_request_delay: Duration::from_millis(200), view_sync_timeout: Duration::from_millis(250), diff --git a/sequencer/src/genesis.rs b/sequencer/src/genesis.rs index ea8e93a3f97..7d70854cff8 100644 --- a/sequencer/src/genesis.rs +++ b/sequencer/src/genesis.rs @@ -74,9 +74,9 @@ impl Serialize for TomlKeyU64 { } } -impl Into for &TomlKeyU64 { - fn into(self) -> u64 { - self.0 +impl From<&TomlKeyU64> for u64 { + fn from(val: &TomlKeyU64) -> Self { + val.0 } } @@ -110,6 +110,7 @@ pub struct Genesis { #[serde(rename = "upgrade", with = "upgrade_ser")] #[serde(default)] pub upgrades: BTreeMap, + #[serde(default)] pub da_committees: Option>>, } diff --git a/sequencer/src/lib.rs b/sequencer/src/lib.rs index 71dbd103af4..4bf916e9a01 100644 --- a/sequencer/src/lib.rs +++ b/sequencer/src/lib.rs @@ -1042,6 +1042,7 @@ pub mod testing { fixed_leader_for_gpuvid: 0, num_nodes_with_stake: num_nodes.try_into().unwrap(), known_da_nodes: known_nodes_with_stake.clone(), + da_committees: Default::default(), known_nodes_with_stake: known_nodes_with_stake.clone(), next_view_timeout: Duration::from_secs(5).as_millis() as u64, num_bootstrap: 1usize, @@ -1291,7 +1292,7 @@ pub mod testing { let block_reward = fetcher.fetch_fixed_block_reward().await.ok(); let mut membership = EpochCommittees::new_stake( config.known_nodes_with_stake.clone(), - config.known_da_nodes.clone(), + config.build_da_committees(), block_reward, fetcher, config.epoch_height, diff --git a/sequencer/src/message_compat_tests.rs b/sequencer/src/message_compat_tests.rs index 18488bb4b28..02f14cafa51 100755 --- a/sequencer/src/message_compat_tests.rs +++ b/sequencer/src/message_compat_tests.rs @@ -69,7 +69,7 @@ async fn test_message_compat(_ver: Ver) { let membership = EpochMembershipCoordinator::new( Arc::new(RwLock::new(EpochCommittees::new_stake( committee.clone(), - committee, + [(EpochNumber::new(0), committee)].into(), None, Fetcher::mock(), epoch_height, diff --git a/sequencer/src/restart_tests.rs b/sequencer/src/restart_tests.rs index 9427f884422..8a32a76a067 100755 --- a/sequencer/src/restart_tests.rs +++ b/sequencer/src/restart_tests.rs @@ -668,6 +668,7 @@ impl TestNetwork { .into_iter() .collect(), genesis_version: Version { major: 0, minor: 1 }, + da_committees: None, }; let node_params = (0..da_nodes + regular_nodes) diff --git a/sequencer/src/run.rs b/sequencer/src/run.rs index e4b51d34107..59ce4de280a 100644 --- a/sequencer/src/run.rs +++ b/sequencer/src/run.rs @@ -336,6 +336,7 @@ mod test { epoch_start_block: None, stake_table_capacity: None, genesis_version: Version { major: 0, minor: 1 }, + da_committees: None, }; genesis.to_file(&genesis_file).unwrap(); diff --git a/types/src/v0/impls/instance_state.rs b/types/src/v0/impls/instance_state.rs index b4ab6e45ac3..e6991fbfba0 100644 --- a/types/src/v0/impls/instance_state.rs +++ b/types/src/v0/impls/instance_state.rs @@ -170,7 +170,7 @@ impl NodeState { let membership = Arc::new(RwLock::new(EpochCommittees::new_stake( vec![], - vec![], + Default::default(), None, Fetcher::mock(), 0, @@ -202,7 +202,7 @@ impl NodeState { let membership = Arc::new(RwLock::new(EpochCommittees::new_stake( vec![], - vec![], + Default::default(), None, Fetcher::mock(), 0, @@ -232,7 +232,7 @@ impl NodeState { let membership = Arc::new(RwLock::new(EpochCommittees::new_stake( vec![], - vec![], + Default::default(), None, Fetcher::mock(), 0, @@ -327,7 +327,7 @@ impl Default for NodeState { let membership = Arc::new(RwLock::new(EpochCommittees::new_stake( vec![], - vec![], + Default::default(), None, Fetcher::mock(), 0, From 2ba49dd8ea6cfb59d71f21aa39028c09347d4d8e Mon Sep 17 00:00:00 2001 From: pls148 <184445976+pls148@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:32:17 -0700 Subject: [PATCH 3/5] fix test failures --- types/src/v0/config.rs | 1 + types/src/v0/impls/stake_table.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/types/src/v0/config.rs b/types/src/v0/config.rs index bc6bc172d03..16cbd240741 100644 --- a/types/src/v0/config.rs +++ b/types/src/v0/config.rs @@ -55,6 +55,7 @@ pub struct PublicHotShotConfig { num_nodes_with_stake: NonZeroUsize, known_nodes_with_stake: Vec>, known_da_nodes: Vec>, + #[serde(default)] da_committees: BTreeMap>>, da_staked_committee_size: usize, fixed_leader_for_gpuvid: usize, diff --git a/types/src/v0/impls/stake_table.rs b/types/src/v0/impls/stake_table.rs index b71db957684..038724e40da 100644 --- a/types/src/v0/impls/stake_table.rs +++ b/types/src/v0/impls/stake_table.rs @@ -1699,7 +1699,7 @@ impl EpochCommittees { let da_members = da_committees .get(&Epoch::new(0)) .cloned() - .expect("Should always have an epoch 0 DA committee"); + .unwrap_or_default(); // For testing reasons, we want to support being given an empty map // For each member, get the stake table entry let stake_table: Vec<_> = committee_members From ebd970cc7b8bc27ef88bf0e9835faa63bfca3019 Mon Sep 17 00:00:00 2001 From: pls148 <184445976+pls148@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:07:13 -0700 Subject: [PATCH 4/5] change da_committees to a map of version to map of epoch to committee --- .../hotshot/types/src/hotshot_config_file.rs | 3 +- crates/hotshot/types/src/lib.rs | 30 +++++-------------- sequencer/src/genesis.rs | 2 +- sequencer/src/lib.rs | 21 ++++++++----- sequencer/src/message_compat_tests.rs | 2 +- types/src/v0/config.rs | 7 ++--- types/src/v0/impls/stake_table.rs | 9 +----- 7 files changed, 29 insertions(+), 45 deletions(-) diff --git a/crates/hotshot/types/src/hotshot_config_file.rs b/crates/hotshot/types/src/hotshot_config_file.rs index d989f7d4d50..45e739d4f97 100644 --- a/crates/hotshot/types/src/hotshot_config_file.rs +++ b/crates/hotshot/types/src/hotshot_config_file.rs @@ -8,6 +8,7 @@ use std::{collections::BTreeMap, num::NonZeroUsize, time::Duration}; use alloy::primitives::U256; use url::Url; +use vbs::version::Version; use vec1::Vec1; use crate::{ @@ -47,7 +48,7 @@ pub struct HotShotConfigFile { pub known_da_nodes: Vec>, #[serde(skip)] /// The known DA nodes' public keys and stake values, by start epoch - pub da_committees: BTreeMap>>, + pub da_committees: BTreeMap>>>, /// Number of staking DA nodes pub staked_da_nodes: usize, /// Number of fixed leaders for GPU VID diff --git a/crates/hotshot/types/src/lib.rs b/crates/hotshot/types/src/lib.rs index b516ad19675..bab804dfda5 100644 --- a/crates/hotshot/types/src/lib.rs +++ b/crates/hotshot/types/src/lib.rs @@ -19,9 +19,10 @@ use traits::{ signature_key::{SignatureKey, StateSignatureKey}, }; use url::Url; +use vbs::version::Version; use vec1::Vec1; -use crate::{traits::node_implementation::ConsensusTime, utils::bincode_opts}; +use crate::utils::bincode_opts; pub mod bundle; pub mod consensus; pub mod constants; @@ -198,7 +199,7 @@ pub struct HotShotConfig { /// All public keys known to be DA nodes pub known_da_nodes: Vec>, /// All public keys known to be DA nodes, by start epoch - pub da_committees: BTreeMap>>, + pub da_committees: BTreeMap>>>, /// List of DA committee (staking)nodes for static DA committee pub da_staked_committee_size: usize, /// Number of fixed leaders for GPU VID, normally it will be 0, it's only used when running GPU VID @@ -271,25 +272,10 @@ impl HotShotConfig { self.known_nodes_with_stake.clone().into() } - pub fn build_da_committees(&self) -> BTreeMap>> { - if self.da_committees.is_empty() { - tracing::warn!( - "da_committees is not set, falling back to known_da_nodes, which is deprecated." - ); - - [(TYPES::Epoch::new(0), self.known_da_nodes.clone())].into() - } else { - if !self.known_da_nodes.is_empty() { - tracing::warn!( - "Both known_da_nodes and da_committees are set, known_da_nodes is deprecated \ - and will be ignored." - ); - } - - self.da_committees - .iter() - .map(|(k, v)| (TYPES::Epoch::new(*k), v.clone())) - .collect() - } + pub fn build_da_committees(&self) -> Vec> { + // TODO: THIS IS A TEMPORARY FIX WITH THE WRONG RETURN TYPE. + // It's done so that we can start using this function and have the existing behavior + // (use known_da_nodes) while we transition to using da_committees. + self.known_da_nodes.clone() } } diff --git a/sequencer/src/genesis.rs b/sequencer/src/genesis.rs index 7d70854cff8..2f8b31b89ce 100644 --- a/sequencer/src/genesis.rs +++ b/sequencer/src/genesis.rs @@ -111,7 +111,7 @@ pub struct Genesis { #[serde(default)] pub upgrades: BTreeMap, #[serde(default)] - pub da_committees: Option>>, + pub da_committees: Option>>>, } impl Genesis { diff --git a/sequencer/src/lib.rs b/sequencer/src/lib.rs index 4bf916e9a01..6a44c3f800b 100644 --- a/sequencer/src/lib.rs +++ b/sequencer/src/lib.rs @@ -397,14 +397,21 @@ where .iter() .map(|(k, v)| { ( - k.into(), + *k, v.iter() - .map(|pcd| hotshot_types::PeerConfig { - stake_table_entry: StakeTableEntry { - stake_key: pcd.stake_table_key, - stake_amount: U256::from(pcd.stake), - }, - state_ver_key: pcd.state_ver_key.clone(), + .map(|(k, v)| { + ( + k.into(), + v.iter() + .map(|pcd| hotshot_types::PeerConfig { + stake_table_entry: StakeTableEntry { + stake_key: pcd.stake_table_key, + stake_amount: U256::from(pcd.stake), + }, + state_ver_key: pcd.state_ver_key.clone(), + }) + .collect(), + ) }) .collect(), ) diff --git a/sequencer/src/message_compat_tests.rs b/sequencer/src/message_compat_tests.rs index 02f14cafa51..18488bb4b28 100755 --- a/sequencer/src/message_compat_tests.rs +++ b/sequencer/src/message_compat_tests.rs @@ -69,7 +69,7 @@ async fn test_message_compat(_ver: Ver) { let membership = EpochMembershipCoordinator::new( Arc::new(RwLock::new(EpochCommittees::new_stake( committee.clone(), - [(EpochNumber::new(0), committee)].into(), + committee, None, Fetcher::mock(), epoch_height, diff --git a/types/src/v0/config.rs b/types/src/v0/config.rs index 16cbd240741..bab24e400f2 100644 --- a/types/src/v0/config.rs +++ b/types/src/v0/config.rs @@ -8,6 +8,7 @@ use hotshot_types::{ }; use serde::{Deserialize, Serialize}; use tide_disco::Url; +use vbs::version::Version; use vec1::Vec1; use crate::{PubKey, SeqTypes}; @@ -56,7 +57,7 @@ pub struct PublicHotShotConfig { known_nodes_with_stake: Vec>, known_da_nodes: Vec>, #[serde(default)] - da_committees: BTreeMap>>, + da_committees: BTreeMap>>>, da_staked_committee_size: usize, fixed_leader_for_gpuvid: usize, next_view_timeout: u64, @@ -202,10 +203,6 @@ impl PublicHotShotConfig { self.known_da_nodes.clone() } - pub fn da_committees(&self) -> BTreeMap>> { - self.da_committees.clone() - } - pub fn blocks_per_epoch(&self) -> u64 { self.epoch_height } diff --git a/types/src/v0/impls/stake_table.rs b/types/src/v0/impls/stake_table.rs index 038724e40da..47a6a5bd55d 100644 --- a/types/src/v0/impls/stake_table.rs +++ b/types/src/v0/impls/stake_table.rs @@ -1689,18 +1689,11 @@ impl EpochCommittees { // TODO remove `new` from trait and rename this to `new`. // https://github.com/EspressoSystems/HotShot/commit/fcb7d54a4443e29d643b3bbc53761856aef4de8b committee_members: Vec>, - da_committees: BTreeMap>>, + da_members: Vec>, fixed_block_reward: Option, fetcher: Fetcher, epoch_height: u64, ) -> Self { - // TODO: Store da_committees in EpochCommittees. Currently we just pull da_members back out, but we - // will need to figure out how to integrate da_committees into EpochCommittee and the like. - let da_members = da_committees - .get(&Epoch::new(0)) - .cloned() - .unwrap_or_default(); // For testing reasons, we want to support being given an empty map - // For each member, get the stake table entry let stake_table: Vec<_> = committee_members .iter() From a09bb19c19f21d84dd20159d4f597037bf6173f8 Mon Sep 17 00:00:00 2001 From: pls148 <184445976+pls148@users.noreply.github.com> Date: Mon, 6 Oct 2025 15:02:18 -0700 Subject: [PATCH 5/5] switch to vec of versioneddacommittee --- .../hotshot/types/src/hotshot_config_file.rs | 7 ++- crates/hotshot/types/src/lib.rs | 14 ++++-- sequencer/src/genesis.rs | 45 ++++--------------- sequencer/src/lib.rs | 36 +++++++-------- types/src/v0/config.rs | 7 ++- 5 files changed, 40 insertions(+), 69 deletions(-) diff --git a/crates/hotshot/types/src/hotshot_config_file.rs b/crates/hotshot/types/src/hotshot_config_file.rs index 45e739d4f97..54852b917e0 100644 --- a/crates/hotshot/types/src/hotshot_config_file.rs +++ b/crates/hotshot/types/src/hotshot_config_file.rs @@ -4,16 +4,15 @@ // You should have received a copy of the MIT License // along with the HotShot repository. If not, see . -use std::{collections::BTreeMap, num::NonZeroUsize, time::Duration}; +use std::{num::NonZeroUsize, time::Duration}; use alloy::primitives::U256; use url::Url; -use vbs::version::Version; use vec1::Vec1; use crate::{ constants::REQUEST_DATA_DELAY, upgrade_config::UpgradeConfig, HotShotConfig, NodeType, - PeerConfig, ValidatorConfig, + PeerConfig, ValidatorConfig, VersionedDaCommittee, }; /// Default builder URL, used as placeholder @@ -48,7 +47,7 @@ pub struct HotShotConfigFile { pub known_da_nodes: Vec>, #[serde(skip)] /// The known DA nodes' public keys and stake values, by start epoch - pub da_committees: BTreeMap>>>, + pub da_committees: Vec>, /// Number of staking DA nodes pub staked_da_nodes: usize, /// Number of fixed leaders for GPU VID diff --git a/crates/hotshot/types/src/lib.rs b/crates/hotshot/types/src/lib.rs index bab804dfda5..7c8bcc76715 100644 --- a/crates/hotshot/types/src/lib.rs +++ b/crates/hotshot/types/src/lib.rs @@ -5,9 +5,7 @@ // along with the HotShot repository. If not, see . //! Types and Traits for the `HotShot` consensus module -use std::{ - collections::BTreeMap, fmt::Debug, future::Future, num::NonZeroUsize, pin::Pin, time::Duration, -}; +use std::{fmt::Debug, future::Future, num::NonZeroUsize, pin::Pin, time::Duration}; use alloy::primitives::U256; use bincode::Options; @@ -184,6 +182,14 @@ impl Debug for PeerConfig { } } +#[derive(Clone, derive_more::Debug, serde::Serialize, serde::Deserialize)] +#[serde(bound(deserialize = ""))] +pub struct VersionedDaCommittee { + pub start_version: Version, + pub start_epoch: u64, + pub committee: Vec>, +} + /// Holds configuration for a `HotShot` #[derive(Clone, derive_more::Debug, serde::Serialize, serde::Deserialize)] #[serde(bound(deserialize = ""))] @@ -199,7 +205,7 @@ pub struct HotShotConfig { /// All public keys known to be DA nodes pub known_da_nodes: Vec>, /// All public keys known to be DA nodes, by start epoch - pub da_committees: BTreeMap>>>, + pub da_committees: Vec>, /// List of DA committee (staking)nodes for static DA committee pub da_staked_committee_size: usize, /// Number of fixed leaders for GPU VID, normally it will be 0, it's only used when running GPU VID diff --git a/sequencer/src/genesis.rs b/sequencer/src/genesis.rs index 2f8b31b89ce..887e4e5d20a 100644 --- a/sequencer/src/genesis.rs +++ b/sequencer/src/genesis.rs @@ -45,41 +45,6 @@ pub enum L1Finalized { Timestamp { timestamp: Timestamp }, } -/// Helper type to deal with TOML keys that are u64 but represented as strings -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TomlKeyU64(u64); - -impl<'de> Deserialize<'de> for TomlKeyU64 { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - tracing::warn!("Using TomlKeyU64::deserialize"); - let s = String::deserialize(deserializer)?; - - let n = s - .parse::() - .map_err(|_| serde::de::Error::custom("invalid epoch"))?; - - std::result::Result::Ok(TomlKeyU64(n)) - } -} - -impl Serialize for TomlKeyU64 { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.0.to_string()) - } -} - -impl From<&TomlKeyU64> for u64 { - fn from(val: &TomlKeyU64) -> Self { - val.0 - } -} - #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PeerConfigData { pub stake_table_key: ::SignatureKey, @@ -87,6 +52,14 @@ pub struct PeerConfigData { pub stake: u64, } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct VersionedDaCommittee { + #[serde(with = "version_ser")] + pub start_version: Version, + pub start_epoch: u64, + pub committee: Vec, +} + /// Genesis of an Espresso chain. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Genesis { @@ -111,7 +84,7 @@ pub struct Genesis { #[serde(default)] pub upgrades: BTreeMap, #[serde(default)] - pub da_committees: Option>>>, + pub da_committees: Option>, } impl Genesis { diff --git a/sequencer/src/lib.rs b/sequencer/src/lib.rs index 6a44c3f800b..dbceb9ff51e 100644 --- a/sequencer/src/lib.rs +++ b/sequencer/src/lib.rs @@ -65,7 +65,7 @@ use hotshot_types::{ storage::Storage, }, utils::BuilderCommitment, - ValidatorConfig, + ValidatorConfig, VersionedDaCommittee, }; pub use options::Options; use serde::{Deserialize, Serialize}; @@ -395,26 +395,20 @@ where tracing::warn!("setting da_committees from genesis"); network_config.config.da_committees = da_committees .iter() - .map(|(k, v)| { - ( - *k, - v.iter() - .map(|(k, v)| { - ( - k.into(), - v.iter() - .map(|pcd| hotshot_types::PeerConfig { - stake_table_entry: StakeTableEntry { - stake_key: pcd.stake_table_key, - stake_amount: U256::from(pcd.stake), - }, - state_ver_key: pcd.state_ver_key.clone(), - }) - .collect(), - ) - }) - .collect(), - ) + .map(|src| VersionedDaCommittee:: { + start_version: src.start_version, + start_epoch: src.start_epoch, + committee: src + .committee + .iter() + .map(|pcd| hotshot_types::PeerConfig { + stake_table_entry: StakeTableEntry { + stake_key: pcd.stake_table_key, + stake_amount: U256::from(pcd.stake), + }, + state_ver_key: pcd.state_ver_key.clone(), + }) + .collect(), }) .collect(); } diff --git a/types/src/v0/config.rs b/types/src/v0/config.rs index bab24e400f2..2a47b6e9935 100644 --- a/types/src/v0/config.rs +++ b/types/src/v0/config.rs @@ -1,14 +1,13 @@ -use std::{collections::BTreeMap, num::NonZeroUsize, time::Duration}; +use std::{num::NonZeroUsize, time::Duration}; use hotshot_types::{ network::{ BuilderType, CombinedNetworkConfig, Libp2pConfig, NetworkConfig, RandomBuilderConfig, }, - HotShotConfig, PeerConfig, ValidatorConfig, + HotShotConfig, PeerConfig, ValidatorConfig, VersionedDaCommittee, }; use serde::{Deserialize, Serialize}; use tide_disco::Url; -use vbs::version::Version; use vec1::Vec1; use crate::{PubKey, SeqTypes}; @@ -57,7 +56,7 @@ pub struct PublicHotShotConfig { known_nodes_with_stake: Vec>, known_da_nodes: Vec>, #[serde(default)] - da_committees: BTreeMap>>>, + da_committees: Vec>, da_staked_committee_size: usize, fixed_leader_for_gpuvid: usize, next_view_timeout: u64,