From 341d9a6dbeaf498b89ff66f10fc3a8262d0a1384 Mon Sep 17 00:00:00 2001 From: Chase Wright Date: Thu, 8 Oct 2026 09:09:23 -0500 Subject: [PATCH] fix(p2p): send the client version as the identify agent version --- bin/ethlambda/src/main.rs | 1 + crates/net/p2p/src/beacon/swarm.rs | 1 + crates/net/p2p/src/lib.rs | 66 +++++++++++++++++++++++++ crates/net/p2p/src/req_resp/handlers.rs | 1 + 4 files changed, 69 insertions(+) diff --git a/bin/ethlambda/src/main.rs b/bin/ethlambda/src/main.rs index 01774b51..5d6f07db 100644 --- a/bin/ethlambda/src/main.rs +++ b/bin/ethlambda/src/main.rs @@ -697,6 +697,7 @@ async fn run_node(options: Options) -> eyre::Result<()> { // the loop has stopped asking for. target_peers: common.discovery.target_peers, wire: setup.wire, + agent_version: version::CLIENT_VERSION, }) .wrap_err("failed to build swarm")?; diff --git a/crates/net/p2p/src/beacon/swarm.rs b/crates/net/p2p/src/beacon/swarm.rs index 9c264ef0..0acb9cfd 100644 --- a/crates/net/p2p/src/beacon/swarm.rs +++ b/crates/net/p2p/src/beacon/swarm.rs @@ -274,6 +274,7 @@ mod tests { listening_socket: "127.0.0.1:0".parse().expect("valid socket"), bootnodes: mainnet_bootnodes, target_peers: crate::discovery::DEFAULT_DISCOVERY_TARGET_PEERS, + agent_version: "ethlambda/test", wire: crate::WireConfig::Beacon(Box::new(BeaconWireConfig { fork_digest: [0x8c, 0x9f, 0x62, 0xfe], fork: ForkName::Fulu, diff --git a/crates/net/p2p/src/lib.rs b/crates/net/p2p/src/lib.rs index 1a0a7c84..c04091b6 100644 --- a/crates/net/p2p/src/lib.rs +++ b/crates/net/p2p/src/lib.rs @@ -584,6 +584,9 @@ pub struct SwarmConfig { /// the connection limits, and so decides the [`Wire`] the built swarm /// carries. pub wire: WireConfig, + /// The identify `agentVersion`, which crawlers read to name a peer's + /// client. Left unset, rust-libp2p reports its own crate version instead. + pub agent_version: &'static str, } /// The half of [`SwarmConfig`] the two networks disagree about. @@ -788,6 +791,7 @@ pub fn build_swarm(config: SwarmConfig) -> Result { listening_socket, target_peers, wire, + agent_version, } = config; // The codec comes out of this match too, not from `Default`: the two beacon @@ -829,6 +833,7 @@ pub fn build_swarm(config: SwarmConfig) -> Result { // peer reports into the address book, loopback included, and `req_resp` dials those. let identify = libp2p::identify::Behaviour::new( libp2p::identify::Config::new(identify_version.to_owned(), identity.public()) + .with_agent_version(agent_version.to_owned()) .with_cache_size(0), ); @@ -2446,6 +2451,7 @@ pub(crate) mod test_support { bootnodes: Vec::new(), listening_socket: "127.0.0.1:0".parse().expect("valid socket"), target_peers: crate::discovery::DEFAULT_DISCOVERY_TARGET_PEERS, + agent_version: "ethlambda/test", wire: WireConfig::Beacon(Box::new(BeaconWireConfig { fork_digest: [0u8; 4], fork: config.fork_at_epoch(0), @@ -2718,6 +2724,7 @@ mod tests { bootnodes: Vec::new(), listening_socket: "127.0.0.1:0".parse().expect("valid socket"), target_peers: crate::discovery::DEFAULT_DISCOVERY_TARGET_PEERS, + agent_version: "ethlambda/test", wire: WireConfig::Lean(LeanWireConfig { validator_ids: Vec::new(), attestation_committee_count: 1, @@ -2773,6 +2780,62 @@ mod tests { .expect("both swarms must connect over TCP within the timeout"); } + /// The identify reply carries `SwarmConfig::agent_version`, not + /// rust-libp2p's default `rust-libp2p/`. + #[tokio::test] + async fn identify_reports_the_configured_agent_version() { + fn build(node_key_byte: u8) -> BuiltSwarm { + build_swarm(SwarmConfig { + node_key: vec![node_key_byte; 32], + bootnodes: Vec::new(), + listening_socket: "127.0.0.1:0".parse().expect("valid socket"), + target_peers: crate::discovery::DEFAULT_DISCOVERY_TARGET_PEERS, + agent_version: "ethlambda/test", + wire: WireConfig::Lean(LeanWireConfig { + validator_ids: Vec::new(), + attestation_committee_count: 1, + subscription_subnets: HashSet::new(), + milliseconds_per_slot: DEFAULT_MILLISECONDS_PER_SLOT, + }), + }) + .expect("swarm builds") + } + + let mut dialer = build(1); + let mut listener = build(2); + + let listener_addr = loop { + if let SwarmEvent::NewListenAddr { address, .. } = + listener.swarm.select_next_some().await + { + break address + .with_p2p(listener.local_peer_id) + .expect("failed to add peer ID to multiaddr"); + } + }; + dialer.swarm.dial(listener_addr).expect("dial is accepted"); + + let received = async { + loop { + tokio::select! { + event = dialer.swarm.select_next_some() => { + if let SwarmEvent::Behaviour(BehaviourEvent::Identify( + libp2p::identify::Event::Received { info, .. }, + )) = event + { + return info.agent_version; + } + } + _ = listener.swarm.select_next_some() => {} + } + } + }; + let agent_version = tokio::time::timeout(Duration::from_secs(10), received) + .await + .expect("identify must complete within the timeout"); + assert_eq!(agent_version, "ethlambda/test"); + } + #[test] fn gossip_is_held_for_a_verdict_only_when_asked() { let ttl = Duration::from_secs(1); @@ -2837,6 +2900,7 @@ mod tests { bootnodes: Vec::new(), listening_socket: "127.0.0.1:0".parse().expect("valid socket"), target_peers: crate::discovery::DEFAULT_DISCOVERY_TARGET_PEERS, + agent_version: "ethlambda/test", wire: WireConfig::Beacon(Box::new(beacon::swarm::BeaconWireConfig { fork_digest: [0x11, 0x22, 0x33, 0x44], fork: ForkName::Fulu, @@ -3125,6 +3189,7 @@ mod tests { bootnodes, listening_socket: "127.0.0.1:0".parse().expect("valid socket"), target_peers: crate::discovery::DEFAULT_DISCOVERY_TARGET_PEERS, + agent_version: "ethlambda/test", wire: WireConfig::Lean(LeanWireConfig { validator_ids: Vec::new(), attestation_committee_count: 1, @@ -3574,6 +3639,7 @@ mod tests { bootnodes: Vec::new(), listening_socket: "127.0.0.1:0".parse().expect("valid socket"), target_peers: crate::discovery::DEFAULT_DISCOVERY_TARGET_PEERS, + agent_version: "ethlambda/test", wire: WireConfig::Beacon(Box::new(beacon::swarm::BeaconWireConfig { fork_digest: [0x11, 0x22, 0x33, 0x44], fork: ForkName::Fulu, diff --git a/crates/net/p2p/src/req_resp/handlers.rs b/crates/net/p2p/src/req_resp/handlers.rs index d358c16a..13e986fd 100644 --- a/crates/net/p2p/src/req_resp/handlers.rs +++ b/crates/net/p2p/src/req_resp/handlers.rs @@ -2456,6 +2456,7 @@ mod tests { bootnodes: Vec::new(), listening_socket: "127.0.0.1:0".parse().expect("valid socket"), target_peers: crate::discovery::DEFAULT_DISCOVERY_TARGET_PEERS, + agent_version: "ethlambda/test", wire: crate::WireConfig::Lean(crate::LeanWireConfig { validator_ids: Vec::new(), attestation_committee_count: 1,