mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-21 22:04:44 +00:00
Altair networking (#2300)
## Issue Addressed Resolves #2278 ## Proposed Changes Implements the networking components for the Altair hard fork https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/p2p-interface.md ## Additional Info This PR acts as the base branch for networking changes and tracks https://github.com/sigp/lighthouse/pull/2279 . Changes to gossip, rpc and discovery can be separate PRs to be merged here for ease of review. Co-authored-by: realbigsean <seananderson33@gmail.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
//! Implementation of Lighthouse's peer management system.
|
||||
|
||||
pub use self::peerdb::*;
|
||||
use crate::discovery::TARGET_SUBNET_PEERS;
|
||||
use crate::rpc::{GoodbyeReason, MetaData, Protocol, RPCError, RPCResponseErrorCode};
|
||||
use crate::types::SyncState;
|
||||
use crate::{error, metrics, Gossipsub};
|
||||
use crate::{NetworkConfig, NetworkGlobals, PeerId};
|
||||
use crate::{Subnet, SubnetDiscovery};
|
||||
use discv5::Enr;
|
||||
use futures::prelude::*;
|
||||
use futures::Stream;
|
||||
@@ -19,7 +21,7 @@ use std::{
|
||||
task::{Context, Poll},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use types::{EthSpec, SubnetId};
|
||||
use types::{EthSpec, SyncSubnetId};
|
||||
|
||||
pub use libp2p::core::{identity::Keypair, Multiaddr};
|
||||
|
||||
@@ -34,7 +36,7 @@ pub use peer_info::{ConnectionDirection, PeerConnectionStatus, PeerConnectionSta
|
||||
pub use peer_sync_status::{PeerSyncStatus, SyncInfo};
|
||||
use score::{PeerAction, ReportSource, ScoreState};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::net::IpAddr;
|
||||
|
||||
/// The time in seconds between re-status's peers.
|
||||
@@ -78,6 +80,11 @@ pub struct PeerManager<TSpec: EthSpec> {
|
||||
target_peers: usize,
|
||||
/// The maximum number of peers we allow (exceptions for subnet peers)
|
||||
max_peers: usize,
|
||||
/// A collection of sync committee subnets that we need to stay subscribed to.
|
||||
/// Sync committee subnets are longer term (256 epochs). Hence, we need to re-run
|
||||
/// discovery queries for subnet peers if we disconnect from existing sync
|
||||
/// committee subnet peers.
|
||||
sync_committee_subnets: HashMap<SyncSubnetId, Instant>,
|
||||
/// The heartbeat interval to perform routine maintenance.
|
||||
heartbeat: tokio::time::Interval,
|
||||
/// Keeps track of whether the discovery service is enabled or not.
|
||||
@@ -108,6 +115,8 @@ pub enum PeerManagerEvent {
|
||||
UnBanned(PeerId, Vec<IpAddr>),
|
||||
/// Request the behaviour to discover more peers.
|
||||
DiscoverPeers,
|
||||
/// Request the behaviour to discover peers on subnets.
|
||||
DiscoverSubnetPeers(Vec<SubnetDiscovery>),
|
||||
}
|
||||
|
||||
impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
@@ -127,6 +136,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
outbound_ping_peers: HashSetDelay::new(Duration::from_secs(PING_INTERVAL_OUTBOUND)),
|
||||
status_peers: HashSetDelay::new(Duration::from_secs(STATUS_INTERVAL)),
|
||||
target_peers: config.target_peers,
|
||||
sync_committee_subnets: Default::default(),
|
||||
max_peers: (config.target_peers as f32 * (1.0 + PEER_EXCESS_FACTOR)).ceil() as usize,
|
||||
heartbeat,
|
||||
discovery_enabled: !config.disable_discovery,
|
||||
@@ -264,16 +274,16 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
}
|
||||
|
||||
/// Adds a gossipsub subscription to a peer in the peerdb.
|
||||
pub fn add_subscription(&self, peer_id: &PeerId, subnet_id: SubnetId) {
|
||||
pub fn add_subscription(&self, peer_id: &PeerId, subnet: Subnet) {
|
||||
if let Some(info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
|
||||
info.subnets.insert(subnet_id);
|
||||
info.subnets.insert(subnet);
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a gossipsub subscription to a peer in the peerdb.
|
||||
pub fn remove_subscription(&self, peer_id: &PeerId, subnet_id: SubnetId) {
|
||||
pub fn remove_subscription(&self, peer_id: &PeerId, subnet: Subnet) {
|
||||
if let Some(info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
|
||||
info.subnets.remove(&subnet_id);
|
||||
info.subnets.remove(&subnet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,6 +294,21 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert the sync subnet into list of long lived sync committee subnets that we need to
|
||||
/// maintain adequate number of peers for.
|
||||
pub fn add_sync_subnet(&mut self, subnet_id: SyncSubnetId, min_ttl: Instant) {
|
||||
match self.sync_committee_subnets.entry(subnet_id) {
|
||||
Entry::Vacant(_) => {
|
||||
self.sync_committee_subnets.insert(subnet_id, min_ttl);
|
||||
}
|
||||
Entry::Occupied(old) => {
|
||||
if *old.get() < min_ttl {
|
||||
self.sync_committee_subnets.insert(subnet_id, min_ttl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Notifications from the Swarm */
|
||||
|
||||
// A peer is being dialed.
|
||||
@@ -599,9 +624,9 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
|
||||
// if the sequence number is unknown send an update the meta data of the peer.
|
||||
if let Some(meta_data) = &peer_info.meta_data {
|
||||
if meta_data.seq_number < seq {
|
||||
if *meta_data.seq_number() < seq {
|
||||
debug!(self.log, "Requesting new metadata from peer";
|
||||
"peer_id" => %peer_id, "known_seq_no" => meta_data.seq_number, "ping_seq_no" => seq);
|
||||
"peer_id" => %peer_id, "known_seq_no" => meta_data.seq_number(), "ping_seq_no" => seq);
|
||||
self.events.push(PeerManagerEvent::MetaData(*peer_id));
|
||||
}
|
||||
} else {
|
||||
@@ -623,9 +648,9 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
|
||||
// if the sequence number is unknown send update the meta data of the peer.
|
||||
if let Some(meta_data) = &peer_info.meta_data {
|
||||
if meta_data.seq_number < seq {
|
||||
if *meta_data.seq_number() < seq {
|
||||
debug!(self.log, "Requesting new metadata from peer";
|
||||
"peer_id" => %peer_id, "known_seq_no" => meta_data.seq_number, "pong_seq_no" => seq);
|
||||
"peer_id" => %peer_id, "known_seq_no" => meta_data.seq_number(), "pong_seq_no" => seq);
|
||||
self.events.push(PeerManagerEvent::MetaData(*peer_id));
|
||||
}
|
||||
} else {
|
||||
@@ -643,19 +668,19 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
pub fn meta_data_response(&mut self, peer_id: &PeerId, meta_data: MetaData<TSpec>) {
|
||||
if let Some(peer_info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
|
||||
if let Some(known_meta_data) = &peer_info.meta_data {
|
||||
if known_meta_data.seq_number < meta_data.seq_number {
|
||||
if *known_meta_data.seq_number() < *meta_data.seq_number() {
|
||||
debug!(self.log, "Updating peer's metadata";
|
||||
"peer_id" => %peer_id, "known_seq_no" => known_meta_data.seq_number, "new_seq_no" => meta_data.seq_number);
|
||||
"peer_id" => %peer_id, "known_seq_no" => known_meta_data.seq_number(), "new_seq_no" => meta_data.seq_number());
|
||||
} else {
|
||||
debug!(self.log, "Received old metadata";
|
||||
"peer_id" => %peer_id, "known_seq_no" => known_meta_data.seq_number, "new_seq_no" => meta_data.seq_number);
|
||||
"peer_id" => %peer_id, "known_seq_no" => known_meta_data.seq_number(), "new_seq_no" => meta_data.seq_number());
|
||||
// Updating metadata even in this case to prevent storing
|
||||
// incorrect `metadata.attnets` for a peer
|
||||
// incorrect `attnets/syncnets` for a peer
|
||||
}
|
||||
} else {
|
||||
// we have no meta-data for this peer, update
|
||||
debug!(self.log, "Obtained peer's metadata";
|
||||
"peer_id" => %peer_id, "new_seq_no" => meta_data.seq_number);
|
||||
"peer_id" => %peer_id, "new_seq_no" => meta_data.seq_number());
|
||||
}
|
||||
peer_info.meta_data = Some(meta_data);
|
||||
} else {
|
||||
@@ -965,6 +990,46 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run discovery query for additional sync committee peers if we fall below `TARGET_PEERS`.
|
||||
fn maintain_sync_committee_peers(&mut self) {
|
||||
// Remove expired entries
|
||||
self.sync_committee_subnets
|
||||
.retain(|_, v| *v > Instant::now());
|
||||
|
||||
let subnets_to_discover: Vec<SubnetDiscovery> = self
|
||||
.sync_committee_subnets
|
||||
.iter()
|
||||
.filter_map(|(k, v)| {
|
||||
if self
|
||||
.network_globals
|
||||
.peers
|
||||
.read()
|
||||
.good_peers_on_subnet(Subnet::SyncCommittee(*k))
|
||||
.count()
|
||||
< TARGET_SUBNET_PEERS
|
||||
{
|
||||
Some(SubnetDiscovery {
|
||||
subnet: Subnet::SyncCommittee(*k),
|
||||
min_ttl: Some(*v),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// request the subnet query from discovery
|
||||
if !subnets_to_discover.is_empty() {
|
||||
debug!(
|
||||
self.log,
|
||||
"Making subnet queries for maintaining sync committee peers";
|
||||
"subnets" => ?subnets_to_discover.iter().map(|s| s.subnet).collect::<Vec<_>>()
|
||||
);
|
||||
self.events
|
||||
.push(PeerManagerEvent::DiscoverSubnetPeers(subnets_to_discover));
|
||||
}
|
||||
}
|
||||
|
||||
/// The Peer manager's heartbeat maintains the peer count and maintains peer reputations.
|
||||
///
|
||||
/// It will request discovery queries if the peer count has not reached the desired number of
|
||||
@@ -989,6 +1054,9 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
// Updates peer's scores.
|
||||
self.update_peer_scores();
|
||||
|
||||
// Maintain minimum count for sync committee peers.
|
||||
self.maintain_sync_committee_peers();
|
||||
|
||||
// Keep a list of peers we are disconnecting
|
||||
let mut disconnecting_peers = Vec::new();
|
||||
|
||||
@@ -1115,7 +1183,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::discovery::enr::build_enr;
|
||||
use crate::discovery::enr_ext::CombinedKeyExt;
|
||||
use crate::rpc::methods::MetaData;
|
||||
use crate::rpc::methods::{MetaData, MetaDataV2};
|
||||
use crate::Enr;
|
||||
use discv5::enr::CombinedKey;
|
||||
use slog::{o, Drain};
|
||||
@@ -1156,10 +1224,11 @@ mod tests {
|
||||
enr,
|
||||
9000,
|
||||
9000,
|
||||
MetaData {
|
||||
MetaData::V2(MetaDataV2 {
|
||||
seq_number: 0,
|
||||
attnets: Default::default(),
|
||||
},
|
||||
syncnets: Default::default(),
|
||||
}),
|
||||
vec![],
|
||||
&log,
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::client::Client;
|
||||
use super::score::{PeerAction, Score, ScoreState};
|
||||
use super::PeerSyncStatus;
|
||||
use crate::rpc::MetaData;
|
||||
use crate::Multiaddr;
|
||||
use crate::{rpc::MetaData, types::Subnet};
|
||||
use discv5::Enr;
|
||||
use serde::{
|
||||
ser::{SerializeStruct, Serializer},
|
||||
@@ -12,7 +12,7 @@ use std::collections::HashSet;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::time::Instant;
|
||||
use strum::AsRefStr;
|
||||
use types::{EthSpec, SubnetId};
|
||||
use types::EthSpec;
|
||||
use PeerConnectionStatus::*;
|
||||
|
||||
/// Information about a given connected peer.
|
||||
@@ -40,7 +40,7 @@ pub struct PeerInfo<T: EthSpec> {
|
||||
/// connection.
|
||||
pub meta_data: Option<MetaData<T>>,
|
||||
/// Subnets the peer is connected to.
|
||||
pub subnets: HashSet<SubnetId>,
|
||||
pub subnets: HashSet<Subnet>,
|
||||
/// The time we would like to retain this peer. After this time, the peer is no longer
|
||||
/// necessary.
|
||||
#[serde(skip)]
|
||||
@@ -84,17 +84,26 @@ impl<T: EthSpec> PeerInfo<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns if the peer is subscribed to a given `SubnetId` from the metadata attnets field.
|
||||
pub fn on_subnet_metadata(&self, subnet_id: SubnetId) -> bool {
|
||||
/// Returns if the peer is subscribed to a given `Subnet` from the metadata attnets/syncnets field.
|
||||
pub fn on_subnet_metadata(&self, subnet: &Subnet) -> bool {
|
||||
if let Some(meta_data) = &self.meta_data {
|
||||
return meta_data.attnets.get(*subnet_id as usize).unwrap_or(false);
|
||||
match subnet {
|
||||
Subnet::Attestation(id) => {
|
||||
return meta_data.attnets().get(**id as usize).unwrap_or(false)
|
||||
}
|
||||
Subnet::SyncCommittee(id) => {
|
||||
return meta_data
|
||||
.syncnets()
|
||||
.map_or(false, |s| s.get(**id as usize).unwrap_or(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns if the peer is subscribed to a given `SubnetId` from the gossipsub subscriptions.
|
||||
pub fn on_subnet_gossipsub(&self, subnet_id: SubnetId) -> bool {
|
||||
self.subnets.contains(&subnet_id)
|
||||
/// Returns if the peer is subscribed to a given `Subnet` from the gossipsub subscriptions.
|
||||
pub fn on_subnet_gossipsub(&self, subnet: &Subnet) -> bool {
|
||||
self.subnets.contains(subnet)
|
||||
}
|
||||
|
||||
/// Returns the seen IP addresses of the peer.
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
use super::peer_info::{ConnectionDirection, PeerConnectionStatus, PeerInfo};
|
||||
use super::peer_sync_status::PeerSyncStatus;
|
||||
use super::score::{Score, ScoreState};
|
||||
use crate::multiaddr::{Multiaddr, Protocol};
|
||||
use crate::rpc::methods::MetaData;
|
||||
use crate::Enr;
|
||||
use crate::PeerId;
|
||||
use crate::{
|
||||
multiaddr::{Multiaddr, Protocol},
|
||||
types::Subnet,
|
||||
};
|
||||
use rand::seq::SliceRandom;
|
||||
use slog::{crit, debug, error, trace, warn};
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::time::Instant;
|
||||
use types::{EthSpec, SubnetId};
|
||||
use types::EthSpec;
|
||||
|
||||
/// Max number of disconnected nodes to remember.
|
||||
const MAX_DC_PEERS: usize = 500;
|
||||
@@ -267,14 +270,14 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
||||
}
|
||||
|
||||
/// Gives an iterator of all peers on a given subnet.
|
||||
pub fn good_peers_on_subnet(&self, subnet_id: SubnetId) -> impl Iterator<Item = &PeerId> {
|
||||
pub fn good_peers_on_subnet(&self, subnet: Subnet) -> impl Iterator<Item = &PeerId> {
|
||||
self.peers
|
||||
.iter()
|
||||
.filter(move |(_, info)| {
|
||||
// We check both the metadata and gossipsub data as we only want to count long-lived subscribed peers
|
||||
info.is_connected()
|
||||
&& info.on_subnet_metadata(subnet_id)
|
||||
&& info.on_subnet_gossipsub(subnet_id)
|
||||
&& info.on_subnet_metadata(&subnet)
|
||||
&& info.on_subnet_gossipsub(&subnet)
|
||||
&& info.is_good_gossipsub_peer()
|
||||
})
|
||||
.map(|(peer_id, _)| peer_id)
|
||||
@@ -382,11 +385,11 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
||||
|
||||
/// Extends the ttl of all peers on the given subnet that have a shorter
|
||||
/// min_ttl than what's given.
|
||||
pub fn extend_peers_on_subnet(&mut self, subnet_id: SubnetId, min_ttl: Instant) {
|
||||
pub fn extend_peers_on_subnet(&mut self, subnet: &Subnet, min_ttl: Instant) {
|
||||
let log = &self.log;
|
||||
self.peers.iter_mut()
|
||||
.filter(move |(_, info)| {
|
||||
info.is_connected() && info.on_subnet_metadata(subnet_id) && info.on_subnet_gossipsub(subnet_id)
|
||||
info.is_connected() && info.on_subnet_metadata(subnet) && info.on_subnet_gossipsub(subnet)
|
||||
})
|
||||
.for_each(|(peer_id,info)| {
|
||||
if info.min_ttl.is_none() || Some(min_ttl) > info.min_ttl {
|
||||
|
||||
Reference in New Issue
Block a user