From 3ff9324ec0cd57d337d165dd4535fb95331dcc9f Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 20 Jan 2017 12:41:49 +0100 Subject: [PATCH] LES Peer Info (#4195) * connected peers function for network service * get LES peer info in sync API * new peer info in RPC --- ethcore/light/src/net/context.rs | 2 +- ethcore/light/src/net/mod.rs | 6 ++ rpc/src/v1/tests/helpers/sync_provider.rs | 20 ++++--- rpc/src/v1/tests/mocked/parity.rs | 2 +- rpc/src/v1/types/mod.rs.in | 4 +- rpc/src/v1/types/sync.rs | 46 ++++++++++++--- sync/src/api.rs | 69 +++++++++++++++++++---- sync/src/chain.rs | 26 +++------ sync/src/lib.rs | 2 +- util/network/src/host.rs | 14 +++++ util/network/src/service.rs | 7 ++- 11 files changed, 150 insertions(+), 48 deletions(-) diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs index e95434a3b..4fb83db66 100644 --- a/ethcore/light/src/net/context.rs +++ b/ethcore/light/src/net/context.rs @@ -77,7 +77,7 @@ impl<'a> IoContext for NetworkContext<'a> { } } -/// Basic context for a the protocol. +/// Basic context for the protocol. pub trait BasicContext { /// Returns the relevant's peer persistent Id (aka NodeId). fn persistent_peer_id(&self, peer: PeerId) -> Option; diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index c5071cc02..1de912388 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -316,6 +316,12 @@ impl LightProtocol { } } + /// Attempt to get peer status. + pub fn peer_status(&self, peer: &PeerId) -> Option { + self.peers.read().get(&peer) + .map(|peer| peer.lock().status.clone()) + } + /// Check the maximum amount of requests of a specific type /// which a peer would be able to serve. Returns zero if the /// peer is unknown or has no buffer flow parameters. diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index af87cd98c..c3b7911fd 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -18,7 +18,7 @@ use std::collections::BTreeMap; use util::{H256, RwLock}; -use ethsync::{SyncProvider, SyncStatus, SyncState, PeerInfo, TransactionStats}; +use ethsync::{SyncProvider, EthProtocolInfo, SyncStatus, SyncState, PeerInfo, TransactionStats}; /// TestSyncProvider config. pub struct Config { @@ -78,9 +78,12 @@ impl SyncProvider for TestSyncProvider { capabilities: vec!["eth/62".to_owned(), "eth/63".to_owned()], remote_address: "127.0.0.1:7777".to_owned(), local_address: "127.0.0.1:8888".to_owned(), - eth_version: 62, - eth_difficulty: Some(40.into()), - eth_head: 50.into() + eth_info: Some(EthProtocolInfo { + version: 62, + difficulty: Some(40.into()), + head: 50.into(), + }), + les_info: None, }, PeerInfo { id: None, @@ -88,9 +91,12 @@ impl SyncProvider for TestSyncProvider { capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()], remote_address: "Handshake".to_owned(), local_address: "127.0.0.1:3333".to_owned(), - eth_version: 64, - eth_difficulty: None, - eth_head: 60.into() + eth_info: Some(EthProtocolInfo { + version: 64, + difficulty: None, + head: 60.into() + }), + les_info: None, } ] } diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index eb1f3e3eb..27465401e 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -265,7 +265,7 @@ fn rpc_parity_net_peers() { let io = deps.default_client(); let request = r#"{"jsonrpc": "2.0", "method": "parity_netPeers", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/62","eth/63"],"id":"node1","name":"Parity/1","network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":62}}},{"caps":["eth/63","eth/64"],"id":null,"name":"Parity/2","network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":64}}}]},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/62","eth/63"],"id":"node1","name":"Parity/1","network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":62},"les":null}},{"caps":["eth/63","eth/64"],"id":null,"name":"Parity/2","network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":64},"les":null}}]},"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 954622a74..d04610f8c 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -50,8 +50,8 @@ pub use self::hash::{H64, H160, H256, H512, H520, H2048}; pub use self::index::Index; pub use self::log::Log; pub use self::sync::{ - SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, PeerEthereumProtocolInfo, - TransactionStats, ChainStatus + SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, + TransactionStats, ChainStatus, EthProtocolInfo, LesProtocolInfo, }; pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus}; pub use self::transaction_request::TransactionRequest; diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index d0e3c7c99..388d85924 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::collections::BTreeMap; -use ethsync::{PeerInfo as SyncPeerInfo, TransactionStats as SyncTransactionStats}; +use ethsync::{self, PeerInfo as SyncPeerInfo, TransactionStats as SyncTransactionStats}; use serde::{Serialize, Serializer}; use v1::types::{U256, H512}; @@ -82,12 +82,14 @@ pub struct PeerNetworkInfo { #[derive(Default, Debug, Serialize)] pub struct PeerProtocolsInfo { /// Ethereum protocol information - pub eth: Option, + pub eth: Option, + /// LES protocol information. + pub les: Option, } /// Peer Ethereum protocol information #[derive(Default, Debug, Serialize)] -pub struct PeerEthereumProtocolInfo { +pub struct EthProtocolInfo { /// Negotiated ethereum protocol version pub version: u32, /// Peer total difficulty if known @@ -96,6 +98,37 @@ pub struct PeerEthereumProtocolInfo { pub head: String, } +impl From for EthProtocolInfo { + fn from(info: ethsync::EthProtocolInfo) -> Self { + EthProtocolInfo { + version: info.version, + difficulty: info.difficulty.map(Into::into), + head: info.head.hex(), + } + } +} + +/// Peer LES protocol information +#[derive(Default, Debug, Serialize)] +pub struct LesProtocolInfo { + /// Negotiated LES protocol version + pub version: u32, + /// Peer total difficulty + pub difficulty: U256, + /// SHA3 of peer best block hash + pub head: String, +} + +impl From for LesProtocolInfo { + fn from(info: ethsync::LesProtocolInfo) -> Self { + LesProtocolInfo { + version: info.version, + difficulty: info.difficulty.into(), + head: info.head.hex(), + } + } +} + /// Sync status #[derive(Debug, PartialEq)] pub enum SyncStatus { @@ -137,11 +170,8 @@ impl From for PeerInfo { local_address: p.local_address, }, protocols: PeerProtocolsInfo { - eth: Some(PeerEthereumProtocolInfo { - version: p.eth_version, - difficulty: p.eth_difficulty.map(|d| d.into()), - head: p.eth_head.hex(), - }) + eth: p.eth_info.map(Into::into), + les: p.les_info.map(Into::into), }, } } diff --git a/sync/src/api.rs b/sync/src/api.rs index 7a8a3623f..c3a2de0fa 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -36,7 +36,7 @@ use parking_lot::RwLock; use chain::{ETH_PACKET_COUNT, SNAPSHOT_SYNC_PACKET_COUNT}; use light::client::LightChainClient; use light::Provider; -use light::net::{LightProtocol, Params as LightParams, Capabilities, Handler as LightHandler, EventContext}; +use light::net::{self as light_net, LightProtocol, Params as LightParams, Capabilities, Handler as LightHandler, EventContext}; /// Parity sync protocol pub const WARP_SYNC_PROTOCOL_ID: ProtocolId = *b"par"; @@ -123,12 +123,44 @@ pub struct PeerInfo { pub remote_address: String, /// Local endpoint address pub local_address: String, - /// Ethereum protocol version - pub eth_version: u32, + /// Eth protocol info. + pub eth_info: Option, + /// Light protocol info. + pub les_info: Option, +} + +/// Ethereum protocol info. +#[derive(Debug)] +#[cfg_attr(feature = "ipc", derive(Binary))] +pub struct EthProtocolInfo { + /// Protocol version + pub version: u32, /// SHA3 of peer best block hash - pub eth_head: H256, + pub head: H256, /// Peer total difficulty if known - pub eth_difficulty: Option, + pub difficulty: Option, +} + +/// LES protocol info. +#[derive(Debug)] +#[cfg_attr(feature = "ipc", derive(Binary))] +pub struct LesProtocolInfo { + /// Protocol version + pub version: u32, + /// SHA3 of peer best block hash + pub head: H256, + /// Peer total difficulty if known + pub difficulty: U256, +} + +impl From for LesProtocolInfo { + fn from(status: light_net::Status) -> Self { + LesProtocolInfo { + version: status.protocol_version, + head: status.head_hash, + difficulty: status.head_td, + } + } } /// EthSync initialization parameters. @@ -214,11 +246,28 @@ impl SyncProvider for EthSync { /// Get sync peers fn peers(&self) -> Vec { - // TODO: [rob] LES peers/peer info - self.network.with_context_eval(self.subprotocol_name, |context| { - let sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay); - self.eth_handler.sync.write().peers(&sync_io) - }).unwrap_or(Vec::new()) + self.network.with_context_eval(self.subprotocol_name, |ctx| { + let peer_ids = self.network.connected_peers(); + let eth_sync = self.eth_handler.sync.read(); + let light_proto = self.light_proto.as_ref(); + + peer_ids.into_iter().filter_map(|peer_id| { + let session_info = match ctx.session_info(peer_id) { + None => return None, + Some(info) => info, + }; + + Some(PeerInfo { + id: session_info.id.map(|id| id.hex()), + client_version: session_info.client_version, + capabilities: session_info.peer_capabilities.into_iter().map(|c| c.to_string()).collect(), + remote_address: session_info.remote_address, + local_address: session_info.local_address, + eth_info: eth_sync.peer_info(&peer_id), + les_info: light_proto.as_ref().and_then(|lp| lp.peer_status(&peer_id)).map(Into::into), + }) + }).collect() + }).unwrap_or_else(Vec::new) } fn enode(&self) -> Option { diff --git a/sync/src/chain.rs b/sync/src/chain.rs index fb91a8ef8..40ed60b1f 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -102,7 +102,7 @@ use super::SyncConfig; use block_sync::{BlockDownloader, BlockRequest, BlockDownloaderImportError as DownloaderImportError, DownloadAction}; use rand::Rng; use snapshot::{Snapshot, ChunkType}; -use api::{PeerInfo as PeerInfoDigest, WARP_SYNC_PROTOCOL_ID}; +use api::{EthProtocolInfo as PeerInfoDigest, WARP_SYNC_PROTOCOL_ID}; use transactions_stats::{TransactionsStats, Stats as TransactionStats}; known_heap_size!(0, PeerInfo); @@ -431,22 +431,14 @@ impl ChainSync { } /// Returns information on peers connections - pub fn peers(&self, io: &SyncIo) -> Vec { - self.peers.iter() - .filter_map(|(&peer_id, peer_data)| - io.peer_session_info(peer_id).map(|session_info| - PeerInfoDigest { - id: session_info.id.map(|id| id.hex()), - client_version: session_info.client_version, - capabilities: session_info.peer_capabilities.into_iter().map(|c| c.to_string()).collect(), - remote_address: session_info.remote_address, - local_address: session_info.local_address, - eth_version: peer_data.protocol_version as u32, - eth_difficulty: peer_data.difficulty, - eth_head: peer_data.latest_hash, - }) - ) - .collect() + pub fn peer_info(&self, peer_id: &PeerId) -> Option { + self.peers.get(peer_id).map(|peer_data| { + PeerInfoDigest { + version: peer_data.protocol_version as u32, + difficulty: peer_data.difficulty, + head: peer_data.latest_hash, + } + }) } /// Returns transactions propagation statistics diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 9b3cdab5e..0395dda8e 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -75,7 +75,7 @@ mod api; pub use api::{ EthSync, Params, SyncProvider, ManageNetwork, SyncConfig, ServiceConfiguration, NetworkConfiguration, PeerInfo, AllowIP, TransactionStats, - LightSync, LightSyncParams, + LightSync, LightSyncParams, LesProtocolInfo, EthProtocolInfo, }; pub use chain::{SyncStatus, SyncState}; pub use network::{is_valid_node_url, NonReservedPeerMode, NetworkError}; diff --git a/util/network/src/host.rs b/util/network/src/host.rs index 05f0e83a4..3a0a7183f 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -542,6 +542,20 @@ impl Host { Ok(()) } + /// Get all connected peers. + pub fn connected_peers(&self) -> Vec { + let sessions = self.sessions.read(); + let sessions = &*sessions; + + let mut peers = Vec::with_capacity(sessions.count()); + for i in (0..MAX_SESSIONS).map(|x| x + FIRST_SESSION) { + if sessions.get(i).is_some() { + peers.push(i); + } + } + peers + } + fn init_public_interface(&self, io: &IoContext) -> Result<(), NetworkError> { if self.info.read().public_endpoint.is_some() { return Ok(()); diff --git a/util/network/src/service.rs b/util/network/src/service.rs index b1157fa58..af3a25112 100644 --- a/util/network/src/service.rs +++ b/util/network/src/service.rs @@ -16,7 +16,7 @@ use {NetworkProtocolHandler, NetworkConfiguration, NonReservedPeerMode}; use error::NetworkError; -use host::{Host, NetworkContext, NetworkIoMessage, ProtocolId}; +use host::{Host, NetworkContext, NetworkIoMessage, PeerId, ProtocolId}; use stats::NetworkStats; use io::*; use parking_lot::RwLock; @@ -142,6 +142,11 @@ impl NetworkService { Ok(()) } + /// Get a list of all connected peers by id. + pub fn connected_peers(&self) -> Vec { + self.host.read().as_ref().map(|h| h.connected_peers()).unwrap_or_else(Vec::new) + } + /// Try to add a reserved peer. pub fn add_reserved_peer(&self, peer: &str) -> Result<(), NetworkError> { let host = self.host.read();