diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index 6da99f4bf8..d4b54f516a 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -19,7 +19,7 @@ use std::marker::PhantomData; use std::sync::Arc; use types::{EnrForkId, EthSpec, SubnetId}; -const MAX_IDENTIFY_ADDRESSES: usize = 20; +const MAX_IDENTIFY_ADDRESSES: usize = 10; /// Builds the network behaviour that manages the core protocols of eth2. /// This core behaviour is managed by `Behaviour` which adds peer management to all core @@ -508,10 +508,13 @@ impl NetworkBehaviourEventPr if info.listen_addrs.len() > MAX_IDENTIFY_ADDRESSES { debug!( self.log, - "More than 20 addresses have been identified, truncating" + "More than 10 addresses have been identified, truncating" ); info.listen_addrs.truncate(MAX_IDENTIFY_ADDRESSES); } + // send peer info to the peer manager. + self.peer_manager.identify(&peer_id, &info); + debug!(self.log, "Identified Peer"; "peer" => format!("{}", peer_id), "protocol_version" => info.protocol_version, "agent_version" => info.agent_version, diff --git a/beacon_node/eth2-libp2p/src/peer_manager/client.rs b/beacon_node/eth2-libp2p/src/peer_manager/client.rs new file mode 100644 index 0000000000..3eea0707f0 --- /dev/null +++ b/beacon_node/eth2-libp2p/src/peer_manager/client.rs @@ -0,0 +1,133 @@ +//! Known Ethereum 2.0 clients and their fingerprints. +//! +//! Currently using identify to fingerprint. + +use libp2p::identify::IdentifyInfo; + +#[derive(Debug)] +/// Various client and protocol information related to a node. +pub struct Client { + /// The client's name (Ex: lighthouse, prism, nimbus, etc) + pub kind: ClientKind, + /// The client's version. + pub version: String, + /// The OS version of the client. + pub os_version: String, + /// The libp2p protocol version. + pub protocol_version: String, + /// Identify agent string + pub agent_string: Option, +} + +#[derive(Debug)] +pub enum ClientKind { + /// A lighthouse node (the best kind). + Lighthouse, + /// A Nimbus node. + Nimbus, + /// A Teku node. + Teku, + /// A Prysm node. + Prysm, + /// An unknown client. + Unknown, +} + +impl Default for Client { + fn default() -> Self { + Client { + kind: ClientKind::Unknown, + version: "unknown".into(), + os_version: "unknown".into(), + protocol_version: "unknown".into(), + agent_string: None, + } + } +} + +impl Client { + /// Builds a `Client` from `IdentifyInfo`. + pub fn from_identify_info(info: &IdentifyInfo) -> Self { + let (kind, version, os_version) = client_from_agent_version(&info.agent_version); + + Client { + kind, + version, + os_version, + protocol_version: info.protocol_version.clone(), + agent_string: Some(info.agent_version.clone()), + } + } +} + +impl std::fmt::Display for Client { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.kind { + ClientKind::Lighthouse => write!( + f, + "Lighthouse: version: {}, os_version: {}", + self.version, self.os_version + ), + ClientKind::Teku => write!( + f, + "Teku: version: {}, os_version: {}", + self.version, self.os_version + ), + ClientKind::Nimbus => write!( + f, + "Nimbus: version: {}, os_version: {}", + self.version, self.os_version + ), + ClientKind::Prysm => write!( + f, + "Prysm: version: {}, os_version: {}", + self.version, self.os_version + ), + ClientKind::Unknown => { + if let Some(agent_string) = &self.agent_string { + write!(f, "Unknown: {}", agent_string) + } else { + write!(f, "Unknown") + } + } + } + } +} + +// helper function to identify clients from their agent_version. Returns the client +// kind and it's associated version and the OS kind. +fn client_from_agent_version(agent_version: &str) -> (ClientKind, String, String) { + let mut agent_split = agent_version.split("/"); + match agent_split.next() { + Some("Lighthouse") => { + let kind = ClientKind::Lighthouse; + let mut version = String::from("unknown"); + let mut os_version = version.clone(); + if let Some(agent_version) = agent_split.next() { + version = agent_version.into(); + if let Some(agent_os_version) = agent_split.next() { + os_version = agent_os_version.into(); + } + } + (kind, version, os_version) + } + Some("teku") => { + let kind = ClientKind::Teku; + let mut version = String::from("unknown"); + let mut os_version = version.clone(); + if let Some(_) = agent_split.next() { + if let Some(agent_version) = agent_split.next() { + version = agent_version.into(); + if let Some(agent_os_version) = agent_split.next() { + os_version = agent_os_version.into(); + } + } + } + (kind, version, os_version) + } + _ => { + let unknown = String::from("unknown"); + (ClientKind::Unknown, unknown.clone(), unknown) + } + } +} diff --git a/beacon_node/eth2-libp2p/src/peer_manager/mod.rs b/beacon_node/eth2-libp2p/src/peer_manager/mod.rs index af844d3cb0..43ff1314b5 100644 --- a/beacon_node/eth2-libp2p/src/peer_manager/mod.rs +++ b/beacon_node/eth2-libp2p/src/peer_manager/mod.rs @@ -7,12 +7,14 @@ use crate::{NetworkGlobals, PeerId}; use futures::prelude::*; use futures::Stream; use hashmap_delay::HashSetDelay; +use libp2p::identify::IdentifyInfo; use slog::{crit, debug, error, warn}; use smallvec::SmallVec; use std::sync::Arc; use std::time::{Duration, Instant}; use types::EthSpec; +mod client; mod peer_info; mod peerdb; @@ -242,6 +244,16 @@ impl PeerManager { self.update_reputations(); } + /// Updates `PeerInfo` with `identify` information. + pub fn identify(&mut self, peer_id: &PeerId, info: &IdentifyInfo) { + if let Some(peer_info) = self.network_globals.peers.write().peer_info_mut(peer_id) { + peer_info.client = client::Client::from_identify_info(info); + peer_info.listening_addresses = info.listen_addrs.clone(); + } else { + crit!(self.log, "Received an Identify response from an unknown peer"; "peer_id" => format!("{}", peer_id)); + } + } + /* Internal functions */ /// Registers a peer as connected. The `ingoing` parameter determines if the peer is being diff --git a/beacon_node/eth2-libp2p/src/peer_manager/peer_info.rs b/beacon_node/eth2-libp2p/src/peer_manager/peer_info.rs index 584520ba0a..b9e817f8c3 100644 --- a/beacon_node/eth2-libp2p/src/peer_manager/peer_info.rs +++ b/beacon_node/eth2-libp2p/src/peer_manager/peer_info.rs @@ -1,5 +1,7 @@ +use super::client::Client; use super::peerdb::{Rep, DEFAULT_REPUTATION}; use crate::rpc::MetaData; +use crate::Multiaddr; use std::time::Instant; use types::{EthSpec, Slot, SubnetId}; use PeerConnectionStatus::*; @@ -12,9 +14,11 @@ pub struct PeerInfo { /// The peers reputation pub reputation: Rep, /// Client managing this peer - _client: Client, + pub client: Client, /// Connection status of this peer pub connection_status: PeerConnectionStatus, + /// The known listening addresses of this peer. + pub listening_addresses: Vec, /// The current syncing state of the peer. The state may be determined after it's initial /// connection. pub sync_status: PeerSyncStatus, @@ -26,13 +30,11 @@ pub struct PeerInfo { impl Default for PeerInfo { fn default() -> PeerInfo { PeerInfo { - reputation: DEFAULT_REPUTATION, _status: Default::default(), - _client: Client { - _client_name: "Unknown".into(), - _version: vec![0], - }, + reputation: DEFAULT_REPUTATION, + client: Client::default(), connection_status: Default::default(), + listening_addresses: vec![], sync_status: PeerSyncStatus::Unknown, meta_data: None, } @@ -66,15 +68,6 @@ impl Default for PeerStatus { } } -/// Representation of the client managing a peer -#[derive(Debug)] -pub struct Client { - /// The client's name (Ex: lighthouse, prism, nimbus, etc) - _client_name: String, - /// The client's version - _version: Vec, -} - /// Connection Status of the peer #[derive(Debug, Clone)] pub enum PeerConnectionStatus {