diff --git a/substrate/core/network-libp2p/src/lib.rs b/substrate/core/network-libp2p/src/lib.rs index 8c5df15c49..6890450763 100644 --- a/substrate/core/network-libp2p/src/lib.rs +++ b/substrate/core/network-libp2p/src/lib.rs @@ -53,7 +53,7 @@ mod transport; pub use custom_proto::RegisteredProtocol; pub use error::{Error, ErrorKind, DisconnectReason}; -pub use libp2p::{Multiaddr, multiaddr::{Protocol}, multiaddr, PeerId}; +pub use libp2p::{Multiaddr, multiaddr::{Protocol}, multiaddr, PeerId, core::PublicKey}; pub use secret::obtain_private_key; pub use service_task::{start_service, Service, ServiceEvent}; pub use traits::{NetworkConfiguration, NodeIndex, NodeId, NonReservedPeerMode}; diff --git a/substrate/core/network/src/lib.rs b/substrate/core/network/src/lib.rs index 5f10051b15..95b6d68113 100644 --- a/substrate/core/network/src/lib.rs +++ b/substrate/core/network/src/lib.rs @@ -69,7 +69,7 @@ pub use protocol::{ProtocolStatus, PeerInfo, Context}; pub use sync::{Status as SyncStatus, SyncState}; pub use network_libp2p::{ NodeIndex, ProtocolId, Severity, Protocol, Multiaddr, - obtain_private_key, multiaddr, + obtain_private_key, multiaddr, PeerId, PublicKey }; pub use message::{generic as generic_message, RequestId, Status as StatusMessage}; pub use error::Error; diff --git a/substrate/core/network/src/protocol.rs b/substrate/core/network/src/protocol.rs index 42c5954532..41612c788d 100644 --- a/substrate/core/network/src/protocol.rs +++ b/substrate/core/network/src/protocol.rs @@ -240,6 +240,20 @@ impl, H: ExHashT> Protocol { } } + pub fn peers(&self) -> Vec<(NodeIndex, PeerInfo)> { + self.context_data.peers.read().iter().map(|(idx, p)| { + ( + *idx, + PeerInfo { + roles: p.roles, + protocol_version: p.protocol_version, + best_hash: p.best_hash, + best_number: p.best_number, + } + ) + }).collect() + } + pub fn handle_packet(&self, io: &mut SyncIo, who: NodeIndex, mut data: &[u8]) { let message: Message = match Decode::decode(&mut data) { Some(m) => m, diff --git a/substrate/core/network/src/service.rs b/substrate/core/network/src/service.rs index 5487accd1b..501b5d6eb3 100644 --- a/substrate/core/network/src/service.rs +++ b/substrate/core/network/src/service.rs @@ -26,7 +26,7 @@ use network_libp2p::{RegisteredProtocol, parse_str_addr, Protocol as Libp2pProto use io::NetSyncIo; use consensus::import_queue::{ImportQueue, Link}; use consensus_gossip::ConsensusGossip; -use protocol::{self, Protocol, ProtocolContext, Context, ProtocolStatus}; +use protocol::{self, Protocol, ProtocolContext, Context, ProtocolStatus, PeerInfo}; use config::Params; use error::Error; use specialization::NetworkSpecialization; @@ -45,6 +45,8 @@ const PROPAGATE_TIMEOUT: Duration = Duration::from_millis(5000); pub trait SyncProvider: Send + Sync { /// Get sync status fn status(&self) -> ProtocolStatus; + /// Get currently connected peers + fn peers(&self) -> Vec<(NodeIndex, Option, PeerInfo)>; } /// Minimum Requirements for a Hash within Networking @@ -228,6 +230,14 @@ impl, H: ExHashT> SyncProvider< fn status(&self) -> ProtocolStatus { self.handler.status() } + + fn peers(&self) -> Vec<(NodeIndex, Option, PeerInfo)> { + let peers = self.handler.peers(); + let network = self.network.lock(); + peers.into_iter().map(|(idx, info)| { + (idx, network.peer_id_of_node(idx).map(|p| p.clone()), info) + }).collect::>() + } } /// Trait for managing network diff --git a/substrate/core/rpc-servers/src/lib.rs b/substrate/core/rpc-servers/src/lib.rs index bef0d4a368..fb09799ef5 100644 --- a/substrate/core/rpc-servers/src/lib.rs +++ b/substrate/core/rpc-servers/src/lib.rs @@ -52,7 +52,7 @@ pub fn rpc_handler( S: apis::state::StateApi, C: apis::chain::ChainApi, Block::Hash, Block::Header, SignedBlock, Metadata=Metadata>, A: apis::author::AuthorApi, - Y: apis::system::SystemApi, + Y: apis::system::SystemApi>, { let mut io = pubsub::PubSubHandler::default(); io.extend_with(state.to_delegate()); diff --git a/substrate/core/rpc/src/system/helpers.rs b/substrate/core/rpc/src/system/helpers.rs index 0d3b7e56cd..5a679de0ad 100644 --- a/substrate/core/rpc/src/system/helpers.rs +++ b/substrate/core/rpc/src/system/helpers.rs @@ -45,6 +45,23 @@ pub struct Health { pub is_syncing: bool, } +/// Network Peer information +#[derive(Debug, PartialEq, Serialize)] +pub struct PeerInfo { + /// Peer Node Index + pub index: usize, + /// Peer ID + pub peer_id: String, + /// Roles + pub roles: String, + /// Protocol version + pub protocol_version: u32, + /// Peer best block hash + pub best_hash: Hash, + /// Peer best block number + pub best_number: Number, +} + impl fmt::Display for Health { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{} peers ({})", self.peers, if self.is_syncing { diff --git a/substrate/core/rpc/src/system/mod.rs b/substrate/core/rpc/src/system/mod.rs index 634dd1c063..1d33104483 100644 --- a/substrate/core/rpc/src/system/mod.rs +++ b/substrate/core/rpc/src/system/mod.rs @@ -24,14 +24,14 @@ mod tests; use std::sync::Arc; use network; -use runtime_primitives::traits; +use runtime_primitives::traits::{self, Header as HeaderT}; use self::error::Result; -pub use self::helpers::{Properties, SystemInfo, Health}; +pub use self::helpers::{Properties, SystemInfo, Health, PeerInfo}; build_rpc_trait! { /// Substrate system RPC API - pub trait SystemApi { + pub trait SystemApi { /// Get the node's implementation name. Plain old string. #[rpc(name = "system_name")] fn system_name(&self) -> Result; @@ -55,6 +55,10 @@ build_rpc_trait! { /// - not performing a major sync #[rpc(name = "system_health")] fn system_health(&self) -> Result; + + /// Returns currently connected peers + #[rpc(name = "system_peers")] + fn system_peers(&self) -> Result>>; } } @@ -80,7 +84,7 @@ impl System { } } -impl SystemApi for System { +impl SystemApi::Number> for System { fn system_name(&self) -> Result { Ok(self.info.impl_name.clone()) } @@ -115,4 +119,15 @@ impl SystemApi for System { Ok(health) } } + + fn system_peers(&self) -> Result::Number>>> { + Ok(self.sync.peers().into_iter().map(|(idx, peer_id, p)| PeerInfo { + index: idx, + peer_id: peer_id.map_or_else(Default::default, |p| p.to_base58()), + roles: format!("{:?}", p.roles), + protocol_version: p.protocol_version, + best_hash: p.best_hash, + best_number: p.best_number, + }).collect()) + } } diff --git a/substrate/core/rpc/src/system/tests.rs b/substrate/core/rpc/src/system/tests.rs index 5c8f0b9058..976dbe509a 100644 --- a/substrate/core/rpc/src/system/tests.rs +++ b/substrate/core/rpc/src/system/tests.rs @@ -16,8 +16,10 @@ use super::*; -use network::{self, SyncState, SyncStatus, ProtocolStatus}; +use network::{self, SyncState, SyncStatus, ProtocolStatus, NodeIndex, PeerId, PeerInfo as NetworkPeerInfo, PublicKey}; +use network::config::Roles; use test_client::runtime::Block; +use primitives::H256; #[derive(Default)] struct Status { @@ -37,6 +39,15 @@ impl network::SyncProvider for Status { num_active_peers: 0, } } + + fn peers(&self) -> Vec<(NodeIndex, Option, NetworkPeerInfo)> { + vec![(1, Some(PublicKey::Ed25519((0 .. 32).collect::>()).into()), NetworkPeerInfo { + roles: Roles::FULL, + protocol_version: 1, + best_hash: Default::default(), + best_number: 1 + })] + } } @@ -129,3 +140,18 @@ fn system_health() { } ); } + +#[test] +fn system_peers() { + assert_eq!( + api(None).system_peers().unwrap(), + vec![PeerInfo { + index: 1, + peer_id: "QmS5oyTmdjwBowwAH1D9YQnoe2HyWpVemH8qHiU5RqWPh4".into(), + roles: "FULL".into(), + protocol_version: 1, + best_hash: Default::default(), + best_number: 1u64, + }] + ); +}