From 9e42a851e4d7e3bf31bdffce79c8adc721bc4992 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Tue, 21 Apr 2020 23:27:49 +1000 Subject: [PATCH] Adds peers and connected_peers to lighthouse http API (#1030) --- .../eth2-libp2p/src/peer_manager/client.rs | 10 +- .../eth2-libp2p/src/peer_manager/peer_info.rs | 44 +++++- .../eth2-libp2p/src/peer_manager/peerdb.rs | 18 ++- beacon_node/eth2-libp2p/src/rpc/methods.rs | 4 +- beacon_node/eth2-libp2p/src/types/globals.rs | 2 +- beacon_node/rest_api/src/lighthouse.rs | 51 ++++++- beacon_node/rest_api/src/network.rs | 2 +- beacon_node/rest_api/src/router.rs | 8 +- book/src/http/lighthouse.md | 128 ++++++++++++++++++ 9 files changed, 249 insertions(+), 18 deletions(-) diff --git a/beacon_node/eth2-libp2p/src/peer_manager/client.rs b/beacon_node/eth2-libp2p/src/peer_manager/client.rs index 3eea0707f0..3ba68faaa3 100644 --- a/beacon_node/eth2-libp2p/src/peer_manager/client.rs +++ b/beacon_node/eth2-libp2p/src/peer_manager/client.rs @@ -3,9 +3,10 @@ //! Currently using identify to fingerprint. use libp2p::identify::IdentifyInfo; +use serde::Serialize; -#[derive(Debug)] /// Various client and protocol information related to a node. +#[derive(Clone, Debug, Serialize)] pub struct Client { /// The client's name (Ex: lighthouse, prism, nimbus, etc) pub kind: ClientKind, @@ -19,7 +20,7 @@ pub struct Client { pub agent_string: Option, } -#[derive(Debug)] +#[derive(Clone, Debug, Serialize)] pub enum ClientKind { /// A lighthouse node (the best kind). Lighthouse, @@ -125,6 +126,11 @@ fn client_from_agent_version(agent_version: &str) -> (ClientKind, String, String } (kind, version, os_version) } + Some("github.com") => { + let kind = ClientKind::Prysm; + let unknown = String::from("unknown"); + (kind, unknown.clone(), unknown) + } _ => { let unknown = String::from("unknown"); (ClientKind::Unknown, unknown.clone(), unknown) 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 b9e817f8c3..f81ddcc6af 100644 --- a/beacon_node/eth2-libp2p/src/peer_manager/peer_info.rs +++ b/beacon_node/eth2-libp2p/src/peer_manager/peer_info.rs @@ -2,12 +2,17 @@ use super::client::Client; use super::peerdb::{Rep, DEFAULT_REPUTATION}; use crate::rpc::MetaData; use crate::Multiaddr; +use serde::{ + ser::{SerializeStructVariant, Serializer}, + Serialize, +}; use std::time::Instant; use types::{EthSpec, Slot, SubnetId}; use PeerConnectionStatus::*; /// Information about a given connected peer. -#[derive(Debug)] +#[derive(Clone, Debug, Serialize)] +#[serde(bound = "T: EthSpec")] pub struct PeerInfo { /// The connection status of the peer _status: PeerStatus, @@ -54,7 +59,7 @@ impl PeerInfo { } } -#[derive(Debug)] +#[derive(Clone, Debug, Serialize)] pub enum PeerStatus { /// The peer is healthy Healthy, @@ -69,7 +74,7 @@ impl Default for PeerStatus { } /// Connection Status of the peer -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub enum PeerConnectionStatus { Connected { /// number of ingoing connections @@ -86,12 +91,41 @@ pub enum PeerConnectionStatus { since: Instant, }, Unknown { - /// time since we know of this peer + /// time since we last saw this peer since: Instant, }, } -#[derive(Debug, Clone, PartialEq)] +/// Serialization for http requests. +impl Serialize for PeerConnectionStatus { + fn serialize(&self, serializer: S) -> Result { + match self { + Connected { n_in, n_out } => { + let mut s = serializer.serialize_struct_variant("", 0, "Connected", 2)?; + s.serialize_field("in", n_in)?; + s.serialize_field("out", n_out)?; + s.end() + } + Disconnected { since } => { + let mut s = serializer.serialize_struct_variant("", 1, "Disconnected", 1)?; + s.serialize_field("since", &since.elapsed().as_secs())?; + s.end() + } + Banned { since } => { + let mut s = serializer.serialize_struct_variant("", 2, "Banned", 1)?; + s.serialize_field("since", &since.elapsed().as_secs())?; + s.end() + } + Unknown { since } => { + let mut s = serializer.serialize_struct_variant("", 3, "Unknown", 1)?; + s.serialize_field("since", &since.elapsed().as_secs())?; + s.end() + } + } + } +} + +#[derive(Clone, Debug, Serialize)] pub enum PeerSyncStatus { /// At the current state as our node or ahead of us. Synced { diff --git a/beacon_node/eth2-libp2p/src/peer_manager/peerdb.rs b/beacon_node/eth2-libp2p/src/peer_manager/peerdb.rs index 1523aa676d..3af9f4b61d 100644 --- a/beacon_node/eth2-libp2p/src/peer_manager/peerdb.rs +++ b/beacon_node/eth2-libp2p/src/peer_manager/peerdb.rs @@ -41,8 +41,13 @@ impl PeerDB { .map_or(DEFAULT_REPUTATION, |info| info.reputation) } + /// Returns an iterator over all peers in the db. + pub fn peers(&self) -> impl Iterator)> { + self.peers.iter() + } + /// Gives the ids of all known peers. - pub fn peers(&self) -> impl Iterator { + pub fn peer_ids(&self) -> impl Iterator { self.peers.keys() } @@ -66,7 +71,14 @@ impl PeerDB { } /// Gives the ids of all known connected peers. - pub fn connected_peers(&self) -> impl Iterator { + pub fn connected_peers(&self) -> impl Iterator)> { + self.peers + .iter() + .filter(|(_, info)| info.connection_status.is_connected()) + } + + /// Gives the ids of all known connected peers. + pub fn connected_peer_ids(&self) -> impl Iterator { self.peers .iter() .filter(|(_, info)| info.connection_status.is_connected()) @@ -373,7 +385,7 @@ mod tests { } assert_eq!(pdb.n_dc, 0); - for p in pdb.connected_peers().cloned().collect::>() { + for p in pdb.connected_peer_ids().cloned().collect::>() { pdb.disconnect(&p); } diff --git a/beacon_node/eth2-libp2p/src/rpc/methods.rs b/beacon_node/eth2-libp2p/src/rpc/methods.rs index fbfecaad2d..c9e86d3ecd 100644 --- a/beacon_node/eth2-libp2p/src/rpc/methods.rs +++ b/beacon_node/eth2-libp2p/src/rpc/methods.rs @@ -1,6 +1,7 @@ //! Available RPC methods types and ids. use crate::types::EnrBitfield; +use serde::Serialize; use ssz_derive::{Decode, Encode}; use types::{Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot}; @@ -37,7 +38,8 @@ pub struct Ping { } /// The METADATA response structure. -#[derive(Encode, Decode, Clone, Debug, PartialEq)] +#[derive(Encode, Decode, Clone, Debug, PartialEq, Serialize)] +#[serde(bound = "T: EthSpec")] pub struct MetaData { /// A sequential counter indicating when data gets modified. pub seq_number: u64, diff --git a/beacon_node/eth2-libp2p/src/types/globals.rs b/beacon_node/eth2-libp2p/src/types/globals.rs index 0be9d4cb44..33770a65eb 100644 --- a/beacon_node/eth2-libp2p/src/types/globals.rs +++ b/beacon_node/eth2-libp2p/src/types/globals.rs @@ -80,7 +80,7 @@ impl NetworkGlobals { /// Returns the number of libp2p connected peers. pub fn connected_peers(&self) -> usize { - self.peers.read().connected_peers().count() + self.peers.read().connected_peer_ids().count() } /// Returns in the node is syncing. diff --git a/beacon_node/rest_api/src/lighthouse.rs b/beacon_node/rest_api/src/lighthouse.rs index 48a64e3fba..556046ab37 100644 --- a/beacon_node/rest_api/src/lighthouse.rs +++ b/beacon_node/rest_api/src/lighthouse.rs @@ -2,12 +2,57 @@ use crate::response_builder::ResponseBuilder; use crate::ApiResult; -use eth2_libp2p::NetworkGlobals; +use eth2_libp2p::{NetworkGlobals, PeerInfo}; use hyper::{Body, Request}; +use serde::Serialize; use std::sync::Arc; use types::EthSpec; /// The syncing state of the beacon node. -pub fn syncing(req: Request, network: Arc>) -> ApiResult { - ResponseBuilder::new(&req)?.body_no_ssz(&network.sync_state()) +pub fn syncing( + req: Request, + network_globals: Arc>, +) -> ApiResult { + ResponseBuilder::new(&req)?.body_no_ssz(&network_globals.sync_state()) +} + +/// Returns all known peers and corresponding information +pub fn peers(req: Request, network_globals: Arc>) -> ApiResult { + let peers: Vec> = network_globals + .peers + .read() + .peers() + .map(|(peer_id, peer_info)| Peer { + peer_id: peer_id.to_string(), + peer_info: peer_info.clone(), + }) + .collect(); + ResponseBuilder::new(&req)?.body_no_ssz(&peers) +} + +/// Returns all known connected peers and their corresponding information +pub fn connected_peers( + req: Request, + network_globals: Arc>, +) -> ApiResult { + let peers: Vec> = network_globals + .peers + .read() + .connected_peers() + .map(|(peer_id, peer_info)| Peer { + peer_id: peer_id.to_string(), + peer_info: peer_info.clone(), + }) + .collect(); + ResponseBuilder::new(&req)?.body_no_ssz(&peers) +} + +/// Information returned by `peers` and `connected_peers`. +#[derive(Clone, Debug, Serialize)] +#[serde(bound = "T: EthSpec")] +struct Peer { + /// The Peer's ID + peer_id: String, + /// The PeerInfo associated with the peer. + peer_info: PeerInfo, } diff --git a/beacon_node/rest_api/src/network.rs b/beacon_node/rest_api/src/network.rs index 43b48b2a12..ae2486d341 100644 --- a/beacon_node/rest_api/src/network.rs +++ b/beacon_node/rest_api/src/network.rs @@ -65,7 +65,7 @@ pub fn get_peer_list( let connected_peers: Vec = network .peers .read() - .connected_peers() + .connected_peer_ids() .map(PeerId::to_string) .collect(); ResponseBuilder::new(&req)?.body_no_ssz(&connected_peers) diff --git a/beacon_node/rest_api/src/router.rs b/beacon_node/rest_api/src/router.rs index ee391ae0db..1c86e8ebc7 100644 --- a/beacon_node/rest_api/src/router.rs +++ b/beacon_node/rest_api/src/router.rs @@ -203,7 +203,6 @@ pub fn route( (&Method::GET, "/advanced/operation_pool") => { into_boxfut(advanced::get_operation_pool::(req, beacon_chain)) } - (&Method::GET, "/metrics") => into_boxfut(metrics::get_prometheus::( req, beacon_chain, @@ -215,7 +214,12 @@ pub fn route( (&Method::GET, "/lighthouse/syncing") => { into_boxfut(lighthouse::syncing::(req, network_globals)) } - + (&Method::GET, "/lighthouse/peers") => { + into_boxfut(lighthouse::peers::(req, network_globals)) + } + (&Method::GET, "/lighthouse/connected_peers") => into_boxfut( + lighthouse::connected_peers::(req, network_globals), + ), _ => Box::new(futures::future::err(ApiError::NotFound( "Request path and/or method not found.".to_owned(), ))), diff --git a/book/src/http/lighthouse.md b/book/src/http/lighthouse.md index cc6ec2a119..d80c0f694a 100644 --- a/book/src/http/lighthouse.md +++ b/book/src/http/lighthouse.md @@ -7,6 +7,8 @@ The `/lighthouse` endpoints provide lighthouse-specific information about the be HTTP Path | Description | | --- | -- | [`/lighthouse/syncing`](#lighthousesyncing) | Get the node's syncing status +[`/lighthouse/peers`](#lighthousepeers) | Get the peers info known by the beacon node +[`/lighthouse/connected_peers`](#lighthousepeers) | Get the connected_peers known by the beacon node ## `/lighthouse/syncing` @@ -52,3 +54,129 @@ If the node is synced "Synced" } ``` + +## `/lighthouse/peers` + +Get all known peers info from the beacon node. + +### HTTP Specification + +| Property | Specification | +| --- |--- | +Path | `/lighthouse/peers` +Method | GET +JSON Encoding | Object +Query Parameters | None +Typical Responses | 200 + +### Example Response + +```json +[ +{ + "peer_id" : "16Uiu2HAmTEinipUS3haxqucrn7d7SmCKx5XzAVbAZCiNW54ncynG", + "peer_info" : { + "_status" : "Healthy", + "client" : { + "agent_string" : "github.com/libp2p/go-libp2p", + "kind" : "Prysm", + "os_version" : "unknown", + "protocol_version" : "ipfs/0.1.0", + "version" : "unknown" + }, + "connection_status" : { + "Disconnected" : { + "since" : 3 + } + }, + "listening_addresses" : [ + "/ip4/10.3.58.241/tcp/9001", + "/ip4/35.172.14.146/tcp/9001", + "/ip4/35.172.14.146/tcp/9001" + ], + "meta_data" : { + "attnets" : "0x0000000000000000", + "seq_number" : 0 + }, + "reputation" : 20, + "sync_status" : { + "Synced" : { + "status_head_slot" : 18146 + } + } + } + }, + { + "peer_id" : "16Uiu2HAm8XZfPv3YjktCjitSRtfS7UfHfEvpiUyHrdiX6uAD55xZ", + "peer_info" : { + "_status" : "Healthy", + "client" : { + "agent_string" : null, + "kind" : "Unknown", + "os_version" : "unknown", + "protocol_version" : "unknown", + "version" : "unknown" + }, + "connection_status" : { + "Disconnected" : { + "since" : 5 + } + }, + "listening_addresses" : [], + "meta_data" : { + "attnets" : "0x0900000000000000", + "seq_number" : 0 + }, + "reputation" : 20, + "sync_status" : "Unknown" + } + }, +] +``` + +## `/lighthouse/connected_peers` + +Get all known peers info from the beacon node. + +### HTTP Specification + +| Property | Specification | +| --- |--- | +Path | `/lighthouse/connected_peers` +Method | GET +JSON Encoding | Object +Query Parameters | None +Typical Responses | 200 + +### Example Response + +```json +[ + { + "peer_id" : "16Uiu2HAm8XZfPv3YjktCjitSRtfS7UfHfEvpiUyHrdiX6uAD55xZ", + "peer_info" : { + "_status" : "Healthy", + "client" : { + "agent_string" : null, + "kind" : "Unknown", + "os_version" : "unknown", + "protocol_version" : "unknown", + "version" : "unknown" + }, + "connection_status" : { + "Connected" : { + "in" : 5, + "out" : 2 + } + }, + "listening_addresses" : [], + "meta_data" : { + "attnets" : "0x0900000000000000", + "seq_number" : 0 + }, + "reputation" : 20, + "sync_status" : "Unknown" + } + }, + ] +```