Discover query grouping (#1364)
## Issue Addressed #1281 ## Proposed Changes Groups queries for specific subnets into groups of up to 3. ## Additional Info
This commit is contained in:
parent
9ae9df806c
commit
09b40b7a5e
@ -43,7 +43,7 @@ rand = "0.7.3"
|
||||
git = "https://github.com/sigp/rust-libp2p"
|
||||
rev = "147bb43fa56c1b84253606eabedb0794eeed8b94"
|
||||
default-features = false
|
||||
features = ["websocket", "identify", "mplex", "yamux", "noise", "gossipsub", "dns", "secio", "tcp-tokio"]
|
||||
features = ["websocket", "identify", "mplex", "yamux", "noise", "gossipsub", "dns", "secio", "tcp-tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "0.2.21", features = ["full"] }
|
||||
|
@ -19,13 +19,13 @@ use slog::{crit, debug, info, warn};
|
||||
use ssz::{Decode, Encode};
|
||||
use ssz_types::BitVector;
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
collections::{HashMap, VecDeque},
|
||||
net::SocketAddr,
|
||||
path::Path,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
time::Instant,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
use types::{EnrForkId, EthSpec, SubnetId};
|
||||
@ -37,33 +37,50 @@ use subnet_predicate::subnet_predicate;
|
||||
pub const ENR_FILENAME: &str = "enr.dat";
|
||||
/// Target number of peers we'd like to have connected to a given long-lived subnet.
|
||||
const TARGET_SUBNET_PEERS: usize = 3;
|
||||
/// Number of times to attempt a discovery request
|
||||
/// Target number of peers to search for given a grouped subnet query.
|
||||
const TARGET_PEERS_FOR_GROUPED_QUERY: usize = 6;
|
||||
/// Number of times to attempt a discovery request.
|
||||
const MAX_DISCOVERY_RETRY: usize = 3;
|
||||
/// The maximum number of concurrent discovery queries.
|
||||
const MAX_CONCURRENT_QUERIES: usize = 1;
|
||||
const MAX_CONCURRENT_QUERIES: usize = 2;
|
||||
/// The max number of subnets to search for in a single subnet discovery query.
|
||||
const MAX_SUBNETS_IN_QUERY: usize = 3;
|
||||
/// The number of closest peers to search for when doing a regular peer search.
|
||||
///
|
||||
/// We could reduce this constant to speed up queries however at the cost of security. It will
|
||||
/// make it easier to peers to eclipse this node. Kademlia suggests a value of 16.
|
||||
const FIND_NODE_QUERY_CLOSEST_PEERS: usize = 16;
|
||||
/// The threshold for updating `min_ttl` on a connected peer.
|
||||
const DURATION_DIFFERENCE: Duration = Duration::from_millis(1);
|
||||
|
||||
/// The events emitted by polling discovery.
|
||||
pub enum DiscoveryEvent {
|
||||
/// A query has completed. The first parameter is the `min_ttl` of the peers if it is specified
|
||||
/// and the second parameter are the discovered peers.
|
||||
QueryResult(Option<Instant>, Vec<Enr>),
|
||||
/// A query has completed. This result contains a mapping of discovered peer IDs to the `min_ttl`
|
||||
/// of the peer if it is specified.
|
||||
QueryResult(HashMap<PeerId, Option<Instant>>),
|
||||
/// This indicates that our local UDP socketaddr has been updated and we should inform libp2p.
|
||||
SocketUpdated(SocketAddr),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct SubnetQuery {
|
||||
subnet_id: SubnetId,
|
||||
min_ttl: Option<Instant>,
|
||||
retries: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum QueryType {
|
||||
/// We are searching for subnet peers.
|
||||
Subnet {
|
||||
subnet_id: SubnetId,
|
||||
min_ttl: Option<Instant>,
|
||||
retries: usize,
|
||||
},
|
||||
Subnet(SubnetQuery),
|
||||
/// We are searching for more peers without ENR or time constraints.
|
||||
FindPeers,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum GroupedQueryType {
|
||||
/// We are searching for peers on one of a few subnets.
|
||||
Subnet(Vec<SubnetQuery>),
|
||||
/// We are searching for more peers without ENR or time constraints.
|
||||
FindPeers,
|
||||
}
|
||||
@ -73,30 +90,19 @@ impl QueryType {
|
||||
pub fn expired(&self) -> bool {
|
||||
match self {
|
||||
Self::FindPeers => false,
|
||||
Self::Subnet { min_ttl, .. } => {
|
||||
if let Some(ttl) = min_ttl {
|
||||
ttl < &Instant::now()
|
||||
Self::Subnet(subnet_query) => {
|
||||
if let Some(ttl) = subnet_query.min_ttl {
|
||||
ttl < Instant::now()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the min_ttl of the query if one exists
|
||||
///
|
||||
/// This is required for returning to the peer manager. The peer manager will update newly
|
||||
/// connected peers with this `min_ttl`
|
||||
pub fn min_ttl(&self) -> Option<Instant> {
|
||||
match self {
|
||||
Self::FindPeers => None,
|
||||
Self::Subnet { min_ttl, .. } => *min_ttl,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of a query.
|
||||
struct QueryResult(QueryType, Result<Vec<Enr>, discv5::QueryError>);
|
||||
struct QueryResult(GroupedQueryType, Result<Vec<Enr>, discv5::QueryError>);
|
||||
|
||||
// Awaiting the event stream future
|
||||
enum EventStream {
|
||||
@ -381,18 +387,13 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
|
||||
// already exists
|
||||
let mut found = false;
|
||||
for query in self.queued_queries.iter_mut() {
|
||||
if let QueryType::Subnet {
|
||||
subnet_id: ref mut q_subnet_id,
|
||||
min_ttl: ref mut q_min_ttl,
|
||||
retries: ref mut q_retries,
|
||||
} = query
|
||||
{
|
||||
if *q_subnet_id == subnet_id {
|
||||
if *q_min_ttl < min_ttl {
|
||||
*q_min_ttl = min_ttl;
|
||||
if let QueryType::Subnet(ref mut subnet_query) = query {
|
||||
if subnet_query.subnet_id == subnet_id {
|
||||
if subnet_query.min_ttl < min_ttl {
|
||||
subnet_query.min_ttl = min_ttl;
|
||||
}
|
||||
// update the number of retries
|
||||
*q_retries = retries;
|
||||
subnet_query.retries = retries;
|
||||
// mimic an `Iter::Find()` and short-circuit the loop
|
||||
found = true;
|
||||
break;
|
||||
@ -401,11 +402,11 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
|
||||
}
|
||||
if !found {
|
||||
// Set up the query and add it to the queue
|
||||
let query = QueryType::Subnet {
|
||||
let query = QueryType::Subnet(SubnetQuery {
|
||||
subnet_id,
|
||||
min_ttl,
|
||||
retries,
|
||||
};
|
||||
});
|
||||
// update the metrics and insert into the queue.
|
||||
debug!(self.log, "Queuing subnet query"; "subnet" => *subnet_id, "retries" => retries);
|
||||
self.queued_queries.push_back(query);
|
||||
@ -420,28 +421,46 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
|
||||
// Sanitize the queue, removing any out-dated subnet queries
|
||||
self.queued_queries.retain(|query| !query.expired());
|
||||
|
||||
// use this to group subnet queries together for a single discovery request
|
||||
let mut subnet_queries: Vec<SubnetQuery> = Vec::new();
|
||||
|
||||
// Check that we are within our query concurrency limit
|
||||
while !self.at_capacity() && !self.queued_queries.is_empty() {
|
||||
// consume and process the query queue
|
||||
match self.queued_queries.pop_front() {
|
||||
Some(QueryType::FindPeers) => {
|
||||
// Only permit one FindPeers query at a time
|
||||
if self.find_peer_active {
|
||||
// Only start a find peers query if it is the last message in the queue.
|
||||
// We want to prioritize subnet queries, so we don't miss attestations.
|
||||
if self.queued_queries.is_empty() {
|
||||
// This is a regular request to find additional peers
|
||||
debug!(self.log, "Discovery query started");
|
||||
self.find_peer_active = true;
|
||||
self.start_query(
|
||||
GroupedQueryType::FindPeers,
|
||||
FIND_NODE_QUERY_CLOSEST_PEERS,
|
||||
|_| true,
|
||||
);
|
||||
} else {
|
||||
self.queued_queries.push_back(QueryType::FindPeers);
|
||||
continue;
|
||||
}
|
||||
// This is a regular request to find additional peers
|
||||
debug!(self.log, "Discovery query started");
|
||||
self.find_peer_active = true;
|
||||
self.start_query(QueryType::FindPeers, FIND_NODE_QUERY_CLOSEST_PEERS);
|
||||
}
|
||||
Some(QueryType::Subnet {
|
||||
subnet_id,
|
||||
min_ttl,
|
||||
retries,
|
||||
}) => {
|
||||
// This query is for searching for peers of a particular subnet
|
||||
self.start_subnet_query(subnet_id, min_ttl, retries);
|
||||
Some(QueryType::Subnet(subnet_query)) => {
|
||||
subnet_queries.push(subnet_query);
|
||||
|
||||
// We want to start a grouped subnet query if:
|
||||
// 1. We've grouped MAX_SUBNETS_IN_QUERY subnets together.
|
||||
// 2. There are no more messages in the queue.
|
||||
// 3. There is exactly one message in the queue and it is FindPeers.
|
||||
if subnet_queries.len() == MAX_SUBNETS_IN_QUERY
|
||||
|| self.queued_queries.is_empty()
|
||||
|| (self.queued_queries.front() == Some(&QueryType::FindPeers)
|
||||
&& self.queued_queries.len() == 1)
|
||||
{
|
||||
// This query is for searching for peers of a particular subnet
|
||||
// Drain subnet_queries so we can re-use it as we continue to process the queue
|
||||
let grouped_queries: Vec<SubnetQuery> = subnet_queries.drain(..).collect();
|
||||
self.start_subnet_query(grouped_queries);
|
||||
}
|
||||
}
|
||||
None => {} // Queue is empty
|
||||
}
|
||||
@ -456,47 +475,57 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
|
||||
self.active_queries.len() >= MAX_CONCURRENT_QUERIES
|
||||
}
|
||||
|
||||
/// Runs a discovery request for a given subnet_id if one already exists.
|
||||
fn start_subnet_query(
|
||||
&mut self,
|
||||
subnet_id: SubnetId,
|
||||
min_ttl: Option<Instant>,
|
||||
retries: usize,
|
||||
) {
|
||||
// Determine if we have sufficient peers, which may make this discovery unnecessary.
|
||||
let peers_on_subnet = self
|
||||
.network_globals
|
||||
.peers
|
||||
.read()
|
||||
.peers_on_subnet(subnet_id)
|
||||
.count();
|
||||
/// Runs a discovery request for a given group of subnets.
|
||||
fn start_subnet_query(&mut self, subnet_queries: Vec<SubnetQuery>) {
|
||||
let mut filtered_subnet_ids: Vec<SubnetId> = Vec::new();
|
||||
|
||||
if peers_on_subnet > TARGET_SUBNET_PEERS {
|
||||
debug!(self.log, "Discovery ignored";
|
||||
"reason" => "Already connected to desired peers",
|
||||
"connected_peers_on_subnet" => peers_on_subnet,
|
||||
"target_subnet_peers" => TARGET_SUBNET_PEERS,
|
||||
// find subnet queries that are still necessary
|
||||
let filtered_subnet_queries: Vec<SubnetQuery> = subnet_queries
|
||||
.into_iter()
|
||||
.filter(|subnet_query| {
|
||||
// Determine if we have sufficient peers, which may make this discovery unnecessary.
|
||||
let peers_on_subnet = self
|
||||
.network_globals
|
||||
.peers
|
||||
.read()
|
||||
.peers_on_subnet(subnet_query.subnet_id)
|
||||
.count();
|
||||
|
||||
if peers_on_subnet > TARGET_SUBNET_PEERS {
|
||||
debug!(self.log, "Discovery ignored";
|
||||
"reason" => "Already connected to desired peers",
|
||||
"connected_peers_on_subnet" => peers_on_subnet,
|
||||
"target_subnet_peers" => TARGET_SUBNET_PEERS,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
let target_peers = TARGET_SUBNET_PEERS - peers_on_subnet;
|
||||
debug!(self.log, "Discovery query started for subnet";
|
||||
"subnet_id" => *subnet_query.subnet_id,
|
||||
"connected_peers_on_subnet" => peers_on_subnet,
|
||||
"target_subnet_peers" => TARGET_SUBNET_PEERS,
|
||||
"peers_to_find" => target_peers,
|
||||
"attempt" => subnet_query.retries,
|
||||
"min_ttl" => format!("{:?}", subnet_query.min_ttl),
|
||||
);
|
||||
|
||||
filtered_subnet_ids.push(subnet_query.subnet_id);
|
||||
true
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Only start a discovery query if we have a subnet to look for.
|
||||
if !filtered_subnet_queries.is_empty() {
|
||||
// build the subnet predicate as a combination of the eth2_fork_predicate and the subnet predicate
|
||||
let subnet_predicate = subnet_predicate::<TSpec>(filtered_subnet_ids, &self.log);
|
||||
|
||||
self.start_query(
|
||||
GroupedQueryType::Subnet(filtered_subnet_queries),
|
||||
TARGET_PEERS_FOR_GROUPED_QUERY,
|
||||
subnet_predicate,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let target_peers = TARGET_SUBNET_PEERS - peers_on_subnet;
|
||||
debug!(self.log, "Discovery query started for subnet";
|
||||
"subnet_id" => *subnet_id,
|
||||
"connected_peers_on_subnet" => peers_on_subnet,
|
||||
"target_subnet_peers" => TARGET_SUBNET_PEERS,
|
||||
"peers_to_find" => target_peers,
|
||||
"attempt" => retries,
|
||||
"min_ttl" => format!("{:?}", min_ttl),
|
||||
);
|
||||
|
||||
// start the query, and update the queries map if necessary
|
||||
let query = QueryType::Subnet {
|
||||
subnet_id,
|
||||
min_ttl,
|
||||
retries,
|
||||
};
|
||||
self.start_query(query, target_peers);
|
||||
}
|
||||
|
||||
/// Search for a specified number of new peers using the underlying discovery mechanism.
|
||||
@ -504,7 +533,26 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
|
||||
/// This can optionally search for peers for a given predicate. Regardless of the predicate
|
||||
/// given, this will only search for peers on the same enr_fork_id as specified in the local
|
||||
/// ENR.
|
||||
fn start_query(&mut self, query: QueryType, target_peers: usize) {
|
||||
fn start_query(
|
||||
&mut self,
|
||||
grouped_query: GroupedQueryType,
|
||||
target_peers: usize,
|
||||
additional_predicate: impl Fn(&Enr) -> bool + Send + 'static,
|
||||
) {
|
||||
// Make sure there are subnet queries included
|
||||
let contains_queries = match &grouped_query {
|
||||
GroupedQueryType::Subnet(queries) => !queries.is_empty(),
|
||||
GroupedQueryType::FindPeers => true,
|
||||
};
|
||||
|
||||
if !contains_queries {
|
||||
debug!(
|
||||
self.log,
|
||||
"No subnets included in this request. Skipping discovery request."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate a random target node id.
|
||||
let random_node = NodeId::random();
|
||||
|
||||
@ -519,31 +567,24 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
|
||||
let eth2_fork_predicate = move |enr: &Enr| enr.eth2() == Ok(enr_fork_id.clone());
|
||||
|
||||
// General predicate
|
||||
let predicate: Box<dyn Fn(&Enr) -> bool + Send> = match &query {
|
||||
QueryType::FindPeers => Box::new(eth2_fork_predicate),
|
||||
QueryType::Subnet { subnet_id, .. } => {
|
||||
// build the subnet predicate as a combination of the eth2_fork_predicate and the
|
||||
// subnet predicate
|
||||
let subnet_predicate = subnet_predicate::<TSpec>(*subnet_id, &self.log);
|
||||
Box::new(move |enr: &Enr| eth2_fork_predicate(enr) && subnet_predicate(enr))
|
||||
}
|
||||
};
|
||||
let predicate: Box<dyn Fn(&Enr) -> bool + Send> =
|
||||
Box::new(move |enr: &Enr| eth2_fork_predicate(enr) && additional_predicate(enr));
|
||||
|
||||
// Build the future
|
||||
let query_future = self
|
||||
.discv5
|
||||
.find_node_predicate(random_node, predicate, target_peers)
|
||||
.map(|v| QueryResult(query, v));
|
||||
.map(|v| QueryResult(grouped_query, v));
|
||||
|
||||
// Add the future to active queries, to be executed.
|
||||
self.active_queries.push(Box::pin(query_future));
|
||||
}
|
||||
|
||||
/// Drives the queries returning any results from completed queries.
|
||||
fn poll_queries(&mut self, cx: &mut Context) -> Option<(Option<Instant>, Vec<Enr>)> {
|
||||
fn poll_queries(&mut self, cx: &mut Context) -> Option<HashMap<PeerId, Option<Instant>>> {
|
||||
while let Poll::Ready(Some(query_future)) = self.active_queries.poll_next_unpin(cx) {
|
||||
match query_future.0 {
|
||||
QueryType::FindPeers => {
|
||||
GroupedQueryType::FindPeers => {
|
||||
self.find_peer_active = false;
|
||||
match query_future.1 {
|
||||
Ok(r) if r.is_empty() => {
|
||||
@ -551,31 +592,96 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
|
||||
}
|
||||
Ok(r) => {
|
||||
debug!(self.log, "Discovery query completed"; "peers_found" => r.len());
|
||||
return Some((None, r));
|
||||
let mut results: HashMap<PeerId, Option<Instant>> = HashMap::new();
|
||||
r.iter().for_each(|enr| {
|
||||
// cache the found ENR's
|
||||
self.cached_enrs.put(enr.peer_id(), enr.clone());
|
||||
results.insert(enr.peer_id(), None);
|
||||
});
|
||||
return Some(results);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(self.log, "Discovery query failed"; "error" => e.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
QueryType::Subnet {
|
||||
subnet_id,
|
||||
min_ttl,
|
||||
retries,
|
||||
} => {
|
||||
GroupedQueryType::Subnet(queries) => {
|
||||
let subnets_searched_for: Vec<SubnetId> =
|
||||
queries.iter().map(|query| query.subnet_id).collect();
|
||||
match query_future.1 {
|
||||
Ok(r) if r.is_empty() => {
|
||||
debug!(self.log, "Subnet discovery query yielded no results."; "subnet_id" => *subnet_id, "retries" => retries);
|
||||
debug!(self.log, "Grouped subnet discovery query yielded no results."; "subnets_searched_for" => format!("{:?}",subnets_searched_for));
|
||||
}
|
||||
Ok(r) => {
|
||||
debug!(self.log, "Peer subnet discovery request completed"; "peers_found" => r.len(), "subnet_id" => *subnet_id);
|
||||
// A subnet query has completed. Add back to the queue, incrementing retries.
|
||||
self.add_subnet_query(subnet_id, min_ttl, retries + 1);
|
||||
// Report the results back to the peer manager.
|
||||
return Some((query_future.0.min_ttl(), r));
|
||||
debug!(self.log, "Peer grouped subnet discovery request completed"; "peers_found" => r.len(), "subnets_searched_for" => format!("{:?}",subnets_searched_for));
|
||||
|
||||
let mut mapped_results: HashMap<PeerId, Option<Instant>> =
|
||||
HashMap::new();
|
||||
|
||||
// cache the found ENR's
|
||||
for enr in r.iter().cloned() {
|
||||
self.cached_enrs.put(enr.peer_id(), enr);
|
||||
}
|
||||
|
||||
// Map each subnet query's min_ttl to the set of ENR's returned for that subnet.
|
||||
queries.iter().for_each(|query| {
|
||||
// A subnet query has completed. Add back to the queue, incrementing retries.
|
||||
self.add_subnet_query(
|
||||
query.subnet_id,
|
||||
query.min_ttl,
|
||||
query.retries + 1,
|
||||
);
|
||||
|
||||
// Check the specific subnet against the enr
|
||||
let subnet_predicate =
|
||||
subnet_predicate::<TSpec>(vec![query.subnet_id], &self.log);
|
||||
|
||||
r.iter()
|
||||
.filter(|enr| subnet_predicate(enr))
|
||||
.map(|enr| enr.peer_id())
|
||||
.for_each(|peer_id| {
|
||||
let other_min_ttl = mapped_results.get_mut(&peer_id);
|
||||
|
||||
// map peer IDs to the min_ttl furthest in the future
|
||||
match (query.min_ttl, other_min_ttl) {
|
||||
// update the mapping if the min_ttl is greater
|
||||
(
|
||||
Some(min_ttl_instant),
|
||||
Some(Some(other_min_ttl_instant)),
|
||||
) => {
|
||||
if min_ttl_instant.saturating_duration_since(
|
||||
*other_min_ttl_instant,
|
||||
) > DURATION_DIFFERENCE
|
||||
{
|
||||
*other_min_ttl_instant = min_ttl_instant;
|
||||
}
|
||||
}
|
||||
// update the mapping if we have a specified min_ttl
|
||||
(Some(min_ttl), Some(None)) => {
|
||||
mapped_results.insert(peer_id, Some(min_ttl));
|
||||
}
|
||||
// first seen min_ttl for this enr
|
||||
(Some(min_ttl), None) => {
|
||||
mapped_results.insert(peer_id, Some(min_ttl));
|
||||
}
|
||||
// first seen min_ttl for this enr
|
||||
(None, None) => {
|
||||
mapped_results.insert(peer_id, None);
|
||||
}
|
||||
(None, Some(Some(_))) => {} // Don't replace the existing specific min_ttl
|
||||
(None, Some(None)) => {} // No-op because this is a duplicate
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if mapped_results.is_empty() {
|
||||
return None;
|
||||
} else {
|
||||
return Some(mapped_results);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(self.log,"Subnet Discovery query failed"; "subnet_id" => *subnet_id, "error" => e.to_string());
|
||||
warn!(self.log,"Grouped subnet discovery query failed"; "subnets_searched_for" => format!("{:?}",subnets_searched_for), "error" => e.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -594,13 +700,9 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
|
||||
self.process_queue();
|
||||
|
||||
// Drive the queries and return any results from completed queries
|
||||
if let Some((min_ttl, result)) = self.poll_queries(cx) {
|
||||
// cache the found ENR's
|
||||
for enr in result.iter().cloned() {
|
||||
self.cached_enrs.put(enr.peer_id(), enr);
|
||||
}
|
||||
if let Some(results) = self.poll_queries(cx) {
|
||||
// return the result to the peer manager
|
||||
return Poll::Ready(DiscoveryEvent::QueryResult(min_ttl, result));
|
||||
return Poll::Ready(DiscoveryEvent::QueryResult(results));
|
||||
}
|
||||
|
||||
// Process the server event stream
|
||||
|
@ -1,9 +1,10 @@
|
||||
///! The subnet predicate used for searching for a particular subnet.
|
||||
use super::*;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Returns the predicate for a given subnet.
|
||||
pub fn subnet_predicate<TSpec>(
|
||||
subnet_id: SubnetId,
|
||||
subnet_ids: Vec<SubnetId>,
|
||||
log: &slog::Logger,
|
||||
) -> impl Fn(&Enr) -> bool + Send
|
||||
where
|
||||
@ -23,10 +24,27 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
return bitfield.get(*subnet_id as usize).unwrap_or_else(|_| {
|
||||
debug!(log_clone, "Peer found but not on desired subnet"; "peer_id" => format!("{}", enr.peer_id()));
|
||||
false
|
||||
});
|
||||
let matches: Vec<&SubnetId> = subnet_ids
|
||||
.iter()
|
||||
.filter(|id| bitfield.get(**id.deref() as usize).unwrap_or(false))
|
||||
.collect();
|
||||
|
||||
if matches.is_empty() {
|
||||
debug!(
|
||||
log_clone,
|
||||
"Peer found but not on any of the desired subnets";
|
||||
"peer_id" => format!("{}", enr.peer_id())
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
debug!(
|
||||
log_clone,
|
||||
"Peer found on desired subnet(s)";
|
||||
"peer_id" => format!("{}", enr.peer_id()),
|
||||
"subnets" => format!("{:?}", matches.as_slice())
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ pub use self::peerdb::*;
|
||||
use crate::discovery::{Discovery, DiscoveryEvent};
|
||||
use crate::rpc::{GoodbyeReason, MetaData, Protocol, RPCError, RPCResponseErrorCode};
|
||||
use crate::{error, metrics};
|
||||
use crate::{Enr, EnrExt, NetworkConfig, NetworkGlobals, PeerId};
|
||||
use crate::{EnrExt, NetworkConfig, NetworkGlobals, PeerId};
|
||||
use futures::prelude::*;
|
||||
use futures::Stream;
|
||||
use hashset_delay::HashSetDelay;
|
||||
@ -32,6 +32,7 @@ pub(crate) mod score;
|
||||
pub use peer_info::{PeerConnectionStatus::*, PeerInfo};
|
||||
pub use peer_sync_status::{PeerSyncStatus, SyncInfo};
|
||||
use score::{PeerAction, ScoreState};
|
||||
use std::collections::HashMap;
|
||||
/// The time in seconds between re-status's peers.
|
||||
const STATUS_INTERVAL: u64 = 300;
|
||||
/// The time in seconds between PING events. We do not send a ping if the other peer as PING'd us within
|
||||
@ -39,7 +40,7 @@ const STATUS_INTERVAL: u64 = 300;
|
||||
const PING_INTERVAL: u64 = 30;
|
||||
|
||||
/// The heartbeat performs regular updates such as updating reputations and performing discovery
|
||||
/// requests. This defines the interval in seconds.
|
||||
/// requests. This defines the interval in seconds.
|
||||
const HEARTBEAT_INTERVAL: u64 = 30;
|
||||
|
||||
/// A fraction of `PeerManager::target_peers` that we allow to connect to us in excess of
|
||||
@ -487,13 +488,11 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
/// with a new `PeerId` which involves a discovery routing table lookup. We could dial the
|
||||
/// multiaddr here, however this could relate to duplicate PeerId's etc. If the lookup
|
||||
/// proves resource constraining, we should switch to multiaddr dialling here.
|
||||
fn peers_discovered(&mut self, peers: &[Enr], min_ttl: Option<Instant>) {
|
||||
fn peers_discovered(&mut self, results: HashMap<PeerId, Option<Instant>>) {
|
||||
let mut to_dial_peers = Vec::new();
|
||||
|
||||
let connected_or_dialing = self.network_globals.connected_or_dialing_peers();
|
||||
for enr in peers {
|
||||
let peer_id = enr.peer_id();
|
||||
|
||||
for (peer_id, min_ttl) in results {
|
||||
// we attempt a connection if this peer is a subnet peer or if the max peer count
|
||||
// is not yet filled (including dialling peers)
|
||||
if (min_ttl.is_some() || connected_or_dialing + to_dial_peers.len() < self.max_peers)
|
||||
@ -726,9 +725,7 @@ impl<TSpec: EthSpec> Stream for PeerManager<TSpec> {
|
||||
while let Poll::Ready(event) = self.discovery.poll(cx) {
|
||||
match event {
|
||||
DiscoveryEvent::SocketUpdated(socket_addr) => self.socket_updated(socket_addr),
|
||||
DiscoveryEvent::QueryResult(min_ttl, peers) => {
|
||||
self.peers_discovered(&peers, min_ttl)
|
||||
}
|
||||
DiscoveryEvent::QueryResult(results) => self.peers_discovered(results),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ const ADVANCE_SUBSCRIBE_TIME: u32 = 3;
|
||||
/// The default number of slots before items in hash delay sets used by this class should expire.
|
||||
const DEFAULT_EXPIRATION_TIMEOUT: u32 = 3;
|
||||
// 36s at 12s slot time
|
||||
/// The default number of slots before items in hash delay sets used by this class should expire.
|
||||
/// The duration difference between two instance for them to be considered equal.
|
||||
const DURATION_DIFFERENCE: Duration = Duration::from_millis(1);
|
||||
|
||||
#[derive(Debug, Eq, Clone)]
|
||||
|
Loading…
Reference in New Issue
Block a user