Integrate identify into peer manager (#1011)

This commit is contained in:
Age Manning 2020-04-17 16:02:14 +10:00 committed by GitHub
parent 6edb4f655c
commit 0b2b379f14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 158 additions and 17 deletions

View File

@ -19,7 +19,7 @@ use std::marker::PhantomData;
use std::sync::Arc;
use types::{EnrForkId, EthSpec, SubnetId};
const MAX_IDENTIFY_ADDRESSES: usize = 20;
const MAX_IDENTIFY_ADDRESSES: usize = 10;
/// Builds the network behaviour that manages the core protocols of eth2.
/// This core behaviour is managed by `Behaviour` which adds peer management to all core
@ -508,10 +508,13 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> NetworkBehaviourEventPr
if info.listen_addrs.len() > MAX_IDENTIFY_ADDRESSES {
debug!(
self.log,
"More than 20 addresses have been identified, truncating"
"More than 10 addresses have been identified, truncating"
);
info.listen_addrs.truncate(MAX_IDENTIFY_ADDRESSES);
}
// send peer info to the peer manager.
self.peer_manager.identify(&peer_id, &info);
debug!(self.log, "Identified Peer"; "peer" => format!("{}", peer_id),
"protocol_version" => info.protocol_version,
"agent_version" => info.agent_version,

View File

@ -0,0 +1,133 @@
//! Known Ethereum 2.0 clients and their fingerprints.
//!
//! Currently using identify to fingerprint.
use libp2p::identify::IdentifyInfo;
#[derive(Debug)]
/// Various client and protocol information related to a node.
pub struct Client {
/// The client's name (Ex: lighthouse, prism, nimbus, etc)
pub kind: ClientKind,
/// The client's version.
pub version: String,
/// The OS version of the client.
pub os_version: String,
/// The libp2p protocol version.
pub protocol_version: String,
/// Identify agent string
pub agent_string: Option<String>,
}
#[derive(Debug)]
pub enum ClientKind {
/// A lighthouse node (the best kind).
Lighthouse,
/// A Nimbus node.
Nimbus,
/// A Teku node.
Teku,
/// A Prysm node.
Prysm,
/// An unknown client.
Unknown,
}
impl Default for Client {
fn default() -> Self {
Client {
kind: ClientKind::Unknown,
version: "unknown".into(),
os_version: "unknown".into(),
protocol_version: "unknown".into(),
agent_string: None,
}
}
}
impl Client {
/// Builds a `Client` from `IdentifyInfo`.
pub fn from_identify_info(info: &IdentifyInfo) -> Self {
let (kind, version, os_version) = client_from_agent_version(&info.agent_version);
Client {
kind,
version,
os_version,
protocol_version: info.protocol_version.clone(),
agent_string: Some(info.agent_version.clone()),
}
}
}
impl std::fmt::Display for Client {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.kind {
ClientKind::Lighthouse => write!(
f,
"Lighthouse: version: {}, os_version: {}",
self.version, self.os_version
),
ClientKind::Teku => write!(
f,
"Teku: version: {}, os_version: {}",
self.version, self.os_version
),
ClientKind::Nimbus => write!(
f,
"Nimbus: version: {}, os_version: {}",
self.version, self.os_version
),
ClientKind::Prysm => write!(
f,
"Prysm: version: {}, os_version: {}",
self.version, self.os_version
),
ClientKind::Unknown => {
if let Some(agent_string) = &self.agent_string {
write!(f, "Unknown: {}", agent_string)
} else {
write!(f, "Unknown")
}
}
}
}
}
// helper function to identify clients from their agent_version. Returns the client
// kind and it's associated version and the OS kind.
fn client_from_agent_version(agent_version: &str) -> (ClientKind, String, String) {
let mut agent_split = agent_version.split("/");
match agent_split.next() {
Some("Lighthouse") => {
let kind = ClientKind::Lighthouse;
let mut version = String::from("unknown");
let mut os_version = version.clone();
if let Some(agent_version) = agent_split.next() {
version = agent_version.into();
if let Some(agent_os_version) = agent_split.next() {
os_version = agent_os_version.into();
}
}
(kind, version, os_version)
}
Some("teku") => {
let kind = ClientKind::Teku;
let mut version = String::from("unknown");
let mut os_version = version.clone();
if let Some(_) = agent_split.next() {
if let Some(agent_version) = agent_split.next() {
version = agent_version.into();
if let Some(agent_os_version) = agent_split.next() {
os_version = agent_os_version.into();
}
}
}
(kind, version, os_version)
}
_ => {
let unknown = String::from("unknown");
(ClientKind::Unknown, unknown.clone(), unknown)
}
}
}

View File

@ -7,12 +7,14 @@ use crate::{NetworkGlobals, PeerId};
use futures::prelude::*;
use futures::Stream;
use hashmap_delay::HashSetDelay;
use libp2p::identify::IdentifyInfo;
use slog::{crit, debug, error, warn};
use smallvec::SmallVec;
use std::sync::Arc;
use std::time::{Duration, Instant};
use types::EthSpec;
mod client;
mod peer_info;
mod peerdb;
@ -242,6 +244,16 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
self.update_reputations();
}
/// Updates `PeerInfo` with `identify` information.
pub fn identify(&mut self, peer_id: &PeerId, info: &IdentifyInfo) {
if let Some(peer_info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
peer_info.client = client::Client::from_identify_info(info);
peer_info.listening_addresses = info.listen_addrs.clone();
} else {
crit!(self.log, "Received an Identify response from an unknown peer"; "peer_id" => format!("{}", peer_id));
}
}
/* Internal functions */
/// Registers a peer as connected. The `ingoing` parameter determines if the peer is being

View File

@ -1,5 +1,7 @@
use super::client::Client;
use super::peerdb::{Rep, DEFAULT_REPUTATION};
use crate::rpc::MetaData;
use crate::Multiaddr;
use std::time::Instant;
use types::{EthSpec, Slot, SubnetId};
use PeerConnectionStatus::*;
@ -12,9 +14,11 @@ pub struct PeerInfo<T: EthSpec> {
/// The peers reputation
pub reputation: Rep,
/// Client managing this peer
_client: Client,
pub client: Client,
/// Connection status of this peer
pub connection_status: PeerConnectionStatus,
/// The known listening addresses of this peer.
pub listening_addresses: Vec<Multiaddr>,
/// The current syncing state of the peer. The state may be determined after it's initial
/// connection.
pub sync_status: PeerSyncStatus,
@ -26,13 +30,11 @@ pub struct PeerInfo<T: EthSpec> {
impl<TSpec: EthSpec> Default for PeerInfo<TSpec> {
fn default() -> PeerInfo<TSpec> {
PeerInfo {
reputation: DEFAULT_REPUTATION,
_status: Default::default(),
_client: Client {
_client_name: "Unknown".into(),
_version: vec![0],
},
reputation: DEFAULT_REPUTATION,
client: Client::default(),
connection_status: Default::default(),
listening_addresses: vec![],
sync_status: PeerSyncStatus::Unknown,
meta_data: None,
}
@ -66,15 +68,6 @@ impl Default for PeerStatus {
}
}
/// Representation of the client managing a peer
#[derive(Debug)]
pub struct Client {
/// The client's name (Ex: lighthouse, prism, nimbus, etc)
_client_name: String,
/// The client's version
_version: Vec<u8>,
}
/// Connection Status of the peer
#[derive(Debug, Clone)]
pub enum PeerConnectionStatus {