diff --git a/Cargo.lock b/Cargo.lock index f870f7da1..b2d7c827d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4408,7 +4408,9 @@ dependencies = [ name = "reth-network-api" version = "0.1.0" dependencies = [ + "async-trait", "reth-primitives", + "serde", ] [[package]] @@ -4560,6 +4562,7 @@ name = "reth-rpc-types" version = "0.1.0" dependencies = [ "bytes", + "reth-network-api", "reth-primitives", "reth-rlp", "serde", diff --git a/crates/net/network-api/Cargo.toml b/crates/net/network-api/Cargo.toml index 5f4018be4..37a90c6d7 100644 --- a/crates/net/network-api/Cargo.toml +++ b/crates/net/network-api/Cargo.toml @@ -8,4 +8,11 @@ readme = "README.md" description = "Network interfaces" [dependencies] -reth-primitives = { path = "../../primitives" } \ No newline at end of file +# reth +reth-primitives = { path = "../../primitives" } + +# io +serde = { version = "1.0", features = ["derive"] } + +# misc +async-trait = "0.1" \ No newline at end of file diff --git a/crates/net/network-api/src/lib.rs b/crates/net/network-api/src/lib.rs index 5f5aea76f..15bdee40a 100644 --- a/crates/net/network-api/src/lib.rs +++ b/crates/net/network-api/src/lib.rs @@ -9,13 +9,22 @@ //! //! Provides abstractions for the reth-network crate. -use reth_primitives::NodeRecord; -use std::net::SocketAddr; +use async_trait::async_trait; +use reth_primitives::{NodeRecord, H256, U256}; +use serde::{Deserialize, Serialize}; +use std::{error::Error, net::SocketAddr}; /// Provides general purpose information about the network. +#[async_trait] pub trait NetworkInfo: Send + Sync { + /// Associated error type for the network implementation. + type Error: Send + Sync + Error; + /// Returns the [`SocketAddr`] that listens for incoming connections. fn local_addr(&self) -> SocketAddr; + + /// Returns the current status of the network being ran by the local node. + async fn network_status(&self) -> Result; } /// Provides general purpose information about Peers in the network. @@ -28,3 +37,25 @@ pub trait PeersInfo: Send + Sync { /// Returns the Ethereum Node Record of the node. fn local_node_record(&self) -> NodeRecord; } + +/// The status of the network being ran by the local node. +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct NetworkStatus { + /// The local node client name. + pub client_name: String, + /// Information about the Ethereum Wire Protocol. + pub eth_protocol_info: EthProtocolInfo, +} + +/// Information about the Ethereum Wire Protocol (ETH) +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct EthProtocolInfo { + /// The current difficulty at the head of the chain. + pub difficulty: U256, + /// The block hash of the head of the chain. + pub head: H256, + /// Network ID in base 10. + pub network: u64, + /// Genesis block of the current chain. + pub genesis: H256, +} diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index 00bb962f2..b24e6ce8e 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -307,6 +307,11 @@ where self.swarm.state().fetch_client() } + /// Returns the current [`Status`] for the local node. + pub fn status(&self) -> Status { + self.swarm.sessions().status() + } + /// Event hook for an unexpected message from the peer. fn on_invalid_message( &mut self, @@ -499,6 +504,9 @@ where NetworkHandleMessage::FetchClient(tx) => { let _ = tx.send(self.fetch_client()); } + NetworkHandleMessage::GetStatus(tx) => { + let _ = tx.send(self.status()); + } NetworkHandleMessage::StatusUpdate { height, hash, total_difficulty } => { if let Some(transition) = self.swarm.sessions_mut().on_status_update(height, hash, total_difficulty) diff --git a/crates/net/network/src/network.rs b/crates/net/network/src/network.rs index 6083b67a6..bb920bb92 100644 --- a/crates/net/network/src/network.rs +++ b/crates/net/network/src/network.rs @@ -6,14 +6,17 @@ use crate::{ session::PeerInfo, FetchClient, }; +use async_trait::async_trait; use parking_lot::Mutex; -use reth_eth_wire::{DisconnectReason, NewBlock, NewPooledTransactionHashes, SharedTransactions}; +use reth_eth_wire::{ + DisconnectReason, NewBlock, NewPooledTransactionHashes, SharedTransactions, Status, +}; use reth_interfaces::{ p2p::headers::client::StatusUpdater, sync::{SyncState, SyncStateProvider, SyncStateUpdater}, }; use reth_net_common::bandwidth_meter::BandwidthMeter; -use reth_network_api::{NetworkInfo, PeersInfo}; +use reth_network_api::{EthProtocolInfo, NetworkInfo, NetworkStatus, PeersInfo}; use reth_primitives::{NodeRecord, PeerId, TransactionSigned, TxHash, H256, U256}; use std::{ net::SocketAddr, @@ -126,6 +129,13 @@ impl NetworkHandle { self.send_message(NetworkHandleMessage::StatusUpdate { height, hash, total_difficulty }); } + /// Get the current status of the node. + pub async fn get_status(&self) -> Result { + let (tx, rx) = oneshot::channel(); + let _ = self.manager().send(NetworkHandleMessage::GetStatus(tx)); + rx.await + } + /// Announce a block over devp2p /// /// Caution: in PoS this is a noop, since new block propagation will happen over devp2p @@ -215,10 +225,27 @@ impl PeersInfo for NetworkHandle { } } +#[async_trait] impl NetworkInfo for NetworkHandle { + type Error = oneshot::error::RecvError; + fn local_addr(&self) -> SocketAddr { *self.inner.listener_address.lock() } + + async fn network_status(&self) -> Result { + let status = self.get_status().await?; + + Ok(NetworkStatus { + client_name: "Reth".to_string(), + eth_protocol_info: EthProtocolInfo { + difficulty: status.total_difficulty, + head: status.blockhash, + network: status.chain.id(), + genesis: status.genesis, + }, + }) + } } impl StatusUpdater for NetworkHandle { @@ -251,7 +278,7 @@ struct NetworkInner { listener_address: Arc>, /// The identifier used by this node. local_peer_id: PeerId, - /// Access to the all the nodes + /// Access to the all the nodes. peers: PeersHandle, /// The mode of the network network_mode: NetworkMode, @@ -291,6 +318,8 @@ pub(crate) enum NetworkHandleMessage { FetchClient(oneshot::Sender), /// Apply a status update. StatusUpdate { height: u64, hash: H256, total_difficulty: U256 }, + /// Get the currenet status + GetStatus(oneshot::Sender), /// Get PeerInfo from all the peers GetPeerInfo(oneshot::Sender>), /// Get PeerInfo for a specific peer diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index 8d7a68fda..6fd009c92 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -49,6 +49,7 @@ pub struct SessionId(usize); /// Manages a set of sessions. #[must_use = "Session Manager must be polled to process session events."] +#[derive(Debug)] pub(crate) struct SessionManager { /// Tracks the identifier for the next session. next_id: usize, @@ -142,6 +143,11 @@ impl SessionManager { SessionId(id) } + /// Returns the current status of the session. + pub(crate) fn status(&self) -> Status { + self.status + } + /// Spawns the given future onto a new task that is tracked in the `spawned_tasks` [`JoinSet`]. fn spawn(&self, f: F) where diff --git a/crates/net/network/src/swarm.rs b/crates/net/network/src/swarm.rs index af0bb7ab7..0497b4e8a 100644 --- a/crates/net/network/src/swarm.rs +++ b/crates/net/network/src/swarm.rs @@ -101,6 +101,11 @@ where &self.incoming } + /// Access to the [`SessionManager`]. + pub(crate) fn sessions(&self) -> &SessionManager { + &self.sessions + } + /// Mutable access to the [`SessionManager`]. pub(crate) fn sessions_mut(&mut self) -> &mut SessionManager { &mut self.sessions diff --git a/crates/net/rpc-api/src/admin.rs b/crates/net/rpc-api/src/admin.rs index e132d3772..f8678d8a3 100644 --- a/crates/net/rpc-api/src/admin.rs +++ b/crates/net/rpc-api/src/admin.rs @@ -37,5 +37,5 @@ pub trait AdminApi { /// Returns the ENR of the node. #[method(name = "admin_nodeInfo")] - fn node_info(&self) -> Result; + async fn node_info(&self) -> Result; } diff --git a/crates/net/rpc-types/Cargo.toml b/crates/net/rpc-types/Cargo.toml index 7c566ae5d..1cc36a9ce 100644 --- a/crates/net/rpc-types/Cargo.toml +++ b/crates/net/rpc-types/Cargo.toml @@ -12,6 +12,7 @@ Reth RPC types # reth reth-primitives = { path = "../../primitives" } reth-rlp = { path = "../../common/rlp" } +reth-network-api = { path = "../network-api"} # misc serde = { version = "1.0", features = ["derive"] } diff --git a/crates/net/rpc-types/src/admin.rs b/crates/net/rpc-types/src/admin.rs index cd1c197eb..9274be193 100644 --- a/crates/net/rpc-types/src/admin.rs +++ b/crates/net/rpc-types/src/admin.rs @@ -1,9 +1,7 @@ -use reth_primitives::{NodeRecord, PeerId, H256, U256}; +use reth_network_api::NetworkStatus; +use reth_primitives::{NodeRecord, PeerId}; use serde::{Deserialize, Serialize}; -use std::{ - collections::BTreeMap, - net::{IpAddr, SocketAddr}, -}; +use std::net::{IpAddr, SocketAddr}; /// Represents the `admin_nodeInfo` response, which can be queried for all the information /// known about the running node at the networking granularity. @@ -21,28 +19,23 @@ pub struct NodeInfo { /// Address exposed for listening for the local node. #[serde(rename = "listenAddr")] pub listen_addr: SocketAddr, - /// Local node client name. - pub name: String, /// Ports exposed by the node for discovery and listening. pub ports: Ports, /// Networking protocols being run by the local node. - pub protocols: BTreeMap, + #[serde(flatten)] + pub status: NetworkStatus, } impl NodeInfo { /// Creates a new instance of `NodeInfo`. - pub fn new(enr: NodeRecord) -> NodeInfo { - let protocol_info = - BTreeMap::from([("eth".into(), ProtocolInfo::Eth(EthProtocolInfo::default()))]); - + pub fn new(enr: NodeRecord, status: NetworkStatus) -> NodeInfo { NodeInfo { enode: enr, id: enr.id, ip: enr.address, listen_addr: enr.tcp_addr(), - name: "Reth".to_owned(), ports: Ports { discovery: enr.udp_port, listener: enr.tcp_port }, - protocols: protocol_info, + status, } } } @@ -55,22 +48,3 @@ pub struct Ports { /// Port exposed for listening. pub listener: u16, } - -/// Information about the different protocols that can be run by the node (ETH, ) -#[derive(Serialize, Deserialize, Debug)] -pub enum ProtocolInfo { - /// Information about the Ethereum Wire Protocol. - Eth(EthProtocolInfo), -} -/// Information about the Ethereum Wire Protocol (ETH) -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct EthProtocolInfo { - /// The current difficulty at the head of the chain. - pub difficulty: U256, - /// The block hash of the head of the chain. - pub head: H256, - /// Network ID in base 10. - pub network: u64, - /// Genesis block of the current chain. - pub genesis: H256, -} diff --git a/crates/net/rpc/src/admin.rs b/crates/net/rpc/src/admin.rs index cc23af07f..29c52da62 100644 --- a/crates/net/rpc/src/admin.rs +++ b/crates/net/rpc/src/admin.rs @@ -1,6 +1,7 @@ +use async_trait::async_trait; use jsonrpsee::core::RpcResult; use reth_network::{peers::PeerKind, NetworkHandle}; -use reth_network_api::PeersInfo; +use reth_network_api::{NetworkInfo, PeersInfo}; use reth_primitives::NodeRecord; use reth_rpc_api::AdminApiServer; use reth_rpc_types::NodeInfo; @@ -20,6 +21,7 @@ impl AdminApi { } } +#[async_trait] impl AdminApiServer for AdminApi { fn add_peer(&self, record: NodeRecord) -> RpcResult { self.network.add_peer(record.id, record.tcp_addr()); @@ -48,10 +50,14 @@ impl AdminApiServer for AdminApi { todo!() } - fn node_info(&self) -> RpcResult { + async fn node_info(&self) -> RpcResult { let enr = self.network.local_node_record(); + let status = match self.network.network_status().await { + Ok(status) => status, + Err(e) => return RpcResult::Err(jsonrpsee::core::Error::Custom(e.to_string())), + }; - Ok(NodeInfo::new(enr)) + Ok(NodeInfo::new(enr, status)) } }