From 3f3e10dc2fae9c7b28a7055ec642c6fca9cc4e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:39:44 -0300 Subject: [PATCH 1/4] Use fork digest in gossipsub topics Match the leanSpec topic format `/leanconsensus/{fork_digest}/{name}/ssz_snappy` (leanEthereum/leanSpec#622) instead of the custom `/leanconsensus/devnet0/...`. Introduce a `ForkDigest` newtype displayed as lowercase hex without `0x` prefix, plus `block_topic` / `aggregation_topic` / `attestation_subnet_topic` helpers. The digest is plumbed through `SwarmConfig` / `BuiltSwarm` / `P2PServer` so it becomes configurable once the spec defines real fork identification. For now every client agrees on the dummy `12345678` value, exposed as `ForkDigest::DUMMY`. Closes #292 --- CLAUDE.md | 3 +- bin/ethlambda/src/main.rs | 6 +- crates/net/p2p/src/gossipsub/handler.rs | 2 +- crates/net/p2p/src/gossipsub/messages.rs | 93 +++++++++++++++++++++--- crates/net/p2p/src/gossipsub/mod.rs | 4 +- crates/net/p2p/src/lib.rs | 24 +++--- 6 files changed, 106 insertions(+), 26 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 1bc3fa3c..c1c58d95 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -265,7 +265,8 @@ actual_slot = finalized_slot + 1 + relative_index ### Protocols - **Transport**: QUIC over UDP (TLS 1.3) - **Gossipsub**: Blocks + Attestations (snappy raw compression) - - Topic: `/leanconsensus/{network}/{block|attestation}/ssz_snappy` + - Topic: `/leanconsensus/{fork_digest}/{block|aggregation|attestation_N}/ssz_snappy` + - `fork_digest` is a 4-byte hex string (no `0x` prefix); currently the dummy `12345678` agreed across clients - Mesh size: 8 (6-12 bounds), heartbeat: 700ms - **Req/Resp**: Status, BlocksByRoot (snappy frame compression + varint length) diff --git a/bin/ethlambda/src/main.rs b/bin/ethlambda/src/main.rs index e89b6990..01e3e6e3 100644 --- a/bin/ethlambda/src/main.rs +++ b/bin/ethlambda/src/main.rs @@ -20,7 +20,7 @@ use std::{ use clap::Parser; use ethlambda_blockchain::key_manager::ValidatorKeyPair; use ethlambda_network_api::{InitBlockChain, InitP2P, ToBlockChainToP2PRef, ToP2PToBlockChainRef}; -use ethlambda_p2p::{Bootnode, P2P, SwarmConfig, build_swarm, parse_enrs}; +use ethlambda_p2p::{Bootnode, ForkDigest, P2P, SwarmConfig, build_swarm, parse_enrs}; use ethlambda_types::primitives::H256; use ethlambda_types::{ genesis::GenesisConfig, @@ -164,6 +164,10 @@ async fn main() -> eyre::Result<()> { attestation_committee_count: options.attestation_committee_count, is_aggregator: options.is_aggregator, aggregate_subnet_ids: options.aggregate_subnet_ids, + // TODO: derive from fork version + genesis validators root once the + // spec defines how (see leanEthereum/leanSpec#622). Until then all + // clients agree on this placeholder value. + fork_digest: ForkDigest::DUMMY, }) .expect("failed to build swarm"); diff --git a/crates/net/p2p/src/gossipsub/handler.rs b/crates/net/p2p/src/gossipsub/handler.rs index 4d418463..2f403d67 100644 --- a/crates/net/p2p/src/gossipsub/handler.rs +++ b/crates/net/p2p/src/gossipsub/handler.rs @@ -143,7 +143,7 @@ pub async fn publish_attestation(server: &mut P2PServer, attestation: SignedAtte .attestation_topics .get(&subnet_id) .cloned() - .unwrap_or_else(|| attestation_subnet_topic(subnet_id)); + .unwrap_or_else(|| attestation_subnet_topic(&server.fork_digest, subnet_id)); // Publish to the attestation subnet topic. // Aggregators are subscribed to the subnet, so gossipsub uses mesh (not fanout). diff --git a/crates/net/p2p/src/gossipsub/messages.rs b/crates/net/p2p/src/gossipsub/messages.rs index 8b10b31a..4ac06fc7 100644 --- a/crates/net/p2p/src/gossipsub/messages.rs +++ b/crates/net/p2p/src/gossipsub/messages.rs @@ -1,20 +1,93 @@ -/// Topic kind for block gossip +/// Gossipsub topic prefix identifying the Lean consensus network. +pub const TOPIC_PREFIX: &str = "leanconsensus"; +/// Encoding suffix shared by every gossipsub topic (SSZ + snappy). +pub const ENCODING_POSTFIX: &str = "ssz_snappy"; + +/// Topic name for block gossip. pub const BLOCK_TOPIC_KIND: &str = "block"; -/// Topic kind prefix for per-committee attestation subnets. +/// Topic name prefix for per-committee attestation subnets. /// -/// Full topic format: `/leanconsensus/{network}/attestation_{subnet_id}/ssz_snappy` +/// Full topic format: `/leanconsensus/{fork_digest}/attestation_{subnet_id}/ssz_snappy` pub const ATTESTATION_SUBNET_TOPIC_PREFIX: &str = "attestation"; -/// Topic kind for aggregated attestation gossip. +/// Topic name for aggregated attestation gossip. /// -/// Full topic format: `/leanconsensus/{network}/aggregation/ssz_snappy` +/// Full topic format: `/leanconsensus/{fork_digest}/aggregation/ssz_snappy` pub const AGGREGATION_TOPIC_KIND: &str = "aggregation"; -// TODO: make this configurable (e.g., via GenesisConfig or CLI) -pub const NETWORK_NAME: &str = "devnet0"; +/// Four-byte fork digest embedded in gossipsub topic strings. +/// +/// The spec currently mandates a dummy value ([`ForkDigest::DUMMY`]) until +/// fork identification is properly specified. Displayed as lowercase hex +/// without a `0x` prefix, matching the beacon chain convention. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ForkDigest(pub [u8; 4]); + +impl ForkDigest { + /// Placeholder fork digest used across Lean consensus clients. + /// + /// See . + pub const DUMMY: Self = Self([0x12, 0x34, 0x56, 0x78]); +} + +impl std::fmt::Display for ForkDigest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for byte in &self.0 { + write!(f, "{byte:02x}")?; + } + Ok(()) + } +} + +/// Build the block gossipsub topic for the given fork. +pub fn block_topic(fork_digest: &ForkDigest) -> libp2p::gossipsub::IdentTopic { + libp2p::gossipsub::IdentTopic::new(format!( + "/{TOPIC_PREFIX}/{fork_digest}/{BLOCK_TOPIC_KIND}/{ENCODING_POSTFIX}" + )) +} + +/// Build the aggregated-attestation gossipsub topic for the given fork. +pub fn aggregation_topic(fork_digest: &ForkDigest) -> libp2p::gossipsub::IdentTopic { + libp2p::gossipsub::IdentTopic::new(format!( + "/{TOPIC_PREFIX}/{fork_digest}/{AGGREGATION_TOPIC_KIND}/{ENCODING_POSTFIX}" + )) +} -/// Build an attestation subnet topic for the given subnet ID. -pub fn attestation_subnet_topic(subnet_id: u64) -> libp2p::gossipsub::IdentTopic { +/// Build an attestation subnet gossipsub topic for the given fork and subnet. +pub fn attestation_subnet_topic( + fork_digest: &ForkDigest, + subnet_id: u64, +) -> libp2p::gossipsub::IdentTopic { libp2p::gossipsub::IdentTopic::new(format!( - "/leanconsensus/{NETWORK_NAME}/{ATTESTATION_SUBNET_TOPIC_PREFIX}_{subnet_id}/ssz_snappy" + "/{TOPIC_PREFIX}/{fork_digest}/{ATTESTATION_SUBNET_TOPIC_PREFIX}_{subnet_id}/{ENCODING_POSTFIX}" )) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fork_digest_display_is_lowercase_hex_no_prefix() { + assert_eq!(ForkDigest::DUMMY.to_string(), "12345678"); + assert_eq!(ForkDigest([0x00, 0x00, 0x00, 0x00]).to_string(), "00000000"); + assert_eq!(ForkDigest([0xff, 0xff, 0xff, 0xff]).to_string(), "ffffffff"); + assert_eq!(ForkDigest([0xaa, 0xbb, 0xcc, 0xdd]).to_string(), "aabbccdd"); + } + + #[test] + fn topics_embed_fork_digest_per_spec() { + let fd = ForkDigest::DUMMY; + assert_eq!( + block_topic(&fd).to_string(), + "/leanconsensus/12345678/block/ssz_snappy" + ); + assert_eq!( + aggregation_topic(&fd).to_string(), + "/leanconsensus/12345678/aggregation/ssz_snappy" + ); + assert_eq!( + attestation_subnet_topic(&fd, 3).to_string(), + "/leanconsensus/12345678/attestation_3/ssz_snappy" + ); + } +} diff --git a/crates/net/p2p/src/gossipsub/mod.rs b/crates/net/p2p/src/gossipsub/mod.rs index 83c30558..8dac1bd7 100644 --- a/crates/net/p2p/src/gossipsub/mod.rs +++ b/crates/net/p2p/src/gossipsub/mod.rs @@ -6,6 +6,4 @@ pub use encoding::decompress_message; pub use handler::{ handle_gossipsub_message, publish_aggregated_attestation, publish_attestation, publish_block, }; -pub use messages::{ - AGGREGATION_TOPIC_KIND, BLOCK_TOPIC_KIND, NETWORK_NAME, attestation_subnet_topic, -}; +pub use messages::{ForkDigest, aggregation_topic, attestation_subnet_topic, block_topic}; diff --git a/crates/net/p2p/src/lib.rs b/crates/net/p2p/src/lib.rs index 134efe28..6d30aeef 100644 --- a/crates/net/p2p/src/lib.rs +++ b/crates/net/p2p/src/lib.rs @@ -36,8 +36,8 @@ use tracing::{info, trace, warn}; use crate::{ gossipsub::{ - AGGREGATION_TOPIC_KIND, BLOCK_TOPIC_KIND, NETWORK_NAME, attestation_subnet_topic, - publish_aggregated_attestation, publish_attestation, publish_block, + aggregation_topic, attestation_subnet_topic, block_topic, publish_aggregated_attestation, + publish_attestation, publish_block, }, req_resp::{ BLOCKS_BY_ROOT_PROTOCOL_V1, Codec, MAX_COMPRESSED_PAYLOAD_SIZE, Request, @@ -51,6 +51,7 @@ pub mod metrics; mod req_resp; pub(crate) mod swarm_adapter; +pub use gossipsub::ForkDigest; pub use metrics::populate_name_registry; // 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1280ms, 2560ms @@ -82,6 +83,8 @@ pub struct SwarmConfig { pub attestation_committee_count: u64, pub is_aggregator: bool, pub aggregate_subnet_ids: Option>, + /// Fork digest embedded in all gossipsub topic strings. + pub fork_digest: ForkDigest, } /// Result of building the swarm — contains all pieces needed to start the P2P actor. @@ -93,6 +96,7 @@ pub struct BuiltSwarm { pub(crate) aggregation_topic: libp2p::gossipsub::IdentTopic, pub(crate) bootnode_addrs: HashMap, pub(crate) is_aggregator: bool, + pub(crate) fork_digest: ForkDigest, } /// Build and configure the libp2p swarm, dial bootnodes, subscribe to topics. @@ -188,8 +192,7 @@ pub fn build_swarm( .expect("failed to bind gossipsub listening address"); // Subscribe to block topic (all nodes) - let block_topic_str = format!("/leanconsensus/{NETWORK_NAME}/{BLOCK_TOPIC_KIND}/ssz_snappy"); - let block_topic = libp2p::gossipsub::IdentTopic::new(block_topic_str); + let block_topic = block_topic(&config.fork_digest); swarm .behaviour_mut() .gossipsub @@ -197,9 +200,7 @@ pub fn build_swarm( .unwrap(); // Subscribe to aggregation topic (all validators) - let aggregation_topic_str = - format!("/leanconsensus/{NETWORK_NAME}/{AGGREGATION_TOPIC_KIND}/ssz_snappy"); - let aggregation_topic = libp2p::gossipsub::IdentTopic::new(aggregation_topic_str); + let aggregation_topic = aggregation_topic(&config.fork_digest); swarm .behaviour_mut() .gossipsub @@ -223,7 +224,7 @@ pub fn build_swarm( aggregate_subnets.insert(0); } for &subnet_id in &aggregate_subnets { - let topic = attestation_subnet_topic(subnet_id); + let topic = attestation_subnet_topic(&config.fork_digest, subnet_id); swarm.behaviour_mut().gossipsub.subscribe(&topic)?; info!(subnet_id, "Subscribed to attestation subnet"); } @@ -236,13 +237,13 @@ pub fn build_swarm( let subnet_id = vid % config.attestation_committee_count; attestation_topics .entry(subnet_id) - .or_insert_with(|| attestation_subnet_topic(subnet_id)); + .or_insert_with(|| attestation_subnet_topic(&config.fork_digest, subnet_id)); } if let Some(ref explicit_ids) = config.aggregate_subnet_ids { for &subnet_id in explicit_ids { attestation_topics .entry(subnet_id) - .or_insert_with(|| attestation_subnet_topic(subnet_id)); + .or_insert_with(|| attestation_subnet_topic(&config.fork_digest, subnet_id)); } } @@ -259,6 +260,7 @@ pub fn build_swarm( aggregation_topic, bootnode_addrs, is_aggregator: config.is_aggregator, + fork_digest: config.fork_digest, }) } @@ -287,6 +289,7 @@ impl P2P { pending_requests: HashMap::new(), request_id_map: HashMap::new(), bootnode_addrs: built.bootnode_addrs, + fork_digest: built.fork_digest, }; let handle = server.start(); spawn_listener(handle.context(), swarm_stream.map(WrappedSwarmEvent)); @@ -318,6 +321,7 @@ pub struct P2PServer { pub(crate) block_topic: libp2p::gossipsub::IdentTopic, pub(crate) aggregation_topic: libp2p::gossipsub::IdentTopic, pub(crate) is_aggregator: bool, + pub(crate) fork_digest: ForkDigest, pub(crate) connected_peers: HashSet, pub(crate) pending_requests: HashMap, From 9bac44224774e1c69b90f719b5be2b1c4cfb9b5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:52:15 -0300 Subject: [PATCH 2/4] Simplify: hardcode fork digest as a string constant Drop the `ForkDigest` newtype, `SwarmConfig` / `BuiltSwarm` / `P2PServer` plumbing, and `pub` re-export. The fork digest is the dummy `12345678` placeholder every Lean client currently agrees on; a `FORK_DIGEST` `&str` constant with a TODO captures that cleanly until the spec defines real fork identification, and leaves the topic builder functions taking only the pieces that actually vary (subnet id). --- bin/ethlambda/src/main.rs | 6 +- crates/net/p2p/src/gossipsub/handler.rs | 2 +- crates/net/p2p/src/gossipsub/messages.rs | 76 +++++++----------------- crates/net/p2p/src/gossipsub/mod.rs | 2 +- crates/net/p2p/src/lib.rs | 17 ++---- 5 files changed, 30 insertions(+), 73 deletions(-) diff --git a/bin/ethlambda/src/main.rs b/bin/ethlambda/src/main.rs index 01e3e6e3..e89b6990 100644 --- a/bin/ethlambda/src/main.rs +++ b/bin/ethlambda/src/main.rs @@ -20,7 +20,7 @@ use std::{ use clap::Parser; use ethlambda_blockchain::key_manager::ValidatorKeyPair; use ethlambda_network_api::{InitBlockChain, InitP2P, ToBlockChainToP2PRef, ToP2PToBlockChainRef}; -use ethlambda_p2p::{Bootnode, ForkDigest, P2P, SwarmConfig, build_swarm, parse_enrs}; +use ethlambda_p2p::{Bootnode, P2P, SwarmConfig, build_swarm, parse_enrs}; use ethlambda_types::primitives::H256; use ethlambda_types::{ genesis::GenesisConfig, @@ -164,10 +164,6 @@ async fn main() -> eyre::Result<()> { attestation_committee_count: options.attestation_committee_count, is_aggregator: options.is_aggregator, aggregate_subnet_ids: options.aggregate_subnet_ids, - // TODO: derive from fork version + genesis validators root once the - // spec defines how (see leanEthereum/leanSpec#622). Until then all - // clients agree on this placeholder value. - fork_digest: ForkDigest::DUMMY, }) .expect("failed to build swarm"); diff --git a/crates/net/p2p/src/gossipsub/handler.rs b/crates/net/p2p/src/gossipsub/handler.rs index 2f403d67..4d418463 100644 --- a/crates/net/p2p/src/gossipsub/handler.rs +++ b/crates/net/p2p/src/gossipsub/handler.rs @@ -143,7 +143,7 @@ pub async fn publish_attestation(server: &mut P2PServer, attestation: SignedAtte .attestation_topics .get(&subnet_id) .cloned() - .unwrap_or_else(|| attestation_subnet_topic(&server.fork_digest, subnet_id)); + .unwrap_or_else(|| attestation_subnet_topic(subnet_id)); // Publish to the attestation subnet topic. // Aggregators are subscribed to the subnet, so gossipsub uses mesh (not fanout). diff --git a/crates/net/p2p/src/gossipsub/messages.rs b/crates/net/p2p/src/gossipsub/messages.rs index 4ac06fc7..79f9da83 100644 --- a/crates/net/p2p/src/gossipsub/messages.rs +++ b/crates/net/p2p/src/gossipsub/messages.rs @@ -1,64 +1,41 @@ -/// Gossipsub topic prefix identifying the Lean consensus network. -pub const TOPIC_PREFIX: &str = "leanconsensus"; -/// Encoding suffix shared by every gossipsub topic (SSZ + snappy). -pub const ENCODING_POSTFIX: &str = "ssz_snappy"; +/// Fork digest embedded in every gossipsub topic string, as lowercase hex +/// without a `0x` prefix. +/// +/// The [leanSpec](https://github.com/leanEthereum/leanSpec/pull/622) +/// currently mandates a dummy value shared across all clients; this will +/// eventually be derived from the fork version and genesis validators root. +// TODO: derive dynamically once the spec defines fork identification. +pub const FORK_DIGEST: &str = "12345678"; /// Topic name for block gossip. pub const BLOCK_TOPIC_KIND: &str = "block"; /// Topic name prefix for per-committee attestation subnets. /// -/// Full topic format: `/leanconsensus/{fork_digest}/attestation_{subnet_id}/ssz_snappy` +/// Full topic format: `/leanconsensus/{FORK_DIGEST}/attestation_{subnet_id}/ssz_snappy` pub const ATTESTATION_SUBNET_TOPIC_PREFIX: &str = "attestation"; /// Topic name for aggregated attestation gossip. /// -/// Full topic format: `/leanconsensus/{fork_digest}/aggregation/ssz_snappy` +/// Full topic format: `/leanconsensus/{FORK_DIGEST}/aggregation/ssz_snappy` pub const AGGREGATION_TOPIC_KIND: &str = "aggregation"; -/// Four-byte fork digest embedded in gossipsub topic strings. -/// -/// The spec currently mandates a dummy value ([`ForkDigest::DUMMY`]) until -/// fork identification is properly specified. Displayed as lowercase hex -/// without a `0x` prefix, matching the beacon chain convention. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ForkDigest(pub [u8; 4]); - -impl ForkDigest { - /// Placeholder fork digest used across Lean consensus clients. - /// - /// See . - pub const DUMMY: Self = Self([0x12, 0x34, 0x56, 0x78]); -} - -impl std::fmt::Display for ForkDigest { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for byte in &self.0 { - write!(f, "{byte:02x}")?; - } - Ok(()) - } -} - -/// Build the block gossipsub topic for the given fork. -pub fn block_topic(fork_digest: &ForkDigest) -> libp2p::gossipsub::IdentTopic { +/// Build the block gossipsub topic. +pub fn block_topic() -> libp2p::gossipsub::IdentTopic { libp2p::gossipsub::IdentTopic::new(format!( - "/{TOPIC_PREFIX}/{fork_digest}/{BLOCK_TOPIC_KIND}/{ENCODING_POSTFIX}" + "/leanconsensus/{FORK_DIGEST}/{BLOCK_TOPIC_KIND}/ssz_snappy" )) } -/// Build the aggregated-attestation gossipsub topic for the given fork. -pub fn aggregation_topic(fork_digest: &ForkDigest) -> libp2p::gossipsub::IdentTopic { +/// Build the aggregated-attestation gossipsub topic. +pub fn aggregation_topic() -> libp2p::gossipsub::IdentTopic { libp2p::gossipsub::IdentTopic::new(format!( - "/{TOPIC_PREFIX}/{fork_digest}/{AGGREGATION_TOPIC_KIND}/{ENCODING_POSTFIX}" + "/leanconsensus/{FORK_DIGEST}/{AGGREGATION_TOPIC_KIND}/ssz_snappy" )) } -/// Build an attestation subnet gossipsub topic for the given fork and subnet. -pub fn attestation_subnet_topic( - fork_digest: &ForkDigest, - subnet_id: u64, -) -> libp2p::gossipsub::IdentTopic { +/// Build an attestation subnet gossipsub topic for the given subnet. +pub fn attestation_subnet_topic(subnet_id: u64) -> libp2p::gossipsub::IdentTopic { libp2p::gossipsub::IdentTopic::new(format!( - "/{TOPIC_PREFIX}/{fork_digest}/{ATTESTATION_SUBNET_TOPIC_PREFIX}_{subnet_id}/{ENCODING_POSTFIX}" + "/leanconsensus/{FORK_DIGEST}/{ATTESTATION_SUBNET_TOPIC_PREFIX}_{subnet_id}/ssz_snappy" )) } @@ -66,27 +43,18 @@ pub fn attestation_subnet_topic( mod tests { use super::*; - #[test] - fn fork_digest_display_is_lowercase_hex_no_prefix() { - assert_eq!(ForkDigest::DUMMY.to_string(), "12345678"); - assert_eq!(ForkDigest([0x00, 0x00, 0x00, 0x00]).to_string(), "00000000"); - assert_eq!(ForkDigest([0xff, 0xff, 0xff, 0xff]).to_string(), "ffffffff"); - assert_eq!(ForkDigest([0xaa, 0xbb, 0xcc, 0xdd]).to_string(), "aabbccdd"); - } - #[test] fn topics_embed_fork_digest_per_spec() { - let fd = ForkDigest::DUMMY; assert_eq!( - block_topic(&fd).to_string(), + block_topic().to_string(), "/leanconsensus/12345678/block/ssz_snappy" ); assert_eq!( - aggregation_topic(&fd).to_string(), + aggregation_topic().to_string(), "/leanconsensus/12345678/aggregation/ssz_snappy" ); assert_eq!( - attestation_subnet_topic(&fd, 3).to_string(), + attestation_subnet_topic(3).to_string(), "/leanconsensus/12345678/attestation_3/ssz_snappy" ); } diff --git a/crates/net/p2p/src/gossipsub/mod.rs b/crates/net/p2p/src/gossipsub/mod.rs index 8dac1bd7..b50ea4fd 100644 --- a/crates/net/p2p/src/gossipsub/mod.rs +++ b/crates/net/p2p/src/gossipsub/mod.rs @@ -6,4 +6,4 @@ pub use encoding::decompress_message; pub use handler::{ handle_gossipsub_message, publish_aggregated_attestation, publish_attestation, publish_block, }; -pub use messages::{ForkDigest, aggregation_topic, attestation_subnet_topic, block_topic}; +pub use messages::{aggregation_topic, attestation_subnet_topic, block_topic}; diff --git a/crates/net/p2p/src/lib.rs b/crates/net/p2p/src/lib.rs index 6d30aeef..930a3c9c 100644 --- a/crates/net/p2p/src/lib.rs +++ b/crates/net/p2p/src/lib.rs @@ -51,7 +51,6 @@ pub mod metrics; mod req_resp; pub(crate) mod swarm_adapter; -pub use gossipsub::ForkDigest; pub use metrics::populate_name_registry; // 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1280ms, 2560ms @@ -83,8 +82,6 @@ pub struct SwarmConfig { pub attestation_committee_count: u64, pub is_aggregator: bool, pub aggregate_subnet_ids: Option>, - /// Fork digest embedded in all gossipsub topic strings. - pub fork_digest: ForkDigest, } /// Result of building the swarm — contains all pieces needed to start the P2P actor. @@ -96,7 +93,6 @@ pub struct BuiltSwarm { pub(crate) aggregation_topic: libp2p::gossipsub::IdentTopic, pub(crate) bootnode_addrs: HashMap, pub(crate) is_aggregator: bool, - pub(crate) fork_digest: ForkDigest, } /// Build and configure the libp2p swarm, dial bootnodes, subscribe to topics. @@ -192,7 +188,7 @@ pub fn build_swarm( .expect("failed to bind gossipsub listening address"); // Subscribe to block topic (all nodes) - let block_topic = block_topic(&config.fork_digest); + let block_topic = block_topic(); swarm .behaviour_mut() .gossipsub @@ -200,7 +196,7 @@ pub fn build_swarm( .unwrap(); // Subscribe to aggregation topic (all validators) - let aggregation_topic = aggregation_topic(&config.fork_digest); + let aggregation_topic = aggregation_topic(); swarm .behaviour_mut() .gossipsub @@ -224,7 +220,7 @@ pub fn build_swarm( aggregate_subnets.insert(0); } for &subnet_id in &aggregate_subnets { - let topic = attestation_subnet_topic(&config.fork_digest, subnet_id); + let topic = attestation_subnet_topic(subnet_id); swarm.behaviour_mut().gossipsub.subscribe(&topic)?; info!(subnet_id, "Subscribed to attestation subnet"); } @@ -237,13 +233,13 @@ pub fn build_swarm( let subnet_id = vid % config.attestation_committee_count; attestation_topics .entry(subnet_id) - .or_insert_with(|| attestation_subnet_topic(&config.fork_digest, subnet_id)); + .or_insert_with(|| attestation_subnet_topic(subnet_id)); } if let Some(ref explicit_ids) = config.aggregate_subnet_ids { for &subnet_id in explicit_ids { attestation_topics .entry(subnet_id) - .or_insert_with(|| attestation_subnet_topic(&config.fork_digest, subnet_id)); + .or_insert_with(|| attestation_subnet_topic(subnet_id)); } } @@ -260,7 +256,6 @@ pub fn build_swarm( aggregation_topic, bootnode_addrs, is_aggregator: config.is_aggregator, - fork_digest: config.fork_digest, }) } @@ -289,7 +284,6 @@ impl P2P { pending_requests: HashMap::new(), request_id_map: HashMap::new(), bootnode_addrs: built.bootnode_addrs, - fork_digest: built.fork_digest, }; let handle = server.start(); spawn_listener(handle.context(), swarm_stream.map(WrappedSwarmEvent)); @@ -321,7 +315,6 @@ pub struct P2PServer { pub(crate) block_topic: libp2p::gossipsub::IdentTopic, pub(crate) aggregation_topic: libp2p::gossipsub::IdentTopic, pub(crate) is_aggregator: bool, - pub(crate) fork_digest: ForkDigest, pub(crate) connected_peers: HashSet, pub(crate) pending_requests: HashMap, From fbd2d3883bb27903d8d3b7eca42ab48c7c070d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:56:38 -0300 Subject: [PATCH 3/4] Drop the gossipsub topic builder tests --- crates/net/p2p/src/gossipsub/messages.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/crates/net/p2p/src/gossipsub/messages.rs b/crates/net/p2p/src/gossipsub/messages.rs index 79f9da83..4b7a3acd 100644 --- a/crates/net/p2p/src/gossipsub/messages.rs +++ b/crates/net/p2p/src/gossipsub/messages.rs @@ -38,24 +38,3 @@ pub fn attestation_subnet_topic(subnet_id: u64) -> libp2p::gossipsub::IdentTopic "/leanconsensus/{FORK_DIGEST}/{ATTESTATION_SUBNET_TOPIC_PREFIX}_{subnet_id}/ssz_snappy" )) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn topics_embed_fork_digest_per_spec() { - assert_eq!( - block_topic().to_string(), - "/leanconsensus/12345678/block/ssz_snappy" - ); - assert_eq!( - aggregation_topic().to_string(), - "/leanconsensus/12345678/aggregation/ssz_snappy" - ); - assert_eq!( - attestation_subnet_topic(3).to_string(), - "/leanconsensus/12345678/attestation_3/ssz_snappy" - ); - } -} From 4e204775833df1c6828a66793c00385eed3fe8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:57:41 -0300 Subject: [PATCH 4/4] Restore 'topic kind' wording on topic-name constants --- crates/net/p2p/src/gossipsub/messages.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/net/p2p/src/gossipsub/messages.rs b/crates/net/p2p/src/gossipsub/messages.rs index 4b7a3acd..4a47f201 100644 --- a/crates/net/p2p/src/gossipsub/messages.rs +++ b/crates/net/p2p/src/gossipsub/messages.rs @@ -7,13 +7,13 @@ // TODO: derive dynamically once the spec defines fork identification. pub const FORK_DIGEST: &str = "12345678"; -/// Topic name for block gossip. +/// Topic kind for block gossip pub const BLOCK_TOPIC_KIND: &str = "block"; -/// Topic name prefix for per-committee attestation subnets. +/// Topic kind prefix for per-committee attestation subnets. /// /// Full topic format: `/leanconsensus/{FORK_DIGEST}/attestation_{subnet_id}/ssz_snappy` pub const ATTESTATION_SUBNET_TOPIC_PREFIX: &str = "attestation"; -/// Topic name for aggregated attestation gossip. +/// Topic kind for aggregated attestation gossip. /// /// Full topic format: `/leanconsensus/{FORK_DIGEST}/aggregation/ssz_snappy` pub const AGGREGATION_TOPIC_KIND: &str = "aggregation";