diff --git a/crates/net/network-api/src/lib.rs b/crates/net/network-api/src/lib.rs index e9cce0866fde..8efaec5f0fb7 100644 --- a/crates/net/network-api/src/lib.rs +++ b/crates/net/network-api/src/lib.rs @@ -195,6 +195,10 @@ pub struct PeerInfo { pub remote_id: PeerId, /// The client's name and version pub client_version: Arc, + /// The peer's enode + pub enode: String, + /// The peer's enr + pub enr: Option, /// The peer's address we're connected to pub remote_addr: SocketAddr, /// The local address of the connection @@ -207,6 +211,8 @@ pub struct PeerInfo { pub status: Arc, /// The timestamp when the session to that peer has been established. pub session_established: Instant, + /// The peer's connection kind + pub kind: PeerKind, } /// The direction of the connection. diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index b3fa43252ec1..3434e9439468 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -42,7 +42,7 @@ use reth_eth_wire::{ DisconnectReason, EthVersion, Status, }; use reth_metrics::common::mpsc::UnboundedMeteredSender; -use reth_network_api::{EthProtocolInfo, NetworkStatus, ReputationChangeKind}; +use reth_network_api::{EthProtocolInfo, NetworkStatus, PeerInfo, ReputationChangeKind}; use reth_network_peers::{NodeRecord, PeerId}; use reth_primitives::ForkId; use reth_provider::{BlockNumReader, BlockReader}; @@ -604,17 +604,17 @@ where } } NetworkHandleMessage::GetPeerInfos(tx) => { - let _ = tx.send(self.swarm.sessions_mut().get_peer_info()); + let _ = tx.send(self.get_peer_infos()); } NetworkHandleMessage::GetPeerInfoById(peer_id, tx) => { - let _ = tx.send(self.swarm.sessions_mut().get_peer_info_by_id(peer_id)); + let _ = tx.send(self.get_peer_info_by_id(peer_id)); } NetworkHandleMessage::GetPeerInfosByIds(peer_ids, tx) => { - let _ = tx.send(self.swarm.sessions().get_peer_infos_by_ids(peer_ids)); + let _ = tx.send(self.get_peer_infos_by_ids(peer_ids)); } NetworkHandleMessage::GetPeerInfosByPeerKind(kind, tx) => { - let peers = self.swarm.state().peers().peers_by_kind(kind); - let _ = tx.send(self.swarm.sessions().get_peer_infos_by_ids(peers)); + let peer_ids = self.swarm.state().peers().peers_by_kind(kind); + let _ = tx.send(self.get_peer_infos_by_ids(peer_ids)); } NetworkHandleMessage::AddRlpxSubProtocol(proto) => self.add_rlpx_sub_protocol(proto), NetworkHandleMessage::GetTransactionsHandle(tx) => { @@ -865,6 +865,42 @@ where } } + /// Returns [`PeerInfo`] for all connected peers + fn get_peer_infos(&self) -> Vec { + self.swarm + .sessions() + .active_sessions() + .iter() + .filter_map(|(&peer_id, session)| { + self.swarm + .state() + .peers() + .peer_by_id(peer_id) + .map(|(record, kind)| session.peer_info(&record, kind)) + }) + .collect() + } + + /// Returns [`PeerInfo`] for a given peer. + /// + /// Returns `None` if there's no active session to the peer. + fn get_peer_info_by_id(&self, peer_id: PeerId) -> Option { + self.swarm.sessions().active_sessions().get(&peer_id).and_then(|session| { + self.swarm + .state() + .peers() + .peer_by_id(peer_id) + .map(|(record, kind)| session.peer_info(&record, kind)) + }) + } + + /// Returns [`PeerInfo`] for a given peers. + /// + /// Ignore the non-active peer. + fn get_peer_infos_by_ids(&self, peer_ids: impl IntoIterator) -> Vec { + peer_ids.into_iter().filter_map(|peer_id| self.get_peer_info_by_id(peer_id)).collect() + } + /// Updates the metrics for active,established connections #[inline] fn update_active_connection_metrics(&self) { diff --git a/crates/net/network/src/peers.rs b/crates/net/network/src/peers.rs index ca80faf5c1bb..41950226282c 100644 --- a/crates/net/network/src/peers.rs +++ b/crates/net/network/src/peers.rs @@ -212,15 +212,17 @@ impl PeersManager { }) } - /// Returns the [`NodeRecord`] for the given peer id - #[allow(dead_code)] - fn peer_by_id(&self, peer_id: PeerId) -> Option { + /// Returns the `NodeRecord` and `PeerKind` for the given peer id + pub(crate) fn peer_by_id(&self, peer_id: PeerId) -> Option<(NodeRecord, PeerKind)> { self.peers.get(&peer_id).map(|v| { - NodeRecord::new_with_ports( - v.addr.tcp.ip(), - v.addr.tcp.port(), - v.addr.udp.map(|addr| addr.port()), - peer_id, + ( + NodeRecord::new_with_ports( + v.addr.tcp.ip(), + v.addr.tcp.port(), + v.addr.udp.map(|addr| addr.port()), + peer_id, + ), + v.kind, ) }) } @@ -1378,7 +1380,7 @@ mod tests { _ => unreachable!(), } - let record = peers.peer_by_id(peer).unwrap(); + let (record, _) = peers.peer_by_id(peer).unwrap(); assert_eq!(record.tcp_addr(), socket_addr); assert_eq!(record.udp_addr(), socket_addr); } @@ -1405,7 +1407,7 @@ mod tests { _ => unreachable!(), } - let record = peers.peer_by_id(peer).unwrap(); + let (record, _) = peers.peer_by_id(peer).unwrap(); assert_eq!(record.tcp_addr(), tcp_addr); assert_eq!(record.udp_addr(), udp_addr); } diff --git a/crates/net/network/src/session/handle.rs b/crates/net/network/src/session/handle.rs index b28b1e27e390..4c1a5e5315ac 100644 --- a/crates/net/network/src/session/handle.rs +++ b/crates/net/network/src/session/handle.rs @@ -11,8 +11,8 @@ use reth_eth_wire::{ errors::EthStreamError, DisconnectReason, EthVersion, Status, }; -use reth_network_api::PeerInfo; -use reth_network_peers::PeerId; +use reth_network_api::{PeerInfo, PeerKind}; +use reth_network_peers::{NodeRecord, PeerId}; use std::{io, net::SocketAddr, sync::Arc, time::Instant}; use tokio::sync::{ mpsc::{self, error::SendError}, @@ -136,10 +136,12 @@ impl ActiveSessionHandle { } /// Extracts the [`PeerInfo`] from the session handle. - pub(crate) fn peer_info(&self) -> PeerInfo { + pub(crate) fn peer_info(&self, record: &NodeRecord, kind: PeerKind) -> PeerInfo { PeerInfo { remote_id: self.remote_id, direction: self.direction, + enode: record.to_string(), + enr: None, remote_addr: self.remote_addr, local_addr: self.local_addr, capabilities: self.capabilities.clone(), @@ -147,6 +149,7 @@ impl ActiveSessionHandle { eth_version: self.version, status: self.status.clone(), session_established: self.established, + kind, } } } diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index 715ed59cf635..fc6f0e6a1f1e 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -170,6 +170,11 @@ impl SessionManager { self.secret_key } + /// Returns a borrowed reference to the active sessions. + pub const fn active_sessions(&self) -> &HashMap { + &self.active_sessions + } + /// Returns the session hello message. pub fn hello_message(&self) -> HelloMessageWithProtocols { self.hello_message.clone() @@ -587,35 +592,6 @@ impl SessionManager { } } } - - /// Returns [`PeerInfo`] for all connected peers - pub(crate) fn get_peer_info(&self) -> Vec { - self.active_sessions.values().map(ActiveSessionHandle::peer_info).collect() - } - - /// Returns [`PeerInfo`] for a given peer. - /// - /// Returns `None` if there's no active session to the peer. - pub(crate) fn get_peer_info_by_id(&self, peer_id: PeerId) -> Option { - self.active_sessions.get(&peer_id).map(ActiveSessionHandle::peer_info) - } - /// Returns [`PeerInfo`] for a given peer. - /// - /// Returns `None` if there's no active session to the peer. - pub(crate) fn get_peer_infos_by_ids( - &self, - peer_ids: impl IntoIterator, - ) -> Vec { - let mut infos = Vec::new(); - for peer_id in peer_ids { - if let Some(info) = - self.active_sessions.get(&peer_id).map(ActiveSessionHandle::peer_info) - { - infos.push(info); - } - } - infos - } } /// Events produced by the [`SessionManager`] diff --git a/crates/rpc/rpc-api/src/admin.rs b/crates/rpc/rpc-api/src/admin.rs index 173cd8ef7a98..66f8918a33cb 100644 --- a/crates/rpc/rpc-api/src/admin.rs +++ b/crates/rpc/rpc-api/src/admin.rs @@ -1,6 +1,6 @@ use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use reth_network_peers::{AnyNode, NodeRecord}; -use reth_rpc_types::{admin::NodeInfo, PeerInfo}; +use reth_rpc_types::admin::{NodeInfo, PeerInfo}; /// Admin namespace rpc interface that gives access to several non-standard RPC methods. #[cfg_attr(not(feature = "client"), rpc(server, namespace = "admin"))] diff --git a/crates/rpc/rpc/src/admin.rs b/crates/rpc/rpc/src/admin.rs index f294a52c1bf3..1d59baa6e274 100644 --- a/crates/rpc/rpc/src/admin.rs +++ b/crates/rpc/rpc/src/admin.rs @@ -6,12 +6,12 @@ use async_trait::async_trait; use jsonrpsee::core::RpcResult; use reth_chainspec::ChainSpec; use reth_network_api::{NetworkInfo, PeerKind, Peers}; -use reth_network_peers::{AnyNode, NodeRecord}; +use reth_network_peers::{id2pk, AnyNode, NodeRecord}; use reth_rpc_api::AdminApiServer; use reth_rpc_server_types::ToRpcResult; -use reth_rpc_types::{ - admin::{EthProtocolInfo, NodeInfo, Ports, ProtocolInfo}, - PeerEthProtocolInfo, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, +use reth_rpc_types::admin::{ + EthInfo, EthPeerInfo, EthProtocolInfo, NodeInfo, PeerInfo, PeerNetworkInfo, PeerProtocolInfo, + Ports, ProtocolInfo, }; /// `admin` API implementation. @@ -63,33 +63,43 @@ where Ok(true) } + /// Handler for `admin_peers` async fn peers(&self) -> RpcResult> { let peers = self.network.get_all_peers().await.to_rpc_result()?; - let peers = peers - .into_iter() - .map(|peer| PeerInfo { - id: Some(peer.remote_id.to_string()), - name: peer.client_version.to_string(), - caps: peer.capabilities.capabilities().iter().map(|cap| cap.to_string()).collect(), - network: PeerNetworkInfo { - remote_address: peer.remote_addr.to_string(), - local_address: peer - .local_addr - .unwrap_or_else(|| self.network.local_addr()) - .to_string(), - }, - protocols: PeerProtocolsInfo { - eth: Some(PeerEthProtocolInfo { - difficulty: Some(peer.status.total_difficulty), - head: peer.status.blockhash.to_string(), - version: peer.status.version as u32, - }), - pip: None, - }, - }) - .collect(); + let mut infos = Vec::with_capacity(peers.len()); - Ok(peers) + for peer in peers { + if let Ok(pk) = id2pk(peer.remote_id) { + infos.push(PeerInfo { + id: pk.to_string(), + name: peer.client_version.to_string(), + enode: peer.enode, + enr: peer.enr, + caps: peer + .capabilities + .capabilities() + .iter() + .map(|cap| cap.to_string()) + .collect(), + network: PeerNetworkInfo { + remote_address: peer.remote_addr, + local_address: peer.local_addr.unwrap_or_else(|| self.network.local_addr()), + inbound: peer.direction.is_incoming(), + trusted: peer.kind.is_trusted(), + static_node: peer.kind.is_static(), + }, + protocols: PeerProtocolInfo { + eth: Some(EthPeerInfo::Info(EthInfo { + version: peer.status.version as u64, + })), + snap: None, + other: Default::default(), + }, + }) + } + } + + Ok(infos) } /// Handler for `admin_nodeInfo`