Add unused seed peers to the AddressBook (#2974)

* Add unused seed peers to the AddressBook

* Document a new `await`

We added an extra await on the AddressBook thread mutex.

Co-authored-by: teor <teor@riseup.net>

* Fix a typo

* Refactor names

* Return early from `limit_initial_peers`

* Add `proptest`s regressions

* Return `MetaAddr` instead of `None`

* Test if `zebra_network::init()` deadlocks

* Remove unneeded regressions

* Rename `TimestampCollector` to `AddressBookUpdater` (#2992)

* Rename `TimestampCollector` to `AddressBookUpdater`

* Update comments

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* Move `all_peers` instead of copying them

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* Make `Duration` a const

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* Use a timeout instead of measuring the elapsed time

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* Copy `initial_peers` instead of moving them

* Refactor the position of `NewInitial` and `new_initial`

Co-authored-by: teor <teor@riseup.net>
Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
This commit is contained in:
Marek 2021-11-04 12:34:00 +01:00 committed by GitHub
parent 59ee4168cb
commit d03161c63f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 152 additions and 67 deletions

View File

@ -6,12 +6,14 @@ use futures::{channel::mpsc, prelude::*};
use crate::{meta_addr::MetaAddrChange, AddressBook}; use crate::{meta_addr::MetaAddrChange, AddressBook};
/// The timestamp collector hooks into incoming message streams for each peer and /// The `AddressBookUpdater` hooks into incoming message streams for each peer
/// records per-connection last-seen timestamps into an [`AddressBook`]. /// and lets the owner of the sender handle update the address book. For
pub struct TimestampCollector {} /// example, it can be used to record per-connection last-seen timestamps, or
/// add new initial peers to the address book.
pub struct AddressBookUpdater {}
impl TimestampCollector { impl AddressBookUpdater {
/// Spawn a new [`TimestampCollector`] task, updating a new [`AddressBook`] /// Spawn a new [`AddressBookUpdater`] task, updating a new [`AddressBook`]
/// configured with a `local_listener`. /// configured with a `local_listener`.
/// ///
/// Returns handles for the transmission channel for timestamp events, and /// Returns handles for the transmission channel for timestamp events, and

View File

@ -56,6 +56,7 @@ extern crate bitflags;
pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>; pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
mod address_book; mod address_book;
mod address_book_updater;
mod config; mod config;
pub mod constants; pub mod constants;
mod isolated; mod isolated;
@ -64,7 +65,6 @@ mod peer;
mod peer_set; mod peer_set;
mod policies; mod policies;
mod protocol; mod protocol;
mod timestamp_collector;
pub use crate::{ pub use crate::{
address_book::AddressBook, address_book::AddressBook,

View File

@ -186,6 +186,15 @@ pub struct MetaAddr {
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub enum MetaAddrChange { pub enum MetaAddrChange {
/// Creates a `MetaAddr` for an initial peer.
NewInitial {
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_socket_addr_strategy()")
)]
addr: SocketAddr,
},
/// Creates a new gossiped `MetaAddr`. /// Creates a new gossiped `MetaAddr`.
NewGossiped { NewGossiped {
#[cfg_attr( #[cfg_attr(
@ -250,6 +259,14 @@ pub enum MetaAddrChange {
} }
impl MetaAddr { impl MetaAddr {
/// Returns a [`MetaAddrChange::NewInitial`] for a peer that was excluded from
/// the list of the initial peers.
pub fn new_initial_peer(addr: SocketAddr) -> MetaAddrChange {
NewInitial {
addr: canonical_socket_addr(addr),
}
}
/// Returns a new `MetaAddr`, based on the deserialized fields from a /// Returns a new `MetaAddr`, based on the deserialized fields from a
/// gossiped peer [`Addr`][crate::protocol::external::Message::Addr] message. /// gossiped peer [`Addr`][crate::protocol::external::Message::Addr] message.
pub fn new_gossiped_meta_addr( pub fn new_gossiped_meta_addr(
@ -562,7 +579,8 @@ impl MetaAddrChange {
/// Return the address for this change. /// Return the address for this change.
pub fn addr(&self) -> SocketAddr { pub fn addr(&self) -> SocketAddr {
match self { match self {
NewGossiped { addr, .. } NewInitial { addr }
| NewGossiped { addr, .. }
| NewAlternate { addr, .. } | NewAlternate { addr, .. }
| NewLocal { addr, .. } | NewLocal { addr, .. }
| UpdateAttempt { addr } | UpdateAttempt { addr }
@ -577,7 +595,8 @@ impl MetaAddrChange {
/// This method should only be used in tests. /// This method should only be used in tests.
pub fn set_addr(&mut self, new_addr: SocketAddr) { pub fn set_addr(&mut self, new_addr: SocketAddr) {
match self { match self {
NewGossiped { addr, .. } NewInitial { addr }
| NewGossiped { addr, .. }
| NewAlternate { addr, .. } | NewAlternate { addr, .. }
| NewLocal { addr, .. } | NewLocal { addr, .. }
| UpdateAttempt { addr } | UpdateAttempt { addr }
@ -589,6 +608,7 @@ impl MetaAddrChange {
/// Return the untrusted services for this change, if available. /// Return the untrusted services for this change, if available.
pub fn untrusted_services(&self) -> Option<PeerServices> { pub fn untrusted_services(&self) -> Option<PeerServices> {
match self { match self {
NewInitial { .. } => None,
NewGossiped { NewGossiped {
untrusted_services, .. untrusted_services, ..
} => Some(*untrusted_services), } => Some(*untrusted_services),
@ -607,6 +627,7 @@ impl MetaAddrChange {
/// Return the untrusted last seen time for this change, if available. /// Return the untrusted last seen time for this change, if available.
pub fn untrusted_last_seen(&self) -> Option<DateTime32> { pub fn untrusted_last_seen(&self) -> Option<DateTime32> {
match self { match self {
NewInitial { .. } => None,
NewGossiped { NewGossiped {
untrusted_last_seen, untrusted_last_seen,
.. ..
@ -623,6 +644,7 @@ impl MetaAddrChange {
/// Return the last attempt for this change, if available. /// Return the last attempt for this change, if available.
pub fn last_attempt(&self) -> Option<Instant> { pub fn last_attempt(&self) -> Option<Instant> {
match self { match self {
NewInitial { .. } => None,
NewGossiped { .. } => None, NewGossiped { .. } => None,
NewAlternate { .. } => None, NewAlternate { .. } => None,
NewLocal { .. } => None, NewLocal { .. } => None,
@ -638,6 +660,7 @@ impl MetaAddrChange {
/// Return the last response for this change, if available. /// Return the last response for this change, if available.
pub fn last_response(&self) -> Option<DateTime32> { pub fn last_response(&self) -> Option<DateTime32> {
match self { match self {
NewInitial { .. } => None,
NewGossiped { .. } => None, NewGossiped { .. } => None,
NewAlternate { .. } => None, NewAlternate { .. } => None,
NewLocal { .. } => None, NewLocal { .. } => None,
@ -652,9 +675,10 @@ impl MetaAddrChange {
} }
} }
/// Return the last attempt for this change, if available. /// Return the last failure for this change, if available.
pub fn last_failure(&self) -> Option<Instant> { pub fn last_failure(&self) -> Option<Instant> {
match self { match self {
NewInitial { .. } => None,
NewGossiped { .. } => None, NewGossiped { .. } => None,
NewAlternate { .. } => None, NewAlternate { .. } => None,
NewLocal { .. } => None, NewLocal { .. } => None,
@ -672,6 +696,7 @@ impl MetaAddrChange {
/// Return the peer connection state for this change. /// Return the peer connection state for this change.
pub fn peer_addr_state(&self) -> PeerAddrState { pub fn peer_addr_state(&self) -> PeerAddrState {
match self { match self {
NewInitial { .. } => NeverAttemptedGossiped,
NewGossiped { .. } => NeverAttemptedGossiped, NewGossiped { .. } => NeverAttemptedGossiped,
NewAlternate { .. } => NeverAttemptedAlternate, NewAlternate { .. } => NeverAttemptedAlternate,
// local listeners get sanitized, so the state doesn't matter here // local listeners get sanitized, so the state doesn't matter here
@ -685,15 +710,18 @@ impl MetaAddrChange {
/// If this change can create a new `MetaAddr`, return that address. /// If this change can create a new `MetaAddr`, return that address.
pub fn into_new_meta_addr(self) -> Option<MetaAddr> { pub fn into_new_meta_addr(self) -> Option<MetaAddr> {
match self { match self {
NewGossiped { .. } | NewAlternate { .. } | NewLocal { .. } => Some(MetaAddr { NewInitial { .. } | NewGossiped { .. } | NewAlternate { .. } | NewLocal { .. } => {
addr: self.addr(), Some(MetaAddr {
services: self.untrusted_services(), addr: self.addr(),
untrusted_last_seen: self.untrusted_last_seen(), services: self.untrusted_services(),
last_response: None, untrusted_last_seen: self.untrusted_last_seen(),
last_attempt: None, last_response: None,
last_failure: None, last_attempt: None,
last_connection_state: self.peer_addr_state(), last_failure: None,
}), last_connection_state: self.peer_addr_state(),
})
}
UpdateAttempt { .. } | UpdateResponded { .. } | UpdateFailed { .. } => None, UpdateAttempt { .. } | UpdateResponded { .. } | UpdateFailed { .. } => None,
} }
} }

View File

@ -52,7 +52,7 @@ use crate::{
pub struct Handshake<S, C = NoChainTip> { pub struct Handshake<S, C = NoChainTip> {
config: Config, config: Config,
inbound_service: S, inbound_service: S,
timestamp_collector: mpsc::Sender<MetaAddrChange>, address_book_updater: mpsc::Sender<MetaAddrChange>,
inv_collector: broadcast::Sender<(InventoryHash, SocketAddr)>, inv_collector: broadcast::Sender<(InventoryHash, SocketAddr)>,
nonces: Arc<futures::lock::Mutex<HashSet<Nonce>>>, nonces: Arc<futures::lock::Mutex<HashSet<Nonce>>>,
user_agent: String, user_agent: String,
@ -304,7 +304,7 @@ impl fmt::Debug for ConnectedAddr {
pub struct Builder<S, C = NoChainTip> { pub struct Builder<S, C = NoChainTip> {
config: Option<Config>, config: Option<Config>,
inbound_service: Option<S>, inbound_service: Option<S>,
timestamp_collector: Option<mpsc::Sender<MetaAddrChange>>, address_book_updater: Option<mpsc::Sender<MetaAddrChange>>,
our_services: Option<PeerServices>, our_services: Option<PeerServices>,
user_agent: Option<String>, user_agent: Option<String>,
relay: Option<bool>, relay: Option<bool>,
@ -346,11 +346,11 @@ where
/// ///
/// This channel takes `MetaAddr`s, permanent addresses which can be used to /// This channel takes `MetaAddr`s, permanent addresses which can be used to
/// make outbound connections to peers. /// make outbound connections to peers.
pub fn with_timestamp_collector( pub fn with_address_book_updater(
mut self, mut self,
timestamp_collector: mpsc::Sender<MetaAddrChange>, address_book_updater: mpsc::Sender<MetaAddrChange>,
) -> Self { ) -> Self {
self.timestamp_collector = Some(timestamp_collector); self.address_book_updater = Some(address_book_updater);
self self
} }
@ -382,7 +382,7 @@ where
// TODO: Until Rust RFC 2528 reaches stable, we can't do `..self` // TODO: Until Rust RFC 2528 reaches stable, we can't do `..self`
config: self.config, config: self.config,
inbound_service: self.inbound_service, inbound_service: self.inbound_service,
timestamp_collector: self.timestamp_collector, address_book_updater: self.address_book_updater,
our_services: self.our_services, our_services: self.our_services,
user_agent: self.user_agent, user_agent: self.user_agent,
relay: self.relay, relay: self.relay,
@ -410,9 +410,9 @@ where
let (tx, _) = broadcast::channel(100); let (tx, _) = broadcast::channel(100);
tx tx
}); });
let timestamp_collector = self.timestamp_collector.unwrap_or_else(|| { let address_book_updater = self.address_book_updater.unwrap_or_else(|| {
// No timestamp collector was passed, so create a stub channel. // No `AddressBookUpdater` for timestamp collection was passed, so create a stub
// Dropping the receiver means sends will fail, but we don't care. // channel. Dropping the receiver means sends will fail, but we don't care.
let (tx, _rx) = mpsc::channel(1); let (tx, _rx) = mpsc::channel(1);
tx tx
}); });
@ -425,7 +425,7 @@ where
config, config,
inbound_service, inbound_service,
inv_collector, inv_collector,
timestamp_collector, address_book_updater,
nonces, nonces,
user_agent, user_agent,
our_services, our_services,
@ -449,7 +449,7 @@ where
Builder { Builder {
config: None, config: None,
inbound_service: None, inbound_service: None,
timestamp_collector: None, address_book_updater: None,
user_agent: None, user_agent: None,
our_services: None, our_services: None,
relay: None, relay: None,
@ -709,7 +709,7 @@ where
// Clone these upfront, so they can be moved into the future. // Clone these upfront, so they can be moved into the future.
let nonces = self.nonces.clone(); let nonces = self.nonces.clone();
let inbound_service = self.inbound_service.clone(); let inbound_service = self.inbound_service.clone();
let mut timestamp_collector = self.timestamp_collector.clone(); let mut address_book_updater = self.address_book_updater.clone();
let inv_collector = self.inv_collector.clone(); let inv_collector = self.inv_collector.clone();
let config = self.config.clone(); let config = self.config.clone();
let user_agent = self.user_agent.clone(); let user_agent = self.user_agent.clone();
@ -764,7 +764,7 @@ where
for alt_addr in alternate_addrs { for alt_addr in alternate_addrs {
let alt_addr = MetaAddr::new_alternate(&alt_addr, &remote_services); let alt_addr = MetaAddr::new_alternate(&alt_addr, &remote_services);
// awaiting a local task won't hang // awaiting a local task won't hang
let _ = timestamp_collector.send(alt_addr).await; let _ = address_book_updater.send(alt_addr).await;
} }
// Set the connection's version to the minimum of the received version or our own. // Set the connection's version to the minimum of the received version or our own.
@ -818,7 +818,7 @@ where
// //
// Every message and error must update the peer address state via // Every message and error must update the peer address state via
// the inbound_ts_collector. // the inbound_ts_collector.
let inbound_ts_collector = timestamp_collector.clone(); let inbound_ts_collector = address_book_updater.clone();
let inv_collector = inv_collector.clone(); let inv_collector = inv_collector.clone();
let ts_inner_conn_span = connection_span.clone(); let ts_inner_conn_span = connection_span.clone();
let inv_inner_conn_span = connection_span.clone(); let inv_inner_conn_span = connection_span.clone();
@ -939,14 +939,14 @@ where
// //
// Returning from the spawned closure terminates the connection's heartbeat task. // Returning from the spawned closure terminates the connection's heartbeat task.
let heartbeat_span = tracing::debug_span!(parent: connection_span, "heartbeat"); let heartbeat_span = tracing::debug_span!(parent: connection_span, "heartbeat");
let heartbeat_ts_collector = timestamp_collector.clone(); let heartbeat_ts_collector = address_book_updater.clone();
tokio::spawn( tokio::spawn(
async move { async move {
use futures::future::Either; use futures::future::Either;
let mut shutdown_rx = shutdown_rx; let mut shutdown_rx = shutdown_rx;
let mut server_tx = server_tx; let mut server_tx = server_tx;
let mut timestamp_collector = heartbeat_ts_collector.clone(); let mut heartbeat_ts_collector = heartbeat_ts_collector.clone();
let mut interval_stream = let mut interval_stream =
IntervalStream::new(tokio::time::interval(constants::HEARTBEAT_INTERVAL)); IntervalStream::new(tokio::time::interval(constants::HEARTBEAT_INTERVAL));
@ -969,7 +969,7 @@ where
tracing::trace!("shutting down due to Client shut down"); tracing::trace!("shutting down due to Client shut down");
if let Some(book_addr) = connected_addr.get_address_book_addr() { if let Some(book_addr) = connected_addr.get_address_book_addr() {
// awaiting a local task won't hang // awaiting a local task won't hang
let _ = timestamp_collector let _ = heartbeat_ts_collector
.send(MetaAddr::new_shutdown(&book_addr, remote_services)) .send(MetaAddr::new_shutdown(&book_addr, remote_services))
.await; .await;
} }
@ -985,7 +985,7 @@ where
let heartbeat = send_one_heartbeat(&mut server_tx); let heartbeat = send_one_heartbeat(&mut server_tx);
if heartbeat_timeout( if heartbeat_timeout(
heartbeat, heartbeat,
&mut timestamp_collector, &mut heartbeat_ts_collector,
&connected_addr, &connected_addr,
&remote_services, &remote_services,
) )
@ -1058,7 +1058,7 @@ async fn send_one_heartbeat(server_tx: &mut mpsc::Sender<ClientRequest>) -> Resu
/// `handle_heartbeat_error`. /// `handle_heartbeat_error`.
async fn heartbeat_timeout<F, T>( async fn heartbeat_timeout<F, T>(
fut: F, fut: F,
timestamp_collector: &mut mpsc::Sender<MetaAddrChange>, address_book_updater: &mut mpsc::Sender<MetaAddrChange>,
connected_addr: &ConnectedAddr, connected_addr: &ConnectedAddr,
remote_services: &PeerServices, remote_services: &PeerServices,
) -> Result<T, BoxError> ) -> Result<T, BoxError>
@ -1069,7 +1069,7 @@ where
Ok(inner_result) => { Ok(inner_result) => {
handle_heartbeat_error( handle_heartbeat_error(
inner_result, inner_result,
timestamp_collector, address_book_updater,
connected_addr, connected_addr,
remote_services, remote_services,
) )
@ -1078,7 +1078,7 @@ where
Err(elapsed) => { Err(elapsed) => {
handle_heartbeat_error( handle_heartbeat_error(
Err(elapsed), Err(elapsed),
timestamp_collector, address_book_updater,
connected_addr, connected_addr,
remote_services, remote_services,
) )
@ -1089,10 +1089,10 @@ where
Ok(t) Ok(t)
} }
/// If `result.is_err()`, mark `connected_addr` as failed using `timestamp_collector`. /// If `result.is_err()`, mark `connected_addr` as failed using `address_book_updater`.
async fn handle_heartbeat_error<T, E>( async fn handle_heartbeat_error<T, E>(
result: Result<T, E>, result: Result<T, E>,
timestamp_collector: &mut mpsc::Sender<MetaAddrChange>, address_book_updater: &mut mpsc::Sender<MetaAddrChange>,
connected_addr: &ConnectedAddr, connected_addr: &ConnectedAddr,
remote_services: &PeerServices, remote_services: &PeerServices,
) -> Result<T, E> ) -> Result<T, E>
@ -1105,7 +1105,7 @@ where
tracing::debug!(?err, "heartbeat error, shutting down"); tracing::debug!(?err, "heartbeat error, shutting down");
if let Some(book_addr) = connected_addr.get_address_book_addr() { if let Some(book_addr) = connected_addr.get_address_book_addr() {
let _ = timestamp_collector let _ = address_book_updater
.send(MetaAddr::new_errored(&book_addr, *remote_services)) .send(MetaAddr::new_errored(&book_addr, *remote_services))
.await; .await;
} }

View File

@ -29,11 +29,11 @@ use tracing_futures::Instrument;
use zebra_chain::{chain_tip::ChainTip, parameters::Network}; use zebra_chain::{chain_tip::ChainTip, parameters::Network};
use crate::{ use crate::{
address_book_updater::AddressBookUpdater,
constants, constants,
meta_addr::MetaAddr, meta_addr::{MetaAddr, MetaAddrChange},
peer::{self, HandshakeRequest, OutboundConnectorRequest}, peer::{self, HandshakeRequest, OutboundConnectorRequest},
peer_set::{set::MorePeers, ActiveConnectionCounter, CandidateSet, ConnectionTracker, PeerSet}, peer_set::{set::MorePeers, ActiveConnectionCounter, CandidateSet, ConnectionTracker, PeerSet},
timestamp_collector::TimestampCollector,
AddressBook, BoxError, Config, Request, Response, AddressBook, BoxError, Config, Request, Response,
}; };
@ -95,7 +95,7 @@ where
let (tcp_listener, listen_addr) = open_listener(&config.clone()).await; let (tcp_listener, listen_addr) = open_listener(&config.clone()).await;
let (address_book, timestamp_collector) = TimestampCollector::spawn(listen_addr); let (address_book, address_book_updater) = AddressBookUpdater::spawn(listen_addr);
// Create a broadcast channel for peer inventory advertisements. // Create a broadcast channel for peer inventory advertisements.
// If it reaches capacity, this channel drops older inventory advertisements. // If it reaches capacity, this channel drops older inventory advertisements.
@ -118,7 +118,7 @@ where
.with_config(config.clone()) .with_config(config.clone())
.with_inbound_service(inbound_service) .with_inbound_service(inbound_service)
.with_inventory_collector(inv_sender) .with_inventory_collector(inv_sender)
.with_timestamp_collector(timestamp_collector) .with_address_book_updater(address_book_updater.clone())
.with_advertised_services(PeerServices::NODE_NETWORK) .with_advertised_services(PeerServices::NODE_NETWORK)
.with_user_agent(crate::constants::USER_AGENT.to_string()) .with_user_agent(crate::constants::USER_AGENT.to_string())
.with_latest_chain_tip(latest_chain_tip) .with_latest_chain_tip(latest_chain_tip)
@ -177,6 +177,7 @@ where
config.clone(), config.clone(),
outbound_connector.clone(), outbound_connector.clone(),
peerset_tx.clone(), peerset_tx.clone(),
address_book_updater,
); );
let initial_peers_join = tokio::spawn(initial_peers_fut.instrument(Span::current())); let initial_peers_join = tokio::spawn(initial_peers_fut.instrument(Span::current()));
@ -232,6 +233,7 @@ async fn add_initial_peers<S>(
config: Config, config: Config,
outbound_connector: S, outbound_connector: S,
mut peerset_tx: mpsc::Sender<PeerChange>, mut peerset_tx: mpsc::Sender<PeerChange>,
address_book_updater: mpsc::Sender<MetaAddrChange>,
) -> Result<ActiveConnectionCounter, BoxError> ) -> Result<ActiveConnectionCounter, BoxError>
where where
S: Service< S: Service<
@ -241,7 +243,7 @@ where
> + Clone, > + Clone,
S::Future: Send + 'static, S::Future: Send + 'static,
{ {
let initial_peers = limit_initial_peers(&config).await; let initial_peers = limit_initial_peers(&config, address_book_updater).await;
let mut handshake_success_total: usize = 0; let mut handshake_success_total: usize = 0;
let mut handshake_error_total: usize = 0; let mut handshake_error_total: usize = 0;
@ -359,26 +361,38 @@ where
/// `peerset_initial_target_size`. /// `peerset_initial_target_size`.
/// ///
/// The result is randomly chosen entries from the provided set of addresses. /// The result is randomly chosen entries from the provided set of addresses.
async fn limit_initial_peers(config: &Config) -> HashSet<SocketAddr> { async fn limit_initial_peers(
let initial_peers = config.initial_peers().await; config: &Config,
let initial_peer_count = initial_peers.len(); mut address_book_updater: mpsc::Sender<MetaAddrChange>,
) -> HashSet<SocketAddr> {
let all_peers = config.initial_peers().await;
let peers_count = all_peers.len();
// Limit the number of initial peers to `config.peerset_initial_target_size` if peers_count <= config.peerset_initial_target_size {
if initial_peer_count > config.peerset_initial_target_size { return all_peers;
info!(
"Limiting the initial peers list from {} to {}",
initial_peer_count, config.peerset_initial_target_size
);
} }
let initial_peers_vect: Vec<SocketAddr> = initial_peers.iter().copied().collect(); // Limit the number of initial peers to `config.peerset_initial_target_size`
info!(
"Limiting the initial peers list from {} to {}",
peers_count, config.peerset_initial_target_size
);
// TODO: add unused peers to the AddressBook (#2931) // Split all the peers into the `initial_peers` that will be returned and
// https://docs.rs/rand/0.8.4/rand/seq/trait.SliceRandom.html#tymethod.partial_shuffle // `unused_peers` that will be sent to the address book.
initial_peers_vect let mut all_peers: Vec<SocketAddr> = all_peers.into_iter().collect();
.choose_multiple(&mut rand::thread_rng(), config.peerset_initial_target_size) let (initial_peers, unused_peers) =
.copied() all_peers.partial_shuffle(&mut rand::thread_rng(), config.peerset_initial_target_size);
.collect()
// Send the unused peers to the address book.
for peer in unused_peers {
let peer_addr = MetaAddr::new_initial_peer(*peer);
// `send` only waits when the channel is full.
// The address book updater is a separate task, so we will only wait for a short time.
let _ = address_book_updater.send(peer_addr).await;
}
initial_peers.iter().copied().collect()
} }
/// Open a peer connection listener on `config.listen_addr`, /// Open a peer connection listener on `config.listen_addr`,
@ -391,7 +405,7 @@ async fn limit_initial_peers(config: &Config) -> HashSet<SocketAddr> {
/// ///
/// If opening the listener fails. /// If opening the listener fails.
#[instrument(skip(config), fields(addr = ?config.listen_addr))] #[instrument(skip(config), fields(addr = ?config.listen_addr))]
async fn open_listener(config: &Config) -> (TcpListener, SocketAddr) { pub(crate) async fn open_listener(config: &Config) -> (TcpListener, SocketAddr) {
// Warn if we're configured using the wrong network port. // Warn if we're configured using the wrong network port.
use Network::*; use Network::*;
let wrong_net = match config.network { let wrong_net = match config.network {

View File

@ -32,6 +32,7 @@ use zebra_chain::{chain_tip::NoChainTip, parameters::Network, serialization::Dat
use zebra_test::net::random_known_port; use zebra_test::net::random_known_port;
use crate::{ use crate::{
address_book_updater::AddressBookUpdater,
constants, init, constants, init,
meta_addr::MetaAddr, meta_addr::MetaAddr,
peer::{self, ErrorSlot, HandshakeRequest, OutboundConnectorRequest}, peer::{self, ErrorSlot, HandshakeRequest, OutboundConnectorRequest},
@ -1140,7 +1141,7 @@ async fn add_initial_peers_is_rate_limited() {
let before = Instant::now(); let before = Instant::now();
let (initial_peers_task_handle, peerset_rx) = let (initial_peers_task_handle, peerset_rx) =
spawn_add_initial_peers(PEER_COUNT, outbound_connector); spawn_add_initial_peers(PEER_COUNT, outbound_connector).await;
let connections = peerset_rx.take(PEER_COUNT).collect::<Vec<_>>().await; let connections = peerset_rx.take(PEER_COUNT).collect::<Vec<_>>().await;
let elapsed = Instant::now() - before; let elapsed = Instant::now() - before;
@ -1161,6 +1162,43 @@ async fn add_initial_peers_is_rate_limited() {
); );
} }
/// Test that [`init`] does not deadlock.
#[tokio::test]
async fn network_init_deadlock() {
// The `PEER_COUNT` is the amount of initial seed peers. The value is set so
// that the peers fill up `PEERSET_INITIAL_TARGET_SIZE`, fill up the channel
// for sending unused peers to the `AddressBook`, and so that there are
// still some extra peers left.
const PEER_COUNT: usize = 200;
const PEERSET_INITIAL_TARGET_SIZE: usize = 2;
const TIME_LIMIT: Duration = Duration::from_secs(10);
zebra_test::init();
// Create a list of dummy IPs, and initialize a config using them as the
// initial peers. The amount of these peers will overflow
// `PEERSET_INITIAL_TARGET_SIZE`.
let mut peers = HashSet::new();
for address_number in 0..PEER_COUNT {
peers.insert(
SocketAddr::new(Ipv4Addr::new(127, 1, 1, address_number as _).into(), 1).to_string(),
);
}
let config = Config {
initial_mainnet_peers: peers,
peerset_initial_target_size: PEERSET_INITIAL_TARGET_SIZE,
network: Network::Mainnet,
..Config::default()
};
let nil_inbound_service = service_fn(|_| async { Ok(Response::Nil) });
let init_future = init(config, nil_inbound_service, NoChainTip);
assert!(tokio::time::timeout(TIME_LIMIT, init_future).await.is_ok());
}
/// Open a local listener on `listen_addr` for `network`. /// Open a local listener on `listen_addr` for `network`.
/// Asserts that the local listener address works as expected. /// Asserts that the local listener address works as expected.
async fn local_listener_port_with(listen_addr: SocketAddr, network: Network) { async fn local_listener_port_with(listen_addr: SocketAddr, network: Network) {
@ -1442,7 +1480,7 @@ where
/// Dummy IPs are used. /// Dummy IPs are used.
/// ///
/// Returns the task [`JoinHandle`], and the peer set receiver. /// Returns the task [`JoinHandle`], and the peer set receiver.
fn spawn_add_initial_peers<C>( async fn spawn_add_initial_peers<C>(
peer_count: usize, peer_count: usize,
outbound_connector: C, outbound_connector: C,
) -> ( ) -> (
@ -1475,7 +1513,10 @@ where
let (peerset_tx, peerset_rx) = mpsc::channel::<PeerChange>(peer_count + 1); let (peerset_tx, peerset_rx) = mpsc::channel::<PeerChange>(peer_count + 1);
let add_fut = add_initial_peers(config, outbound_connector, peerset_tx); let (_tcp_listener, listen_addr) = open_listener(&config.clone()).await;
let (_address_book, address_book_updater) = AddressBookUpdater::spawn(listen_addr);
let add_fut = add_initial_peers(config, outbound_connector, peerset_tx, address_book_updater);
let add_task_handle = tokio::spawn(add_fut); let add_task_handle = tokio::spawn(add_fut);
(add_task_handle, peerset_rx) (add_task_handle, peerset_rx)