From 2296a87b733e0d7e02cb3e42e89ffc37bb519b3c Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 31 Mar 2020 12:00:17 +0200 Subject: [PATCH 01/22] protocols/kad/behaviour: Use HashSet to deduplicate GetProviders (#1528) Given that the order of `PeerId`s within the `GetProvidersOk.providers` set is irrelevant but duplication is at best confusing this commit makes use of a `HashSet` instead of a `Vec` to return unique `PeerId`s only. --- protocols/kad/src/behaviour.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 466a4ee0..d00d8e8b 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -42,7 +42,7 @@ use libp2p_swarm::{ use log::{info, debug, warn}; use smallvec::SmallVec; use std::{borrow::{Borrow, Cow}, error, iter, time::Duration}; -use std::collections::VecDeque; +use std::collections::{HashSet, VecDeque}; use std::num::NonZeroUsize; use std::task::{Context, Poll}; use wasm_timer::Instant; @@ -534,7 +534,7 @@ where pub fn get_providers(&mut self, key: record::Key) { let info = QueryInfo::GetProviders { key: key.clone(), - providers: Vec::new(), + providers: HashSet::new(), }; let target = kbucket::Key::new(key); let peers = self.kbuckets.closest_keys(&target); @@ -1233,7 +1233,7 @@ where providers, .. } = &mut query.inner.info { for peer in provider_peers { - providers.push(peer.node_id); + providers.insert(peer.node_id); } } } @@ -1701,7 +1701,7 @@ pub type GetProvidersResult = Result; #[derive(Debug, Clone)] pub struct GetProvidersOk { pub key: record::Key, - pub providers: Vec, + pub providers: HashSet, pub closest_peers: Vec } @@ -1710,7 +1710,7 @@ pub struct GetProvidersOk { pub enum GetProvidersError { Timeout { key: record::Key, - providers: Vec, + providers: HashSet, closest_peers: Vec } } @@ -1843,7 +1843,7 @@ enum QueryInfo { /// The key for which to search for providers. key: record::Key, /// The found providers. - providers: Vec, + providers: HashSet, }, /// A query that searches for the closest closest nodes to a key to be From bc455038fc9c448b6d311fc034568dd44bc69988 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 31 Mar 2020 14:56:04 +0200 Subject: [PATCH 02/22] core/src: Remove poll_broadcast connection notification mechanism (#1527) * core/src: Remove poll_broadcast connection notification mechanism The `Network::poll_broadcast` function has not proven to be useful. This commit removes the mechanism all the way down to the connection manager. With `poll_broadcast` gone there is no mechanism left to send commands to pending connections. Thereby command buffering for pending connections is not needed anymore and is thus removed in this commit as well. * core/src/connection/manager.rs: Remove warning comment Co-Authored-By: Pierre Krieger Co-authored-by: Pierre Krieger --- core/src/connection/manager.rs | 36 +------------------------ core/src/connection/manager/task.rs | 41 +++++++++++------------------ core/src/connection/pool.rs | 12 --------- core/src/network.rs | 13 --------- 4 files changed, 17 insertions(+), 85 deletions(-) diff --git a/core/src/connection/manager.rs b/core/src/connection/manager.rs index c0a4af44..c366a071 100644 --- a/core/src/connection/manager.rs +++ b/core/src/connection/manager.rs @@ -133,7 +133,7 @@ where /// the associated user data. #[derive(Debug)] struct TaskInfo { - /// channel endpoint to send messages to the task + /// Channel endpoint to send messages to the task. sender: mpsc::Sender>, /// The state of the task as seen by the `Manager`. state: TaskState, @@ -286,40 +286,6 @@ impl Manager { ConnectionId(task_id) } - /// Notifies the handlers of all managed connections of an event. - /// - /// This function is "atomic", in the sense that if `Poll::Pending` is - /// returned then no event has been sent. - #[must_use] - pub fn poll_broadcast(&mut self, event: &I, cx: &mut Context) -> Poll<()> - where - I: Clone - { - for task in self.tasks.values_mut() { - if let Poll::Pending = task.sender.poll_ready(cx) { // (*) - return Poll::Pending; - } - } - - for (id, task) in self.tasks.iter_mut() { - let cmd = task::Command::NotifyHandler(event.clone()); - match task.sender.start_send(cmd) { - Ok(()) => {}, - Err(e) if e.is_full() => unreachable!("by (*)"), - Err(e) if e.is_disconnected() => { - // The background task ended. The manager will eventually be - // informed through an `Error` event from the task. - log::trace!("Connection dropped: {:?}", id); - }, - Err(e) => { - log::error!("Unexpected error: {:?}", e); - } - } - } - - Poll::Ready(()) - } - /// Gets an entry for a managed connection, if it exists. pub fn entry(&mut self, id: ConnectionId) -> Option> { if let hash_map::Entry::Occupied(task) = self.tasks.entry(id.0) { diff --git a/core/src/connection/manager/task.rs b/core/src/connection/manager/task.rs index 9149e89e..4272722d 100644 --- a/core/src/connection/manager/task.rs +++ b/core/src/connection/manager/task.rs @@ -87,7 +87,7 @@ where commands: stream::Fuse>>, /// Inner state of this `Task`. - state: State, + state: State, } impl Task @@ -111,7 +111,6 @@ where state: State::Pending { future: Box::pin(future), handler, - events: Vec::new() }, } } @@ -133,7 +132,7 @@ where } /// The state associated with the `Task` of a connection. -enum State +enum State where M: StreamMuxer, H: IntoConnectionHandler, @@ -146,12 +145,6 @@ where future: Pin>, /// The intended handler for the established connection. handler: H, - /// While we are dialing the future, we need to buffer the events received via - /// `Command::NotifyHandler` so that they get delivered to the `handler` - /// once the connection is established. We can't leave these in `Task::receiver` - /// because we have to detect if the connection attempt has been aborted (by - /// dropping the corresponding `sender` owned by the manager). - events: Vec }, /// The connection is established and a new event is ready to be emitted. @@ -198,30 +191,29 @@ where 'poll: loop { match std::mem::replace(&mut this.state, State::Done) { - State::Pending { mut future, handler, mut events } => { - // Process commands from the manager. - loop { - match Stream::poll_next(Pin::new(&mut this.commands), cx) { - Poll::Pending => break, - Poll::Ready(None) => return Poll::Ready(()), - Poll::Ready(Some(Command::NotifyHandler(event))) => - events.push(event), - } + State::Pending { mut future, handler } => { + // Check if the manager aborted this task by dropping the `commands` + // channel sender side. + match Stream::poll_next(Pin::new(&mut this.commands), cx) { + Poll::Pending => {}, + Poll::Ready(None) => return Poll::Ready(()), + Poll::Ready(Some(Command::NotifyHandler(_))) => unreachable!( + "Manager does not allow sending commands to pending tasks.", + ) } // Check if the connection succeeded. match Future::poll(Pin::new(&mut future), cx) { Poll::Ready(Ok((info, muxer))) => { - let mut c = Connection::new(muxer, handler.into_handler(&info)); - for event in events { - c.inject_event(event) - } this.state = State::EstablishedReady { - connection: Some(c), + connection: Some(Connection::new( + muxer, + handler.into_handler(&info), + )), event: Event::Established { id, info } } } Poll::Pending => { - this.state = State::Pending { future, handler, events }; + this.state = State::Pending { future, handler }; return Poll::Pending } Poll::Ready(Err(error)) => { @@ -338,4 +330,3 @@ where } } } - diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index e6441beb..300c649a 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -330,18 +330,6 @@ where id } - /// Sends an event to all nodes. - /// - /// This function is "atomic", in the sense that if `Poll::Pending` is returned then no event - /// has been sent to any node yet. - #[must_use] - pub fn poll_broadcast(&mut self, event: &TInEvent, cx: &mut Context) -> Poll<()> - where - TInEvent: Clone - { - self.manager.poll_broadcast(event, cx) - } - /// Adds an existing established connection to the pool. /// /// Returns the assigned connection ID on success. An error is returned diff --git a/core/src/network.rs b/core/src/network.rs index 52d0da80..39ea68ed 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -269,18 +269,6 @@ where }) } - /// Notifies the connection handler of _every_ connection of _every_ peer of an event. - /// - /// This function is "atomic", in the sense that if `Poll::Pending` is returned then no event - /// has been sent to any node yet. - #[must_use] - pub fn poll_broadcast(&mut self, event: &TInEvent, cx: &mut Context) -> Poll<()> - where - TInEvent: Clone - { - self.pool.poll_broadcast(event, cx) - } - /// Returns a list of all connected peers, i.e. peers to whom the `Network` /// has at least one established connection. pub fn connected_peers(&self) -> impl Iterator { @@ -641,4 +629,3 @@ impl NetworkConfig { self } } - From be970466b385f113ff25bdbbae82fe6325d5149a Mon Sep 17 00:00:00 2001 From: Roman Borschel Date: Tue, 31 Mar 2020 15:41:13 +0200 Subject: [PATCH 03/22] Full support for multiple connections per peer in libp2p-swarm. (#1519) * [libp2p-swarm] Make the multiple connections per peer first-class. This commit makes the notion of multiple connections per peer first-class in the API of libp2p-swarm, introducing the new callbacks `inject_connection_established` and `inject_connection_closed`. The `endpoint` parameter from `inject_connected` and `inject_disconnected` is removed, since the first connection to open may not be the last connection to close, i.e. it cannot be guaranteed, as was previously the case, that the endpoints passed to these callbacks match up. * Have identify track all addresses. So that identify requests can be answered with the correct observed address of the connection on which the request arrives. * Cleanup * Cleanup * Improve the `Peer` state API. * Remove connection ID from `SwarmEvent::Dialing`. * Mark `DialPeerCondition` non-exhaustive. * Re-encapsulate `NetworkConfig`. To retain the possibility of not re-exposing all network configuration choices, thereby providing a more convenient API on the \`SwarmBuilder\`. * Rework Swarm::dial API. * Update CHANGELOG. * Doc formatting tweaks. --- CHANGELOG.md | 7 + core/src/connection.rs | 5 +- core/src/connection/pool.rs | 8 +- core/src/network.rs | 93 ++++---- core/src/network/event.rs | 2 + core/src/network/peer.rs | 80 ++++++- misc/core-derive/src/lib.rs | 70 +++--- protocols/floodsub/src/layer.rs | 20 +- protocols/gossipsub/src/behaviour.rs | 10 +- protocols/gossipsub/src/behaviour/tests.rs | 6 +- protocols/identify/src/identify.rs | 43 ++-- protocols/kad/src/behaviour.rs | 36 +-- protocols/mdns/src/behaviour.rs | 5 +- protocols/ping/src/lib.rs | 6 +- swarm/src/behaviour.rs | 64 +++++- swarm/src/lib.rs | 239 +++++++++++++------- swarm/src/protocols_handler/node_handler.rs | 16 +- swarm/src/toggle.rs | 20 +- 18 files changed, 491 insertions(+), 239 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c6595a4..bbf9976b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# Version ??? + +- Support for multiple connections per peer and configurable connection limits. + See [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), + [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and + [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. + # Version 0.16.2 (2020-02-28) - Fixed yamux connections not properly closing and being stuck in the `CLOSE_WAIT` state. diff --git a/core/src/connection.rs b/core/src/connection.rs index de6a03d0..82747f60 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -35,7 +35,7 @@ pub use pool::{EstablishedConnection, EstablishedConnectionIter, PendingConnecti use crate::muxing::StreamMuxer; use crate::{Multiaddr, PeerId}; -use std::{fmt, pin::Pin, task::Context, task::Poll}; +use std::{error::Error, fmt, pin::Pin, task::Context, task::Poll}; use std::hash::Hash; use substream::{Muxing, SubstreamEvent}; @@ -334,3 +334,6 @@ impl fmt::Display for ConnectionLimit { write!(f, "{}/{}", self.current, self.limit) } } + +/// A `ConnectionLimit` can represent an error if it has been exceeded. +impl Error for ConnectionLimit {} diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 300c649a..56e11236 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -225,7 +225,7 @@ where TPeerId: Clone + Send + 'static, { let endpoint = info.to_connected_point(); - if let Some(limit) = self.limits.max_pending_incoming { + if let Some(limit) = self.limits.max_incoming { let current = self.iter_pending_incoming().count(); if current >= limit { return Err(ConnectionLimit { limit, current }) @@ -834,8 +834,8 @@ where /// The configurable limits of a connection [`Pool`]. #[derive(Debug, Clone, Default)] pub struct PoolLimits { - pub max_pending_outgoing: Option, - pub max_pending_incoming: Option, + pub max_outgoing: Option, + pub max_incoming: Option, pub max_established_per_peer: Option, } @@ -851,7 +851,7 @@ impl PoolLimits { where F: FnOnce() -> usize { - Self::check(current, self.max_pending_outgoing) + Self::check(current, self.max_outgoing) } fn check(current: F, limit: Option) -> Result<(), ConnectionLimit> diff --git a/core/src/network.rs b/core/src/network.rs index 39ea68ed..10b6e063 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -220,7 +220,7 @@ where /// [`Connection`](crate::connection::Connection) upon success and the /// connection ID is returned. pub fn dial(&mut self, address: &Multiaddr, handler: THandler) - -> Result> + -> Result where TTrans: Transport, TTrans::Error: Send + 'static, @@ -232,10 +232,17 @@ where TConnInfo: Send + 'static, TPeerId: Send + 'static, { - let future = self.transport().clone().dial(address.clone())? - .map_err(|err| PendingConnectionError::Transport(TransportError::Other(err))); let info = OutgoingInfo { address, peer_id: None }; - self.pool.add_outgoing(future, handler, info).map_err(DialError::MaxPending) + match self.transport().clone().dial(address.clone()) { + Ok(f) => { + let f = f.map_err(|err| PendingConnectionError::Transport(TransportError::Other(err))); + self.pool.add_outgoing(f, handler, info) + } + Err(err) => { + let f = future::err(PendingConnectionError::Transport(err)); + self.pool.add_outgoing(f, handler, info) + } + } } /// Returns information about the state of the `Network`. @@ -275,6 +282,22 @@ where self.pool.iter_connected() } + /// Checks whether the network has an established connection to a peer. + pub fn is_connected(&self, peer: &TPeerId) -> bool { + self.pool.is_connected(peer) + } + + /// Checks whether the network has an ongoing dialing attempt to a peer. + pub fn is_dialing(&self, peer: &TPeerId) -> bool { + self.dialing.contains_key(peer) + } + + /// Checks whether the network has neither an ongoing dialing attempt, + /// nor an established connection to a peer. + pub fn is_disconnected(&self, peer: &TPeerId) -> bool { + !self.is_connected(peer) && !self.is_dialing(peer) + } + /// Returns a list of all the peers to whom a new outgoing connection /// is currently being established. pub fn dialing_peers(&self) -> impl Iterator { @@ -284,7 +307,7 @@ where /// Gets the configured limit on pending incoming connections, /// i.e. concurrent incoming connection attempts. pub fn incoming_limit(&self) -> Option { - self.pool.limits().max_pending_incoming + self.pool.limits().max_incoming } /// The total number of established connections in the `Network`. @@ -380,8 +403,9 @@ where } event } - Poll::Ready(PoolEvent::ConnectionError { connected, error, num_established, .. }) => { + Poll::Ready(PoolEvent::ConnectionError { id, connected, error, num_established, .. }) => { NetworkEvent::ConnectionError { + id, connected, error, num_established, @@ -557,43 +581,6 @@ pub struct NetworkInfo { pub num_connections_established: usize, } -/// The possible errors of [`Network::dial`]. -#[derive(Debug)] -pub enum DialError { - /// The configured limit of pending outgoing connections has been reached. - MaxPending(ConnectionLimit), - /// A transport error occurred when creating the connection. - Transport(TransportError), -} - -impl fmt::Display for DialError -where T: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - DialError::MaxPending(limit) => write!(f, "Dial error (pending limit): {}", limit.current), - DialError::Transport(err) => write!(f, "Dial error (transport): {}", err), - } - } -} - -impl std::error::Error for DialError -where T: std::error::Error + 'static, -{ - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - DialError::MaxPending(_) => None, - DialError::Transport(e) => Some(e), - } - } -} - -impl From> for DialError { - fn from(e: TransportError) -> DialError { - DialError::Transport(e) - } -} - /// The (optional) configuration for a [`Network`]. /// /// The default configuration specifies no dedicated task executor @@ -610,17 +597,29 @@ impl NetworkConfig { self } + /// Shortcut for calling `executor` with an object that calls the given closure. + pub fn set_executor_fn(mut self, f: impl Fn(Pin + Send>>) + Send + 'static) -> Self { + struct SpawnImpl(F); + impl + Send>>)> Executor for SpawnImpl { + fn exec(&self, f: Pin + Send>>) { + (self.0)(f) + } + } + self.set_executor(Box::new(SpawnImpl(f))); + self + } + pub fn executor(&self) -> Option<&Box> { self.executor.as_ref() } - pub fn set_pending_incoming_limit(&mut self, n: usize) -> &mut Self { - self.pool_limits.max_pending_incoming = Some(n); + pub fn set_incoming_limit(&mut self, n: usize) -> &mut Self { + self.pool_limits.max_incoming = Some(n); self } - pub fn set_pending_outgoing_limit(&mut self, n: usize) -> &mut Self { - self.pool_limits.max_pending_outgoing = Some(n); + pub fn set_outgoing_limit(&mut self, n: usize) -> &mut Self { + self.pool_limits.max_outgoing = Some(n); self } diff --git a/core/src/network/event.rs b/core/src/network/event.rs index 233b35fd..afbedcf7 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -114,6 +114,8 @@ where /// /// The connection is closed as a result of the error. ConnectionError { + /// The ID of the connection that encountered an error. + id: ConnectionId, /// Information about the connection that encountered the error. connected: Connected, /// The error that occurred. diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index d9f1bda4..b06be772 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -174,28 +174,66 @@ where TConnInfo: fmt::Debug + ConnectionInfo + Send + 'static, TPeerId: Eq + Hash + Clone + Send + 'static, { + /// Checks whether the peer is currently connected. + /// + /// Returns `true` iff [`Peer::into_connected`] returns `Some`. + pub fn is_connected(&self) -> bool { + match self { + Peer::Connected(..) => true, + Peer::Dialing(peer) => peer.is_connected(), + Peer::Disconnected(..) => false, + Peer::Local => false + } + } - /// If we are connected, returns the `ConnectedPeer`. + /// Checks whether the peer is currently being dialed. + /// + /// Returns `true` iff [`Peer::into_dialing`] returns `Some`. + pub fn is_dialing(&self) -> bool { + match self { + Peer::Dialing(_) => true, + Peer::Connected(peer) => peer.is_dialing(), + Peer::Disconnected(..) => false, + Peer::Local => false + } + } + + /// Checks whether the peer is currently disconnected. + /// + /// Returns `true` iff [`Peer::into_disconnected`] returns `Some`. + pub fn is_disconnected(&self) -> bool { + match self { + Peer::Disconnected(..) => true, + _ => false + } + } + + /// Converts the peer into a `ConnectedPeer`, if there an established connection exists. pub fn into_connected(self) -> Option< ConnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler, TConnInfo, TPeerId> > { match self { Peer::Connected(peer) => Some(peer), - _ => None, + Peer::Dialing(peer) => peer.into_connected(), + Peer::Disconnected(..) => None, + Peer::Local => None, } } - /// If a connection is pending, returns the `DialingPeer`. + /// Converts the peer into a `DialingPeer`, if a dialing attempt exists. pub fn into_dialing(self) -> Option< DialingPeer<'a, TTrans, TInEvent, TOutEvent, THandler, TConnInfo, TPeerId> > { match self { Peer::Dialing(peer) => Some(peer), - _ => None, + Peer::Connected(peer) => peer.into_dialing(), + Peer::Disconnected(..) => None, + Peer::Local => None } } - /// If we are not connected, returns the `DisconnectedPeer`. + /// Converts the peer into a `DisconnectedPeer`, if neither an established connection + /// nor a dialing attempt exists. pub fn into_disconnected(self) -> Option< DisconnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler, TConnInfo, TPeerId> > { @@ -225,6 +263,10 @@ where TConnInfo: ConnectionInfo, TPeerId: Eq + Hash + Clone, { + pub fn id(&self) -> &TPeerId { + &self.peer_id + } + /// Attempts to establish a new connection to this peer using the given addresses, /// if there is currently no ongoing dialing attempt. /// @@ -294,7 +336,7 @@ where self.network.dialing.contains_key(&self.peer_id) } - /// Turns this peer into a [`DialingPeer`], if there is an ongoing + /// Converts this peer into a [`DialingPeer`], if there is an ongoing /// dialing attempt, `None` otherwise. pub fn into_dialing(self) -> Option< DialingPeer<'a, TTrans, TInEvent, TOutEvent, THandler, TConnInfo, TPeerId> @@ -373,12 +415,34 @@ where TConnInfo: ConnectionInfo, TPeerId: Eq + Hash + Clone, { + pub fn id(&self) -> &TPeerId { + &self.peer_id + } + /// Disconnects from this peer, closing all pending connections. pub fn disconnect(self) -> DisconnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler, TConnInfo, TPeerId> { self.network.disconnect(&self.peer_id); DisconnectedPeer { network: self.network, peer_id: self.peer_id } } + /// Checks whether there is an established connection to the peer. + /// + /// Returns `true` iff [`DialingPeer::into_connected`] returns `Some`. + pub fn is_connected(&self) -> bool { + self.network.pool.is_connected(&self.peer_id) + } + + /// Converts the peer into a `ConnectedPeer`, if an established connection exists. + pub fn into_connected(self) + -> Option> + { + if self.is_connected() { + Some(ConnectedPeer { peer_id: self.peer_id, network: self.network }) + } else { + None + } + } + /// Obtains the connection that is currently being established. pub fn connection<'b>(&'b mut self) -> DialingConnection<'b, TInEvent, TConnInfo, TPeerId> { let attempt = match self.network.dialing.entry(self.peer_id.clone()) { @@ -452,6 +516,10 @@ where TInEvent: Send + 'static, TOutEvent: Send + 'static, { + pub fn id(&self) -> &TPeerId { + &self.peer_id + } + /// Attempts to connect to this peer using the given addresses. pub fn connect(self, first: Multiaddr, rest: TIter, handler: THandler) -> Result, diff --git a/misc/core-derive/src/lib.rs b/misc/core-derive/src/lib.rs index 46a21fd3..205d4c45 100644 --- a/misc/core-derive/src/lib.rs +++ b/misc/core-derive/src/lib.rs @@ -131,44 +131,52 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { // Build the list of statements to put in the body of `inject_connected()`. let inject_connected_stmts = { - let num_fields = data_struct.fields.iter().filter(|f| !is_ignored(f)).count(); data_struct.fields.iter().enumerate().filter_map(move |(field_n, field)| { if is_ignored(&field) { return None; } - - Some(if field_n == num_fields - 1 { - match field.ident { - Some(ref i) => quote!{ self.#i.inject_connected(peer_id, endpoint); }, - None => quote!{ self.#field_n.inject_connected(peer_id, endpoint); }, - } - } else { - match field.ident { - Some(ref i) => quote!{ self.#i.inject_connected(peer_id.clone(), endpoint.clone()); }, - None => quote!{ self.#field_n.inject_connected(peer_id.clone(), endpoint.clone()); }, - } + Some(match field.ident { + Some(ref i) => quote!{ self.#i.inject_connected(peer_id); }, + None => quote!{ self.#field_n.inject_connected(peer_id); }, }) }) }; // Build the list of statements to put in the body of `inject_disconnected()`. let inject_disconnected_stmts = { - let num_fields = data_struct.fields.iter().filter(|f| !is_ignored(f)).count(); data_struct.fields.iter().enumerate().filter_map(move |(field_n, field)| { if is_ignored(&field) { return None; } + Some(match field.ident { + Some(ref i) => quote!{ self.#i.inject_disconnected(peer_id); }, + None => quote!{ self.#field_n.inject_disconnected(peer_id); }, + }) + }) + }; - Some(if field_n == num_fields - 1 { - match field.ident { - Some(ref i) => quote!{ self.#i.inject_disconnected(peer_id, endpoint); }, - None => quote!{ self.#field_n.inject_disconnected(peer_id, endpoint); }, - } - } else { - match field.ident { - Some(ref i) => quote!{ self.#i.inject_disconnected(peer_id, endpoint.clone()); }, - None => quote!{ self.#field_n.inject_disconnected(peer_id, endpoint.clone()); }, - } + // Build the list of statements to put in the body of `inject_connection_established()`. + let inject_connection_established_stmts = { + data_struct.fields.iter().enumerate().filter_map(move |(field_n, field)| { + if is_ignored(&field) { + return None; + } + Some(match field.ident { + Some(ref i) => quote!{ self.#i.inject_connection_established(peer_id, connection_id, endpoint); }, + None => quote!{ self.#field_n.inject_connection_established(peer_id, connection_id, endpoint); }, + }) + }) + }; + + // Build the list of statements to put in the body of `inject_connection_closed()`. + let inject_connection_closed_stmts = { + data_struct.fields.iter().enumerate().filter_map(move |(field_n, field)| { + if is_ignored(&field) { + return None; + } + Some(match field.ident { + Some(ref i) => quote!{ self.#i.inject_connection_closed(peer_id, connection_id, endpoint); }, + None => quote!{ self.#field_n.inject_connection_closed(peer_id, connection_id, endpoint); }, }) }) }; @@ -383,8 +391,8 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { std::task::Poll::Ready(#network_behaviour_action::DialAddress { address }) => { return std::task::Poll::Ready(#network_behaviour_action::DialAddress { address }); } - std::task::Poll::Ready(#network_behaviour_action::DialPeer { peer_id }) => { - return std::task::Poll::Ready(#network_behaviour_action::DialPeer { peer_id }); + std::task::Poll::Ready(#network_behaviour_action::DialPeer { peer_id, condition }) => { + return std::task::Poll::Ready(#network_behaviour_action::DialPeer { peer_id, condition }); } std::task::Poll::Ready(#network_behaviour_action::NotifyHandler { peer_id, handler, event }) => { return std::task::Poll::Ready(#network_behaviour_action::NotifyHandler { @@ -421,14 +429,22 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { out } - fn inject_connected(&mut self, peer_id: #peer_id, endpoint: #connected_point) { + fn inject_connected(&mut self, peer_id: &#peer_id) { #(#inject_connected_stmts);* } - fn inject_disconnected(&mut self, peer_id: &#peer_id, endpoint: #connected_point) { + fn inject_disconnected(&mut self, peer_id: &#peer_id) { #(#inject_disconnected_stmts);* } + fn inject_connection_established(&mut self, peer_id: &#peer_id, connection_id: &#connection_id, endpoint: &#connected_point) { + #(#inject_connection_established_stmts);* + } + + fn inject_connection_closed(&mut self, peer_id: &#peer_id, connection_id: &#connection_id, endpoint: &#connected_point) { + #(#inject_connection_closed_stmts);* + } + fn inject_addr_reach_failure(&mut self, peer_id: Option<&#peer_id>, addr: &#multiaddr, error: &dyn std::error::Error) { #(#inject_addr_reach_failure_stmts);* } diff --git a/protocols/floodsub/src/layer.rs b/protocols/floodsub/src/layer.rs index 18e87c54..1c837b2d 100644 --- a/protocols/floodsub/src/layer.rs +++ b/protocols/floodsub/src/layer.rs @@ -22,14 +22,15 @@ use crate::protocol::{FloodsubConfig, FloodsubMessage, FloodsubRpc, FloodsubSubs use crate::topic::Topic; use cuckoofilter::CuckooFilter; use fnv::FnvHashSet; -use libp2p_core::{ConnectedPoint, Multiaddr, PeerId, connection::ConnectionId}; +use libp2p_core::{Multiaddr, PeerId, connection::ConnectionId}; use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, PollParameters, ProtocolsHandler, OneShotHandler, - NotifyHandler + NotifyHandler, + DialPeerCondition, }; use rand; use smallvec::SmallVec; @@ -96,7 +97,9 @@ impl Floodsub { } if self.target_peers.insert(peer_id.clone()) { - self.events.push_back(NetworkBehaviourAction::DialPeer { peer_id }); + self.events.push_back(NetworkBehaviourAction::DialPeer { + peer_id, condition: DialPeerCondition::Disconnected + }); } } @@ -236,9 +239,9 @@ impl NetworkBehaviour for Floodsub { Vec::new() } - fn inject_connected(&mut self, id: PeerId, _: ConnectedPoint) { + fn inject_connected(&mut self, id: &PeerId) { // We need to send our subscriptions to the newly-connected node. - if self.target_peers.contains(&id) { + if self.target_peers.contains(id) { for topic in self.subscribed_topics.iter().cloned() { self.events.push_back(NetworkBehaviourAction::NotifyHandler { peer_id: id.clone(), @@ -257,14 +260,17 @@ impl NetworkBehaviour for Floodsub { self.connected_peers.insert(id.clone(), SmallVec::new()); } - fn inject_disconnected(&mut self, id: &PeerId, _: ConnectedPoint) { + fn inject_disconnected(&mut self, id: &PeerId) { let was_in = self.connected_peers.remove(id); debug_assert!(was_in.is_some()); // We can be disconnected by the remote in case of inactivity for example, so we always // try to reconnect. if self.target_peers.contains(id) { - self.events.push_back(NetworkBehaviourAction::DialPeer { peer_id: id.clone() }); + self.events.push_back(NetworkBehaviourAction::DialPeer { + peer_id: id.clone(), + condition: DialPeerCondition::Disconnected + }); } } diff --git a/protocols/gossipsub/src/behaviour.rs b/protocols/gossipsub/src/behaviour.rs index 2e55571a..63611df9 100644 --- a/protocols/gossipsub/src/behaviour.rs +++ b/protocols/gossipsub/src/behaviour.rs @@ -27,7 +27,7 @@ use crate::protocol::{ }; use crate::topic::{Topic, TopicHash}; use futures::prelude::*; -use libp2p_core::{ConnectedPoint, Multiaddr, PeerId, connection::ConnectionId}; +use libp2p_core::{Multiaddr, PeerId, connection::ConnectionId}; use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, @@ -1012,7 +1012,7 @@ impl NetworkBehaviour for Gossipsub { Vec::new() } - fn inject_connected(&mut self, id: PeerId, _: ConnectedPoint) { + fn inject_connected(&mut self, id: &PeerId) { info!("New peer connected: {:?}", id); // We need to send our subscriptions to the newly-connected node. let mut subscriptions = vec![]; @@ -1040,7 +1040,7 @@ impl NetworkBehaviour for Gossipsub { self.peer_topics.insert(id.clone(), Vec::new()); } - fn inject_disconnected(&mut self, id: &PeerId, _: ConnectedPoint) { + fn inject_disconnected(&mut self, id: &PeerId) { // remove from mesh, topic_peers, peer_topic and fanout debug!("Peer disconnected: {:?}", id); { @@ -1164,8 +1164,8 @@ impl NetworkBehaviour for Gossipsub { NetworkBehaviourAction::DialAddress { address } => { return Poll::Ready(NetworkBehaviourAction::DialAddress { address }); } - NetworkBehaviourAction::DialPeer { peer_id } => { - return Poll::Ready(NetworkBehaviourAction::DialPeer { peer_id }); + NetworkBehaviourAction::DialPeer { peer_id, condition } => { + return Poll::Ready(NetworkBehaviourAction::DialPeer { peer_id, condition }); } NetworkBehaviourAction::ReportObservedAddr { address } => { return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { address }); diff --git a/protocols/gossipsub/src/behaviour/tests.rs b/protocols/gossipsub/src/behaviour/tests.rs index a3ca6ea5..e2073151 100644 --- a/protocols/gossipsub/src/behaviour/tests.rs +++ b/protocols/gossipsub/src/behaviour/tests.rs @@ -49,17 +49,13 @@ mod tests { // build and connect peer_no random peers let mut peers = vec![]; - let dummy_connected_point = ConnectedPoint::Dialer { - address: "/ip4/0.0.0.0/tcp/0".parse().unwrap(), - }; for _ in 0..peer_no { let peer = PeerId::random(); peers.push(peer.clone()); ::inject_connected( &mut gs, - peer.clone(), - dummy_connected_point.clone(), + &peer, ); if to_subscribe { gs.handle_received_subscriptions( diff --git a/protocols/identify/src/identify.rs b/protocols/identify/src/identify.rs index fe78bc1b..312e273d 100644 --- a/protocols/identify/src/identify.rs +++ b/protocols/identify/src/identify.rs @@ -37,7 +37,13 @@ use libp2p_swarm::{ ProtocolsHandler, ProtocolsHandlerUpgrErr }; -use std::{collections::HashMap, collections::VecDeque, io, pin::Pin, task::Context, task::Poll}; +use std::{ + collections::{HashMap, VecDeque}, + io, + pin::Pin, + task::Context, + task::Poll +}; /// Network behaviour that automatically identifies nodes periodically, returns information /// about them, and answers identify queries from other nodes. @@ -49,7 +55,7 @@ pub struct Identify { /// The public key of the local node. To report on the wire. local_public_key: PublicKey, /// For each peer we're connected to, the observed address to send back to it. - observed_addresses: HashMap, + observed_addresses: HashMap>, /// Pending replies to send. pending_replies: VecDeque, /// Pending events to be emitted when polled. @@ -97,23 +103,32 @@ impl NetworkBehaviour for Identify { Vec::new() } - fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { - let observed = match endpoint { - ConnectedPoint::Dialer { address } => address, - ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr, - }; - - self.observed_addresses.insert(peer_id, observed); + fn inject_connected(&mut self, _: &PeerId) { } - fn inject_disconnected(&mut self, peer_id: &PeerId, _: ConnectedPoint) { + fn inject_connection_established(&mut self, peer_id: &PeerId, conn: &ConnectionId, endpoint: &ConnectedPoint) { + let addr = match endpoint { + ConnectedPoint::Dialer { address } => address.clone(), + ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr.clone(), + }; + + self.observed_addresses.entry(peer_id.clone()).or_default().insert(*conn, addr); + } + + fn inject_connection_closed(&mut self, peer_id: &PeerId, conn: &ConnectionId, _: &ConnectedPoint) { + if let Some(addrs) = self.observed_addresses.get_mut(peer_id) { + addrs.remove(conn); + } + } + + fn inject_disconnected(&mut self, peer_id: &PeerId) { self.observed_addresses.remove(peer_id); } fn inject_event( &mut self, peer_id: PeerId, - _connection: ConnectionId, + connection: ConnectionId, event: ::OutEvent, ) { match event { @@ -132,9 +147,9 @@ impl NetworkBehaviour for Identify { } IdentifyHandlerEvent::Identify(sender) => { let observed = self.observed_addresses.get(&peer_id) - .expect("We only receive events from nodes we're connected to. We insert \ - into the hashmap when we connect to a node and remove only when we \ - disconnect; QED"); + .and_then(|addrs| addrs.get(&connection)) + .expect("`inject_event` is only called with an established connection \ + and `inject_connection_established` ensures there is an entry; qed"); self.pending_replies.push_back( Reply::Queued { peer: peer_id, diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index d00d8e8b..fab9fbd6 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -33,6 +33,7 @@ use crate::record::{self, store::{self, RecordStore}, Record, ProviderRecord}; use fnv::{FnvHashMap, FnvHashSet}; use libp2p_core::{ConnectedPoint, Multiaddr, PeerId, connection::ConnectionId}; use libp2p_swarm::{ + DialPeerCondition, NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, @@ -343,6 +344,7 @@ where kbucket::InsertResult::Pending { disconnected } => { self.queued_events.push_back(NetworkBehaviourAction::DialPeer { peer_id: disconnected.into_preimage(), + condition: DialPeerCondition::Disconnected }) }, } @@ -675,6 +677,7 @@ where debug_assert!(!self.connected_peers.contains(disconnected.preimage())); self.queued_events.push_back(NetworkBehaviourAction::DialPeer { peer_id: disconnected.into_preimage(), + condition: DialPeerCondition::Disconnected }) }, } @@ -1100,12 +1103,25 @@ where peer_addrs } - fn inject_connected(&mut self, peer: PeerId, endpoint: ConnectedPoint) { + fn inject_connection_established(&mut self, peer: &PeerId, _: &ConnectionId, endpoint: &ConnectedPoint) { + // The remote's address can only be put into the routing table, + // and thus shared with other nodes, if the local node is the dialer, + // since the remote address on an inbound connection is specific to + // that connection (e.g. typically the TCP port numbers). + let address = match endpoint { + ConnectedPoint::Dialer { address } => Some(address.clone()), + ConnectedPoint::Listener { .. } => None, + }; + + self.connection_updated(peer.clone(), address, NodeStatus::Connected); + } + + fn inject_connected(&mut self, peer: &PeerId) { // Queue events for sending pending RPCs to the connected peer. // There can be only one pending RPC for a particular peer and query per definition. for (peer_id, event) in self.queries.iter_mut().filter_map(|q| q.inner.pending_rpcs.iter() - .position(|(p, _)| p == &peer) + .position(|(p, _)| p == peer) .map(|p| q.inner.pending_rpcs.remove(p))) { self.queued_events.push_back(NetworkBehaviourAction::NotifyHandler { @@ -1113,17 +1129,7 @@ where }); } - // The remote's address can only be put into the routing table, - // and thus shared with other nodes, if the local node is the dialer, - // since the remote address on an inbound connection is specific to - // that connection (e.g. typically the TCP port numbers). - let address = match endpoint { - ConnectedPoint::Dialer { address } => Some(address), - ConnectedPoint::Listener { .. } => None, - }; - - self.connection_updated(peer.clone(), address, NodeStatus::Connected); - self.connected_peers.insert(peer); + self.connected_peers.insert(peer.clone()); } fn inject_addr_reach_failure( @@ -1173,7 +1179,7 @@ where } } - fn inject_disconnected(&mut self, id: &PeerId, _old_endpoint: ConnectedPoint) { + fn inject_disconnected(&mut self, id: &PeerId) { for query in self.queries.iter_mut() { query.on_failure(id); } @@ -1441,7 +1447,7 @@ where } else if &peer_id != self.kbuckets.local_key().preimage() { query.inner.pending_rpcs.push((peer_id.clone(), event)); self.queued_events.push_back(NetworkBehaviourAction::DialPeer { - peer_id + peer_id, condition: DialPeerCondition::Disconnected }); } } diff --git a/protocols/mdns/src/behaviour.rs b/protocols/mdns/src/behaviour.rs index 85685f72..abc580ff 100644 --- a/protocols/mdns/src/behaviour.rs +++ b/protocols/mdns/src/behaviour.rs @@ -21,7 +21,6 @@ use crate::service::{MdnsService, MdnsPacket, build_query_response, build_service_discovery_response}; use futures::prelude::*; use libp2p_core::{ - ConnectedPoint, Multiaddr, PeerId, address_translation, @@ -199,9 +198,9 @@ impl NetworkBehaviour for Mdns { .collect() } - fn inject_connected(&mut self, _: PeerId, _: ConnectedPoint) {} + fn inject_connected(&mut self, _: &PeerId) {} - fn inject_disconnected(&mut self, _: &PeerId, _: ConnectedPoint) {} + fn inject_disconnected(&mut self, _: &PeerId) {} fn inject_event( &mut self, diff --git a/protocols/ping/src/lib.rs b/protocols/ping/src/lib.rs index 69126644..82b828ad 100644 --- a/protocols/ping/src/lib.rs +++ b/protocols/ping/src/lib.rs @@ -47,7 +47,7 @@ pub mod handler; pub use handler::{PingConfig, PingResult, PingSuccess, PingFailure}; use handler::PingHandler; -use libp2p_core::{ConnectedPoint, Multiaddr, PeerId, connection::ConnectionId}; +use libp2p_core::{Multiaddr, PeerId, connection::ConnectionId}; use libp2p_swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}; use std::{collections::VecDeque, task::Context, task::Poll}; use void::Void; @@ -100,9 +100,9 @@ impl NetworkBehaviour for Ping { Vec::new() } - fn inject_connected(&mut self, _: PeerId, _: ConnectedPoint) {} + fn inject_connected(&mut self, _: &PeerId) {} - fn inject_disconnected(&mut self, _: &PeerId, _: ConnectedPoint) {} + fn inject_disconnected(&mut self, _: &PeerId) {} fn inject_event(&mut self, peer: PeerId, _: ConnectionId, result: PingResult) { self.events.push_front(PingEvent { peer, result }) diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 97497b8a..e7deaf75 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -72,18 +72,34 @@ pub trait NetworkBehaviour: Send + 'static { /// address should be the most likely to be reachable. fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec; - /// Indicates the behaviour that we connected to the node with the given peer id through the - /// given endpoint. + /// Indicates the behaviour that we connected to the node with the given peer id. /// /// This node now has a handler (as spawned by `new_handler`) running in the background. - fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint); + /// + /// This method is only called when the connection to the peer is + /// established, preceded by `inject_connection_established`. + fn inject_connected(&mut self, peer_id: &PeerId); - /// Indicates the behaviour that we disconnected from the node with the given peer id. The - /// endpoint is the one we used to be connected to. + /// Indicates the behaviour that we disconnected from the node with the given peer id. /// /// There is no handler running anymore for this node. Any event that has been sent to it may /// or may not have been processed by the handler. - fn inject_disconnected(&mut self, peer_id: &PeerId, endpoint: ConnectedPoint); + /// + /// This method is only called when the last established connection to the peer + /// is closed, preceded by `inject_connection_closed`. + fn inject_disconnected(&mut self, peer_id: &PeerId); + + /// Informs the behaviour about a newly established connection to a peer. + fn inject_connection_established(&mut self, _: &PeerId, _: &ConnectionId, _: &ConnectedPoint) + {} + + /// Informs the behaviour about a closed connection to a peer. + /// + /// A call to this method is always paired with an earlier call to + /// `inject_connection_established` with the same peer ID, connection ID and + /// endpoint. + fn inject_connection_closed(&mut self, _: &PeerId, _: &ConnectionId, _: &ConnectedPoint) + {} /// Informs the behaviour about an event generated by the handler dedicated to the peer identified by `peer_id`. /// for the behaviour. @@ -204,6 +220,8 @@ pub enum NetworkBehaviourAction { DialPeer { /// The peer to try reach. peer_id: PeerId, + /// The condition for initiating a new dialing attempt. + condition: DialPeerCondition, }, /// Instructs the `Swarm` to send an event to the handler dedicated to a @@ -253,3 +271,37 @@ pub enum NotifyHandler { All } +/// The available conditions under which a new dialing attempt to +/// a peer is initiated when requested by [`NetworkBehaviourAction::DialPeer`]. +#[derive(Debug, Copy, Clone)] +#[non_exhaustive] +pub enum DialPeerCondition { + /// A new dialing attempt is initiated _only if_ the peer is currently + /// considered disconnected, i.e. there is no established connection + /// and no ongoing dialing attempt. + /// + /// If there is an ongoing dialing attempt, the addresses reported by + /// [`NetworkBehaviour::addresses_of_peer`] are added to the ongoing + /// dialing attempt, ignoring duplicates. + Disconnected, + /// A new dialing attempt is initiated _only if_ there is currently + /// no ongoing dialing attempt, i.e. the peer is either considered + /// disconnected or connected but without an ongoing dialing attempt. + /// + /// If there is an ongoing dialing attempt, the addresses reported by + /// [`NetworkBehaviour::addresses_of_peer`] are added to the ongoing + /// dialing attempt, ignoring duplicates. + /// + /// This condition implies [`DialPeerCondition::Disconnected`]. + NotDialing, + // TODO: Once multiple dialing attempts per peer are permitted. + // See https://github.com/libp2p/rust-libp2p/pull/1506. + // Always, +} + +impl Default for DialPeerCondition { + fn default() -> Self { + DialPeerCondition::Disconnected + } +} + diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index b9a2e6dc..0d4d1255 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -65,7 +65,8 @@ pub use behaviour::{ NetworkBehaviourAction, NetworkBehaviourEventProcess, PollParameters, - NotifyHandler + NotifyHandler, + DialPeerCondition }; pub use protocols_handler::{ IntoProtocolsHandler, @@ -89,7 +90,6 @@ use futures::{ stream::FusedStream, }; use libp2p_core::{ - ConnectedPoint, Executor, Transport, Multiaddr, @@ -99,6 +99,8 @@ use libp2p_core::{ ConnectionError, ConnectionId, ConnectionInfo, + ConnectionLimit, + ConnectedPoint, EstablishedConnection, IntoConnectionHandler, ListenerId, @@ -108,7 +110,6 @@ use libp2p_core::{ transport::{TransportError, boxed::Boxed as BoxTransport}, muxing::{StreamMuxer, StreamMuxerBox}, network::{ - DialError, Network, NetworkInfo, NetworkEvent, @@ -201,12 +202,6 @@ pub enum SwarmEvent { /// Endpoint of the connection that has been closed. endpoint: ConnectedPoint, }, - /// Starting to try to reach the given peer. - /// - /// We are trying to connect to this peer until a [`ConnectionEstablished`](SwarmEvent::ConnectionEstablished) - /// event is reported, or a [`UnreachableAddr`](SwarmEvent::UnreachableAddr) event is reported - /// with `attempts_remaining` equal to 0. - Dialing(PeerId), /// Tried to dial an address but it ended up being unreachaable. UnreachableAddr { /// `PeerId` that we were trying to reach. @@ -246,6 +241,13 @@ pub enum SwarmEvent { /// The listener error. error: io::Error, }, + /// A new dialing attempt has been initiated. + /// + /// A [`ConnectionEstablished`](SwarmEvent::ConnectionEstablished) + /// event is reported if the dialing attempt succeeds, otherwise a + /// [`UnreachableAddr`](SwarmEvent::UnreachableAddr) event is reported + /// with `attempts_remaining` equal to 0. + Dialing(PeerId), } /// Contains the state of the network, plus the way it should behave. @@ -367,31 +369,65 @@ where TBehaviour: NetworkBehaviour, /// Tries to dial the given address. /// /// Returns an error if the address is not supported. - pub fn dial_addr(me: &mut Self, addr: Multiaddr) -> Result<(), DialError> { + pub fn dial_addr(me: &mut Self, addr: Multiaddr) -> Result<(), ConnectionLimit> { let handler = me.behaviour.new_handler(); me.network.dial(&addr, handler.into_node_handler_builder()).map(|_id| ()) } - /// Tries to reach the given peer using the elements in the topology. + /// Tries to initiate a dialing attempt to the given peer. /// - /// Has no effect if we are already connected to that peer, or if no address is known for the - /// peer. - pub fn dial(me: &mut Self, peer_id: PeerId) { - let addrs = me.behaviour.addresses_of_peer(&peer_id); + /// If a new dialing attempt has been initiated, `Ok(true)` is returned. + /// + /// If there is an ongoing dialing attempt, the current addresses of the + /// peer, as reported by [`NetworkBehaviour::addresses_of_peer`] are added + /// to the ongoing dialing attempt, ignoring duplicates. In this case no + /// new dialing attempt is initiated. + /// + /// If no new dialing attempt has been initiated, meaning there is an ongoing + /// dialing attempt or `addresses_of_peer` reports no addresses, `Ok(false)` + /// is returned. + pub fn dial(me: &mut Self, peer_id: &PeerId) -> Result { + let mut addrs = me.behaviour.addresses_of_peer(peer_id).into_iter(); match me.network.peer(peer_id.clone()) { Peer::Disconnected(peer) => { - let mut addrs = addrs.into_iter(); if let Some(first) = addrs.next() { let handler = me.behaviour.new_handler().into_node_handler_builder(); - if peer.connect(first, addrs, handler).is_err() { - me.behaviour.inject_dial_failure(&peer_id); + match peer.connect(first, addrs, handler) { + Ok(_) => return Ok(true), + Err(error) => { + log::debug!( + "New dialing attempt to disconnected peer {:?} failed: {:?}.", + peer_id, error); + me.behaviour.inject_dial_failure(&peer_id); + return Err(error) + } } } + Ok(false) }, + Peer::Connected(peer) => { + if let Some(first) = addrs.next() { + let handler = me.behaviour.new_handler().into_node_handler_builder(); + match peer.connect(first, addrs, handler) { + Ok(_) => return Ok(true), + Err(error) => { + log::debug!( + "New dialing attempt to connected peer {:?} failed: {:?}.", + peer_id, error); + me.behaviour.inject_dial_failure(&peer_id); + return Err(error) + } + } + } + Ok(false) + } Peer::Dialing(mut peer) => { - peer.connection().add_addresses(addrs) + peer.connection().add_addresses(addrs); + Ok(false) }, - Peer::Connected(_) | Peer::Local => {} + Peer::Local => { + Err(ConnectionLimit { current: 0, limit: 0 }) + } } } @@ -498,35 +534,29 @@ where TBehaviour: NetworkBehaviour, peer_id, endpoint, }); - } else if num_established.get() == 1 { - this.behaviour.inject_connected(peer_id.clone(), endpoint.clone()); - return Poll::Ready(SwarmEvent::ConnectionEstablished { - peer_id, - endpoint, - num_established, - }); } else { - // For now, secondary connections are not explicitly reported to - // the behaviour. A behaviour only gets awareness of the - // connections via the events emitted from the connection handlers. - log::trace!("Secondary connection established: {:?}; Total (peer): {}.", + log::debug!("Connection established: {:?}; Total (peer): {}.", connection.connected(), num_established); + let endpoint = connection.endpoint().clone(); + this.behaviour.inject_connection_established(&peer_id, &connection.id(), &endpoint); + if num_established.get() == 1 { + this.behaviour.inject_connected(&peer_id); + } return Poll::Ready(SwarmEvent::ConnectionEstablished { - peer_id, - endpoint, - num_established, + peer_id, num_established, endpoint }); } }, - Poll::Ready(NetworkEvent::ConnectionError { connected, error, num_established }) => { - log::debug!("Connection {:?} closed by {:?}", connected, error); - let peer_id = connected.peer_id().clone(); + Poll::Ready(NetworkEvent::ConnectionError { id, connected, error, num_established }) => { + log::debug!("Connection {:?} closed: {:?}", connected, error); + let info = connected.info; let endpoint = connected.endpoint; + this.behaviour.inject_connection_closed(info.peer_id(), &id, &endpoint); if num_established == 0 { - this.behaviour.inject_disconnected(&peer_id, endpoint.clone()); + this.behaviour.inject_disconnected(info.peer_id()); } return Poll::Ready(SwarmEvent::ConnectionClosed { - peer_id, + peer_id: info.peer_id().clone(), endpoint, cause: error, num_established, @@ -663,12 +693,40 @@ where TBehaviour: NetworkBehaviour, Poll::Ready(NetworkBehaviourAction::DialAddress { address }) => { let _ = ExpandedSwarm::dial_addr(&mut *this, address); }, - Poll::Ready(NetworkBehaviourAction::DialPeer { peer_id }) => { + Poll::Ready(NetworkBehaviourAction::DialPeer { peer_id, condition }) => { if this.banned_peers.contains(&peer_id) { this.behaviour.inject_dial_failure(&peer_id); } else { - ExpandedSwarm::dial(&mut *this, peer_id.clone()); - return Poll::Ready(SwarmEvent::Dialing(peer_id)) + let result = match condition { + DialPeerCondition::Disconnected + if this.network.is_disconnected(&peer_id) => + { + ExpandedSwarm::dial(this, &peer_id) + } + DialPeerCondition::NotDialing + if !this.network.is_dialing(&peer_id) => + { + ExpandedSwarm::dial(this, &peer_id) + } + _ => { + log::trace!("Condition for new dialing attempt to {:?} not met: {:?}", + peer_id, condition); + if let Some(mut peer) = this.network.peer(peer_id.clone()).into_dialing() { + let addrs = this.behaviour.addresses_of_peer(peer.id()); + peer.connection().add_addresses(addrs); + } + Ok(false) + } + }; + match result { + Ok(false) => {}, + Ok(true) => return Poll::Ready(SwarmEvent::Dialing(peer_id)), + Err(err) => { + log::debug!("Initiating dialing attempt to {:?} failed: {:?}", + &peer_id, err); + this.behaviour.inject_dial_failure(&peer_id); + } + } } }, Poll::Ready(NetworkBehaviourAction::NotifyHandler { peer_id, handler, event }) => { @@ -922,28 +980,33 @@ impl<'a> PollParameters for SwarmPollParameters<'a> { } } +/// A `SwarmBuilder` provides an API for configuring and constructing a `Swarm`, +/// including the underlying [`Network`]. pub struct SwarmBuilder { local_peer_id: PeerId, transport: BoxTransport<(TConnInfo, StreamMuxerBox), io::Error>, behaviour: TBehaviour, - network: NetworkConfig, + network_config: NetworkConfig, } impl SwarmBuilder where TBehaviour: NetworkBehaviour, TConnInfo: ConnectionInfo + fmt::Debug + Clone + Send + 'static, { - pub fn new(transport: TTransport, behaviour: TBehaviour, local_peer_id: PeerId) -> Self + /// Creates a new `SwarmBuilder` from the given transport, behaviour and + /// local peer ID. The `Swarm` with its underlying `Network` is obtained + /// via [`SwarmBuilder::build`]. + pub fn new(transport: TTrans, behaviour: TBehaviour, local_peer_id: PeerId) -> Self where TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send + 'static, ::OutboundSubstream: Send + 'static, ::Substream: Send + 'static, - TTransport: Transport + Clone + Send + Sync + 'static, - TTransport::Error: Send + Sync + 'static, - TTransport::Listener: Send + 'static, - TTransport::ListenerUpgrade: Send + 'static, - TTransport::Dial: Send + 'static, + TTrans: Transport + Clone + Send + Sync + 'static, + TTrans::Error: Send + Sync + 'static, + TTrans::Listener: Send + 'static, + TTrans::ListenerUpgrade: Send + 'static, + TTrans::Dial: Send + 'static, { let transport = transport .map(|(conn_info, muxer), _| (conn_info, StreamMuxerBox::new(muxer))) @@ -954,35 +1017,41 @@ where TBehaviour: NetworkBehaviour, local_peer_id, transport, behaviour, - network: NetworkConfig::default(), + network_config: Default::default(), } } - pub fn incoming_limit(mut self, incoming_limit: usize) -> Self { - self.network.set_pending_incoming_limit(incoming_limit); - self - } - - /// Sets the executor to use to spawn background tasks. + /// Configures the `Executor` to use for spawning background tasks. /// - /// By default, uses a threads pool. - pub fn executor(mut self, executor: impl Executor + Send + 'static) -> Self { - self.network.set_executor(Box::new(executor)); + /// By default, unless another executor has been configured, + /// [`SwarmBuilder::build`] will try to set up a `ThreadPool`. + pub fn executor(mut self, e: Box) -> Self { + self.network_config.set_executor(e); self } - /// Shortcut for calling `executor` with an object that calls the given closure. - pub fn executor_fn(mut self, executor: impl Fn(Pin + Send>>) + Send + 'static) -> Self { - struct SpawnImpl(F); - impl + Send>>)> Executor for SpawnImpl { - fn exec(&self, f: Pin + Send>>) { - (self.0)(f) - } - } - self.network.set_executor(Box::new(SpawnImpl(executor))); + /// Configures a limit for the number of simultaneous incoming + /// connection attempts. + pub fn incoming_connection_limit(mut self, n: usize) -> Self { + self.network_config.set_incoming_limit(n); self } + /// Configures a limit for the number of simultaneous outgoing + /// connection attempts. + pub fn outgoing_connection_limit(mut self, n: usize) -> Self { + self.network_config.set_outgoing_limit(n); + self + } + + /// Configures a limit for the number of simultaneous + /// established connections per peer. + pub fn peer_connection_limit(mut self, n: usize) -> Self { + self.network_config.set_established_per_peer_limit(n); + self + } + + /// Builds a `Swarm` with the current configuration. pub fn build(mut self) -> Swarm { let supported_protocols = self.behaviour .new_handler() @@ -992,9 +1061,10 @@ where TBehaviour: NetworkBehaviour, .map(|info| info.protocol_name().to_vec()) .collect(); - // If no executor has been explicitly configured, try to set up - // a thread pool. - if self.network.executor().is_none() { + let mut network_cfg = self.network_config; + + // If no executor has been explicitly configured, try to set up a thread pool. + if network_cfg.executor().is_none() { struct PoolWrapper(ThreadPool); impl Executor for PoolWrapper { fn exec(&self, f: Pin + Send>>) { @@ -1002,21 +1072,17 @@ where TBehaviour: NetworkBehaviour, } } - if let Some(executor) = ThreadPoolBuilder::new() - .name_prefix("libp2p-task-") + match ThreadPoolBuilder::new() + .name_prefix("libp2p-swarm-task-") .create() - .ok() .map(|tp| Box::new(PoolWrapper(tp)) as Box<_>) { - self.network.set_executor(Box::new(executor)); + Ok(executor) => { network_cfg.set_executor(Box::new(executor)); }, + Err(err) => log::warn!("Failed to create executor thread pool: {:?}", err) } } - let network = Network::new( - self.transport, - self.local_peer_id, - self.network, - ); + let network = Network::new(self.transport, self.local_peer_id, network_cfg); ExpandedSwarm { network, @@ -1047,9 +1113,13 @@ impl NetworkBehaviour for DummyBehaviour { Vec::new() } - fn inject_connected(&mut self, _: PeerId, _: libp2p_core::ConnectedPoint) {} + fn inject_connected(&mut self, _: &PeerId) {} - fn inject_disconnected(&mut self, _: &PeerId, _: libp2p_core::ConnectedPoint) {} + fn inject_connection_established(&mut self, _: &PeerId, _: &ConnectionId, _: &ConnectedPoint) {} + + fn inject_disconnected(&mut self, _: &PeerId) {} + + fn inject_connection_closed(&mut self, _: &PeerId, _: &ConnectionId, _: &ConnectedPoint) {} fn inject_event(&mut self, _: PeerId, _: ConnectionId, _: ::OutEvent) {} @@ -1067,9 +1137,9 @@ impl NetworkBehaviour for DummyBehaviour { mod tests { use crate::{DummyBehaviour, SwarmBuilder}; use libp2p_core::{ - identity, PeerId, PublicKey, + identity, transport::dummy::{DummyStream, DummyTransport} }; use libp2p_mplex::Multiplex; @@ -1084,7 +1154,8 @@ mod tests { let transport = DummyTransport::<(PeerId, Multiplex)>::new(); let behaviour = DummyBehaviour {}; let swarm = SwarmBuilder::new(transport, behaviour, id.into()) - .incoming_limit(4).build(); + .incoming_connection_limit(4) + .build(); assert_eq!(swarm.network.incoming_limit(), Some(4)); } diff --git a/swarm/src/protocols_handler/node_handler.rs b/swarm/src/protocols_handler/node_handler.rs index 3191ca9e..8b045061 100644 --- a/swarm/src/protocols_handler/node_handler.rs +++ b/swarm/src/protocols_handler/node_handler.rs @@ -133,10 +133,10 @@ enum Shutdown { /// Error generated by the `NodeHandlerWrapper`. #[derive(Debug)] pub enum NodeHandlerWrapperError { - /// Error generated by the handler. + /// The connection handler encountered an error. Handler(TErr), - /// The connection has been deemed useless and has been closed. - UselessTimeout, + /// The connection keep-alive timeout expired. + KeepAliveTimeout, } impl From for NodeHandlerWrapperError { @@ -152,8 +152,8 @@ where fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NodeHandlerWrapperError::Handler(err) => write!(f, "{}", err), - NodeHandlerWrapperError::UselessTimeout => - write!(f, "Node has been closed due to inactivity"), + NodeHandlerWrapperError::KeepAliveTimeout => + write!(f, "Connection closed due to expired keep-alive timeout."), } } } @@ -165,7 +165,7 @@ where fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { NodeHandlerWrapperError::Handler(err) => Some(err), - NodeHandlerWrapperError::UselessTimeout => None, + NodeHandlerWrapperError::KeepAliveTimeout => None, } } } @@ -314,9 +314,9 @@ where if self.negotiating_in.is_empty() && self.negotiating_out.is_empty() { match self.shutdown { Shutdown::None => {}, - Shutdown::Asap => return Poll::Ready(Err(NodeHandlerWrapperError::UselessTimeout)), + Shutdown::Asap => return Poll::Ready(Err(NodeHandlerWrapperError::KeepAliveTimeout)), Shutdown::Later(ref mut delay, _) => match Future::poll(Pin::new(delay), cx) { - Poll::Ready(_) => return Poll::Ready(Err(NodeHandlerWrapperError::UselessTimeout)), + Poll::Ready(_) => return Poll::Ready(Err(NodeHandlerWrapperError::KeepAliveTimeout)), Poll::Pending => {} } } diff --git a/swarm/src/toggle.rs b/swarm/src/toggle.rs index f38e8b06..2ba58e95 100644 --- a/swarm/src/toggle.rs +++ b/swarm/src/toggle.rs @@ -76,15 +76,27 @@ where self.inner.as_mut().map(|b| b.addresses_of_peer(peer_id)).unwrap_or_else(Vec::new) } - fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { + fn inject_connected(&mut self, peer_id: &PeerId) { if let Some(inner) = self.inner.as_mut() { - inner.inject_connected(peer_id, endpoint) + inner.inject_connected(peer_id) } } - fn inject_disconnected(&mut self, peer_id: &PeerId, endpoint: ConnectedPoint) { + fn inject_disconnected(&mut self, peer_id: &PeerId) { if let Some(inner) = self.inner.as_mut() { - inner.inject_disconnected(peer_id, endpoint) + inner.inject_disconnected(peer_id) + } + } + + fn inject_connection_established(&mut self, peer_id: &PeerId, connection: &ConnectionId, endpoint: &ConnectedPoint) { + if let Some(inner) = self.inner.as_mut() { + inner.inject_connection_established(peer_id, connection, endpoint) + } + } + + fn inject_connection_closed(&mut self, peer_id: &PeerId, connection: &ConnectionId, endpoint: &ConnectedPoint) { + if let Some(inner) = self.inner.as_mut() { + inner.inject_connection_closed(peer_id, connection, endpoint) } } From 6b4bdc1fe9e726331621c14c41a280eeebd97f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Go=C5=82=C4=85b?= Date: Wed, 1 Apr 2020 13:07:10 +0200 Subject: [PATCH 04/22] Allow configuration of outbound substream in OneShotHandler. (#1521) Co-authored-by: Pierre Krieger --- swarm/src/protocols_handler/one_shot.rs | 38 +++++++++++++++++++------ 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/swarm/src/protocols_handler/one_shot.rs b/swarm/src/protocols_handler/one_shot.rs index dcb731ca..3bcac1e7 100644 --- a/swarm/src/protocols_handler/one_shot.rs +++ b/swarm/src/protocols_handler/one_shot.rs @@ -54,8 +54,8 @@ where max_dial_negotiated: u32, /// Value to return from `connection_keep_alive`. keep_alive: KeepAlive, - /// After the given duration has elapsed, an inactive connection will shutdown. - inactive_timeout: Duration, + /// The configuration container for the handler + config: OneShotHandlerConfig, } impl @@ -67,7 +67,7 @@ where #[inline] pub fn new( listen_protocol: SubstreamProtocol, - inactive_timeout: Duration + config: OneShotHandlerConfig, ) -> Self { OneShotHandler { listen_protocol, @@ -77,7 +77,7 @@ where dial_negotiated: 0, max_dial_negotiated: 8, keep_alive: KeepAlive::Yes, - inactive_timeout, + config } } @@ -121,7 +121,10 @@ where { #[inline] fn default() -> Self { - OneShotHandler::new(SubstreamProtocol::new(Default::default()), Duration::from_secs(10)) + OneShotHandler::new( + SubstreamProtocol::new(Default::default()), + OneShotHandlerConfig::default() + ) } } @@ -157,7 +160,7 @@ where ) { // If we're shutting down the connection for inactivity, reset the timeout. if !self.keep_alive.is_yes() { - self.keep_alive = KeepAlive::Until(Instant::now() + self.inactive_timeout); + self.keep_alive = KeepAlive::Until(Instant::now() + self.config.inactive_timeout); } self.events_out.push(out.into()); @@ -172,7 +175,7 @@ where self.dial_negotiated -= 1; if self.dial_negotiated == 0 && self.dial_queue.is_empty() { - self.keep_alive = KeepAlive::Until(Instant::now() + self.inactive_timeout); + self.keep_alive = KeepAlive::Until(Instant::now() + self.config.inactive_timeout); } self.events_out.push(out.into()); @@ -224,7 +227,8 @@ where self.dial_negotiated += 1; return Poll::Ready( ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(self.dial_queue.remove(0)), + protocol: SubstreamProtocol::new(self.dial_queue.remove(0)) + .with_timeout(self.config.substream_timeout), info: (), }, ); @@ -236,3 +240,21 @@ where Poll::Pending } } + +/// Configuration parameters for the `OneShotHandler` +#[derive(Debug)] +pub struct OneShotHandlerConfig { + /// After the given duration has elapsed, an inactive connection will shutdown. + inactive_timeout: Duration, + /// Timeout duration for each newly opened outbound substream. + substream_timeout: Duration, +} + +impl Default for OneShotHandlerConfig { + fn default() -> Self { + let inactive_timeout = Duration::from_secs(10); + let substream_timeout = Duration::from_secs(10); + OneShotHandlerConfig { inactive_timeout, substream_timeout } + } +} + From 3f12a5ceaa74c60be745489dc5a2867e4d75a278 Mon Sep 17 00:00:00 2001 From: Toralf Wittner Date: Wed, 1 Apr 2020 14:28:59 +0200 Subject: [PATCH 05/22] mplex: Check for error and shutdown. (#1529) * mplex: Check for error and shutdown. Issues #1504 and #1523 reported panics caused by polling the sink of `secio::LenPrefixCodec` after it had entered its terminal state, i.e. after it had previously encountered an error or was closed. According to the reports this happened only when using mplex as a stream multiplexer. It seems that because mplex always stores and keeps the `Waker` when polling, a wakeup of any of those wakers will resume the polling even for those cases where the previous poll did not return `Poll::Pending` but resolved to a value. To prevent polling after the connection was closed or an error happened we check for those conditions prior to every poll. * Keep error when operations fail. Co-authored-by: Pierre Krieger --- muxers/mplex/src/lib.rs | 68 ++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/muxers/mplex/src/lib.rs b/muxers/mplex/src/lib.rs index 64c532f8..d8350606 100644 --- a/muxers/mplex/src/lib.rs +++ b/muxers/mplex/src/lib.rs @@ -53,7 +53,6 @@ pub struct MplexConfig { impl MplexConfig { /// Builds the default configuration. - #[inline] pub fn new() -> MplexConfig { Default::default() } @@ -62,7 +61,6 @@ impl MplexConfig { /// generated and the connection closes. /// /// A limit is necessary in order to avoid DoS attacks. - #[inline] pub fn max_substreams(&mut self, max: usize) -> &mut Self { self.max_substreams = max; self @@ -71,7 +69,6 @@ impl MplexConfig { /// Sets the maximum number of pending incoming messages. /// /// A limit is necessary in order to avoid DoS attacks. - #[inline] pub fn max_buffer_len(&mut self, max: usize) -> &mut Self { self.max_buffer_len = max; self @@ -80,7 +77,6 @@ impl MplexConfig { /// Sets the behaviour when the maximum buffer length has been reached. /// /// See the documentation of `MaxBufferBehaviour`. - #[inline] pub fn max_buffer_len_behaviour(&mut self, behaviour: MaxBufferBehaviour) -> &mut Self { self.max_buffer_behaviour = behaviour; self @@ -94,7 +90,6 @@ impl MplexConfig { self } - #[inline] fn upgrade(self, i: C) -> Multiplex where C: AsyncRead + AsyncWrite + Unpin @@ -122,7 +117,6 @@ impl MplexConfig { } impl Default for MplexConfig { - #[inline] fn default() -> MplexConfig { MplexConfig { max_substreams: 128, @@ -149,7 +143,6 @@ impl UpgradeInfo for MplexConfig { type Info = &'static [u8]; type InfoIter = iter::Once; - #[inline] fn protocol_info(&self) -> Self::InfoIter { iter::once(b"/mplex/6.7.0") } @@ -334,9 +327,7 @@ where C: AsyncRead + AsyncWrite + Unpin, fn poll_send(inner: &mut MultiplexInner, cx: &mut Context, elem: codec::Elem) -> Poll> where C: AsyncRead + AsyncWrite + Unpin { - if inner.is_shutdown { - return Poll::Ready(Err(IoError::new(IoErrorKind::Other, "connection is shut down"))) - } + ensure_no_error_no_close(inner)?; inner.notifier_write.insert(cx.waker()); @@ -348,10 +339,26 @@ where C: AsyncRead + AsyncWrite + Unpin } }, Poll::Pending => Poll::Pending, - Poll::Ready(Err(err)) => Poll::Ready(Err(err)) + Poll::Ready(Err(err)) => { + inner.error = Err(IoError::new(err.kind(), err.to_string())); + Poll::Ready(Err(err)) + } } } +fn ensure_no_error_no_close(inner: &mut MultiplexInner) -> Result<(), IoError> +where + C: AsyncRead + AsyncWrite + Unpin +{ + if inner.is_shutdown { + return Err(IoError::new(IoErrorKind::Other, "connection is shut down")) + } + if let Err(ref e) = inner.error { + return Err(IoError::new(e.kind(), e.to_string())) + } + Ok(()) +} + impl StreamMuxer for Multiplex where C: AsyncRead + AsyncWrite + Unpin { @@ -418,9 +425,7 @@ where C: AsyncRead + AsyncWrite + Unpin poll_send(&mut inner, cx, elem.clone()) }, OutboundSubstreamState::Flush => { - if inner.is_shutdown { - return Poll::Ready(Err(IoError::new(IoErrorKind::Other, "connection is shut down"))) - } + ensure_no_error_no_close(&mut inner)?; let inner = &mut *inner; // Avoids borrow errors inner.notifier_write.insert(cx.waker()); Sink::poll_flush(Pin::new(&mut inner.inner), &mut Context::from_waker(&waker_ref(&inner.notifier_write))) @@ -438,6 +443,7 @@ where C: AsyncRead + AsyncWrite + Unpin inner.buffer.retain(|elem| { elem.substream_id() != substream.num || elem.endpoint() == Some(Endpoint::Dialer) }); + inner.error = Err(IoError::new(err.kind(), err.to_string())); return Poll::Ready(Err(err)); }, }; @@ -465,7 +471,6 @@ where C: AsyncRead + AsyncWrite + Unpin } } - #[inline] fn destroy_outbound(&self, _substream: Self::OutboundSubstream) { // Nothing to do. } @@ -548,13 +553,14 @@ where C: AsyncRead + AsyncWrite + Unpin fn flush_substream(&self, cx: &mut Context, _substream: &mut Self::Substream) -> Poll> { let mut inner = self.inner.lock(); - if inner.is_shutdown { - return Poll::Ready(Err(IoError::new(IoErrorKind::Other, "connection is shut down"))) - } - + ensure_no_error_no_close(&mut inner)?; let inner = &mut *inner; // Avoids borrow errors inner.notifier_write.insert(cx.waker()); - Sink::poll_flush(Pin::new(&mut inner.inner), &mut Context::from_waker(&waker_ref(&inner.notifier_write))) + let result = Sink::poll_flush(Pin::new(&mut inner.inner), &mut Context::from_waker(&waker_ref(&inner.notifier_write))); + if let Poll::Ready(Err(err)) = &result { + inner.error = Err(IoError::new(err.kind(), err.to_string())); + } + result } fn shutdown_substream(&self, cx: &mut Context, sub: &mut Self::Substream) -> Poll> { @@ -585,28 +591,42 @@ where C: AsyncRead + AsyncWrite + Unpin self.inner.lock().is_acknowledged } - #[inline] fn close(&self, cx: &mut Context) -> Poll> { let inner = &mut *self.inner.lock(); + if inner.is_shutdown { + return Poll::Ready(Ok(())) + } + if let Err(ref e) = inner.error { + return Poll::Ready(Err(IoError::new(e.kind(), e.to_string()))) + } inner.notifier_write.insert(cx.waker()); match Sink::poll_close(Pin::new(&mut inner.inner), &mut Context::from_waker(&waker_ref(&inner.notifier_write))) { Poll::Ready(Ok(())) => { inner.is_shutdown = true; Poll::Ready(Ok(())) } - Poll::Ready(Err(err)) => Poll::Ready(Err(err)), + Poll::Ready(Err(err)) => { + inner.error = Err(IoError::new(err.kind(), err.to_string())); + Poll::Ready(Err(err)) + } Poll::Pending => Poll::Pending, } } - #[inline] fn flush_all(&self, cx: &mut Context) -> Poll> { let inner = &mut *self.inner.lock(); if inner.is_shutdown { return Poll::Ready(Ok(())) } + if let Err(ref e) = inner.error { + return Poll::Ready(Err(IoError::new(e.kind(), e.to_string()))) + } inner.notifier_write.insert(cx.waker()); - Sink::poll_flush(Pin::new(&mut inner.inner), &mut Context::from_waker(&waker_ref(&inner.notifier_write))) + let result = Sink::poll_flush(Pin::new(&mut inner.inner), &mut Context::from_waker(&waker_ref(&inner.notifier_write))); + if let Poll::Ready(Err(err)) = &result { + inner.error = Err(IoError::new(err.kind(), err.to_string())); + } + result } } From b1059cd8018f02b97d475606fc775851e644fb17 Mon Sep 17 00:00:00 2001 From: Tobin Harding Date: Wed, 1 Apr 2020 22:49:42 +1000 Subject: [PATCH 06/22] Pass the error to inject_listener_closed method (#1517) * Pass the error to inject_listener_closed method If there is an error when the listener closes, found in the `NetworkEvent::ListenerClosed` `reason` field, we would like to pass it on to the `inject_listener_closed()` method so that implementors of this method have access to it. Add an error parameter to `inject_listener_closed`. Convert the `reason` field from a `Result` to an `Option` and if there is an error pass `Some(error)` at the method call site. * Pass 'reason' as a Result * Finish change Co-authored-by: Pierre Krieger --- misc/core-derive/src/lib.rs | 6 +++--- swarm/src/behaviour.rs | 3 +-- swarm/src/lib.rs | 5 ++++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/misc/core-derive/src/lib.rs b/misc/core-derive/src/lib.rs index 205d4c45..1153276f 100644 --- a/misc/core-derive/src/lib.rs +++ b/misc/core-derive/src/lib.rs @@ -271,8 +271,8 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { return None } Some(match field.ident { - Some(ref i) => quote!(self.#i.inject_listener_closed(id);), - None => quote!(self.#field_n.inject_listener_closed(id);) + Some(ref i) => quote!(self.#i.inject_listener_closed(id, reason);), + None => quote!(self.#field_n.inject_listener_closed(id, reason);) }) }) }; @@ -469,7 +469,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { #(#inject_listener_error_stmts);* } - fn inject_listener_closed(&mut self, id: #listener_id) { + fn inject_listener_closed(&mut self, id: #listener_id, reason: Result<(), &std::io::Error>) { #(#inject_listener_closed_stmts);* } diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index e7deaf75..fdddc6a0 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -146,7 +146,7 @@ pub trait NetworkBehaviour: Send + 'static { } /// A listener closed. - fn inject_listener_closed(&mut self, _id: ListenerId) { + fn inject_listener_closed(&mut self, _id: ListenerId, _reason: Result<(), &std::io::Error>) { } /// Polls for things that swarm should do. @@ -304,4 +304,3 @@ impl Default for DialPeerCondition { DialPeerCondition::Disconnected } } - diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 0d4d1255..097bc991 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -593,7 +593,10 @@ where TBehaviour: NetworkBehaviour, for addr in addresses.iter() { this.behaviour.inject_expired_listen_addr(addr); } - this.behaviour.inject_listener_closed(listener_id); + this.behaviour.inject_listener_closed(listener_id, match &reason { + Ok(()) => Ok(()), + Err(err) => Err(err), + }); return Poll::Ready(SwarmEvent::ListenerClosed { addresses, reason, From 1ddb4c4c0fdfe98b7bf117bee70e10f5b7441667 Mon Sep 17 00:00:00 2001 From: Roman Borschel Date: Wed, 1 Apr 2020 16:29:16 +0200 Subject: [PATCH 07/22] Update CHANGELOG. (#1534) * Update CHANGELOG. * Update CHANGELOG.md Co-Authored-By: Max Inden * More updates. * Update format. Co-authored-by: Max Inden --- CHANGELOG.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbf9976b..bac2d7b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,50 @@ # Version ??? -- Support for multiple connections per peer and configurable connection limits. +- `libp2p-core`: Finished "identity hashing" for peer IDs migration. + [PR 1460](https://github.com/libp2p/rust-libp2p/pull/1460) +- `libp2p-core`: Remove `poll_broadcast`. + [PR 1527](https://github.com/libp2p/rust-libp2p/pull/1527) +- `libp2p-core`, `libp2p-swarm`: Report addresses of closed listeners. + [PR 1485](https://github.com/libp2p/rust-libp2p/pull/1485) +- `libp2p-core`: Support for multiple connections per peer and configurable connection limits. See [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. +- `libp2p-swarm`: Pass the cause of closing a listener to `inject_listener_closed`. + [PR 1517](https://github.com/libp2p/rust-libp2p/pull/1517) +- `libp2p-swarm`: Support for multiple connections per peer and configurable connection limits. + See [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), + [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and + [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. +- `libp2p-swarm`: The `SwarmEvent` now returns more events. + [PR 1515](https://github.com/libp2p/rust-libp2p/pull/1515) +- `libp2p-swarm`: New `protocols_handler::multi` module. + [PR 1497](https://github.com/libp2p/rust-libp2p/pull/1497) +- `libp2p-swarm`: Allow configuration of outbound substreams. + [PR 1521](https://github.com/libp2p/rust-libp2p/pull/1521) + +- `libp2p-kad`: Providers returned from a lookup are now deduplicated. + [PR 1528](https://github.com/libp2p/rust-libp2p/pull/1528) +- `libp2p-kad`: Allow customising the maximum packet size. + [PR 1502](https://github.com/libp2p/rust-libp2p/pull/1502) +- `libp2p-kad`: Allow customising the (libp2p) connection keep-alive timeout. + [PR 1477](https://github.com/libp2p/rust-libp2p/pull/1477) +- `libp2p-kad`: Avoid storing records that are expired upon receipt (optimisation). + [PR 1496](https://github.com/libp2p/rust-libp2p/pull/1496) +- `libp2p-kad`: Fixed potential panic on computing record expiry. + [PR 1492](https://github.com/libp2p/rust-libp2p/pull/1492) + +- `libp2p-mplex`: Guard against use of underlying `Sink` upon + error or connection close. + [PR 1529](https://github.com/libp2p/rust-libp2p/pull/1529) + +- `multistream-select`: Upgrade to stable futures. + [PR 1484](https://github.com/libp2p/rust-libp2p/pull/1484) + +- `multihash`: Removed the crate in favour of the upstream crate. + [PR 1472](https://github.com/libp2p/rust-libp2p/pull/1472) + # Version 0.16.2 (2020-02-28) - Fixed yamux connections not properly closing and being stuck in the `CLOSE_WAIT` state. From 6af530d6b20531a3ba7815c68bb14da2319480da Mon Sep 17 00:00:00 2001 From: Roman Borschel Date: Wed, 1 Apr 2020 17:37:27 +0200 Subject: [PATCH 08/22] Re-export OneShotHandlerConfig. Make fields public. (#1538) Co-authored-by: Pierre Krieger --- swarm/src/lib.rs | 1 + swarm/src/protocols_handler.rs | 2 +- swarm/src/protocols_handler/one_shot.rs | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 097bc991..11bb1a05 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -77,6 +77,7 @@ pub use protocols_handler::{ ProtocolsHandlerSelect, ProtocolsHandlerUpgrErr, OneShotHandler, + OneShotHandlerConfig, SubstreamProtocol }; diff --git a/swarm/src/protocols_handler.rs b/swarm/src/protocols_handler.rs index 16098918..95722ed9 100644 --- a/swarm/src/protocols_handler.rs +++ b/swarm/src/protocols_handler.rs @@ -63,7 +63,7 @@ pub use dummy::DummyProtocolsHandler; pub use map_in::MapInEvent; pub use map_out::MapOutEvent; pub use node_handler::{NodeHandlerWrapper, NodeHandlerWrapperBuilder, NodeHandlerWrapperError}; -pub use one_shot::OneShotHandler; +pub use one_shot::{OneShotHandler, OneShotHandlerConfig}; pub use select::{IntoProtocolsHandlerSelect, ProtocolsHandlerSelect}; /// A handler for a set of protocols used on a connection with a remote. diff --git a/swarm/src/protocols_handler/one_shot.rs b/swarm/src/protocols_handler/one_shot.rs index 3bcac1e7..92d63088 100644 --- a/swarm/src/protocols_handler/one_shot.rs +++ b/swarm/src/protocols_handler/one_shot.rs @@ -245,9 +245,9 @@ where #[derive(Debug)] pub struct OneShotHandlerConfig { /// After the given duration has elapsed, an inactive connection will shutdown. - inactive_timeout: Duration, + pub inactive_timeout: Duration, /// Timeout duration for each newly opened outbound substream. - substream_timeout: Duration, + pub substream_timeout: Duration, } impl Default for OneShotHandlerConfig { From 3ae02a56a500a722ecb83aaf10bea3717869aa07 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 1 Apr 2020 19:53:34 +0200 Subject: [PATCH 09/22] Bump to 0.17.0 (#1537) --- Cargo.toml | 44 +++++++++++++++--------------- core/Cargo.toml | 10 +++---- misc/core-derive/Cargo.toml | 4 +-- misc/multistream-select/Cargo.toml | 2 +- misc/peer-id-generator/Cargo.toml | 2 +- muxers/mplex/Cargo.toml | 6 ++-- muxers/yamux/Cargo.toml | 4 +-- protocols/deflate/Cargo.toml | 6 ++-- protocols/floodsub/Cargo.toml | 6 ++-- protocols/gossipsub/Cargo.toml | 10 +++---- protocols/identify/Cargo.toml | 12 ++++---- protocols/kad/Cargo.toml | 10 +++---- protocols/mdns/Cargo.toml | 6 ++-- protocols/noise/Cargo.toml | 6 ++-- protocols/ping/Cargo.toml | 12 ++++---- protocols/plaintext/Cargo.toml | 4 +-- protocols/pnet/Cargo.toml | 2 +- protocols/secio/Cargo.toml | 8 +++--- swarm/Cargo.toml | 6 ++-- transports/dns/Cargo.toml | 4 +-- transports/tcp/Cargo.toml | 4 +-- transports/uds/Cargo.toml | 4 +-- transports/wasm-ext/Cargo.toml | 4 +-- transports/websocket/Cargo.toml | 6 ++-- 24 files changed, 91 insertions(+), 91 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c034745b..baf50452 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p" edition = "2018" description = "Peer-to-peer networking library" -version = "0.16.2" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -57,33 +57,33 @@ futures = "0.3.1" multiaddr = { package = "parity-multiaddr", version = "0.7.2", path = "misc/multiaddr" } multihash = "0.10" lazy_static = "1.2" -libp2p-mplex = { version = "0.16.0", path = "muxers/mplex", optional = true } -libp2p-identify = { version = "0.16.0", path = "protocols/identify", optional = true } -libp2p-kad = { version = "0.16.2", path = "protocols/kad", optional = true } -libp2p-floodsub = { version = "0.16.0", path = "protocols/floodsub", optional = true } -libp2p-gossipsub = { version = "0.16.0", path = "./protocols/gossipsub", optional = true } -libp2p-ping = { version = "0.16.0", path = "protocols/ping", optional = true } -libp2p-plaintext = { version = "0.16.0", path = "protocols/plaintext", optional = true } -libp2p-pnet = { version = "0.16.0", path = "protocols/pnet", optional = true } -libp2p-core = { version = "0.16.0", path = "core" } -libp2p-core-derive = { version = "0.16.0", path = "misc/core-derive" } -libp2p-secio = { version = "0.16.1", path = "protocols/secio", default-features = false, optional = true } -libp2p-swarm = { version = "0.16.1", path = "swarm" } -libp2p-uds = { version = "0.16.0", path = "transports/uds", optional = true } -libp2p-wasm-ext = { version = "0.16.2", path = "transports/wasm-ext", optional = true } -libp2p-yamux = { version = "0.16.2", path = "muxers/yamux", optional = true } -libp2p-noise = { version = "0.16.2", path = "protocols/noise", optional = true } +libp2p-mplex = { version = "0.17.0", path = "muxers/mplex", optional = true } +libp2p-identify = { version = "0.17.0", path = "protocols/identify", optional = true } +libp2p-kad = { version = "0.17.0", path = "protocols/kad", optional = true } +libp2p-floodsub = { version = "0.17.0", path = "protocols/floodsub", optional = true } +libp2p-gossipsub = { version = "0.17.0", path = "./protocols/gossipsub", optional = true } +libp2p-ping = { version = "0.17.0", path = "protocols/ping", optional = true } +libp2p-plaintext = { version = "0.17.0", path = "protocols/plaintext", optional = true } +libp2p-pnet = { version = "0.17.0", path = "protocols/pnet", optional = true } +libp2p-core = { version = "0.17.0", path = "core" } +libp2p-core-derive = { version = "0.17.0", path = "misc/core-derive" } +libp2p-secio = { version = "0.17.0", path = "protocols/secio", default-features = false, optional = true } +libp2p-swarm = { version = "0.17.0", path = "swarm" } +libp2p-uds = { version = "0.17.0", path = "transports/uds", optional = true } +libp2p-wasm-ext = { version = "0.17.0", path = "transports/wasm-ext", optional = true } +libp2p-yamux = { version = "0.17.0", path = "muxers/yamux", optional = true } +libp2p-noise = { version = "0.17.0", path = "protocols/noise", optional = true } parking_lot = "0.10.0" pin-project = "0.4.6" smallvec = "1.0" wasm-timer = "0.2.4" [target.'cfg(not(any(target_os = "emscripten", target_os = "unknown")))'.dependencies] -libp2p-deflate = { version = "0.16.0", path = "protocols/deflate", optional = true } -libp2p-dns = { version = "0.16.0", path = "transports/dns", optional = true } -libp2p-mdns = { version = "0.16.0", path = "protocols/mdns", optional = true } -libp2p-tcp = { version = "0.16.0", path = "transports/tcp", optional = true } -libp2p-websocket = { version = "0.16.0", path = "transports/websocket", optional = true } +libp2p-deflate = { version = "0.17.0", path = "protocols/deflate", optional = true } +libp2p-dns = { version = "0.17.0", path = "transports/dns", optional = true } +libp2p-mdns = { version = "0.17.0", path = "protocols/mdns", optional = true } +libp2p-tcp = { version = "0.17.0", path = "transports/tcp", optional = true } +libp2p-websocket = { version = "0.17.0", path = "transports/websocket", optional = true } [dev-dependencies] async-std = "1.0" diff --git a/core/Cargo.toml b/core/Cargo.toml index 254828c9..f97b3f47 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-core" edition = "2018" description = "Core traits and structs of libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -22,7 +22,7 @@ libsecp256k1 = { version = "0.3.1", optional = true } log = "0.4" multiaddr = { package = "parity-multiaddr", version = "0.7.3", path = "../misc/multiaddr" } multihash = "0.10" -multistream-select = { version = "0.7.0", path = "../misc/multistream-select" } +multistream-select = { version = "0.8.0", path = "../misc/multistream-select" } parking_lot = "0.10.0" pin-project = "0.4.6" prost = "0.6.1" @@ -40,9 +40,9 @@ ring = { version = "0.16.9", features = ["alloc", "std"], default-features = fal [dev-dependencies] async-std = "1.0" -libp2p-mplex = { version = "0.16.0", path = "../muxers/mplex" } -libp2p-secio = { version = "0.16.0", path = "../protocols/secio" } -libp2p-tcp = { version = "0.16.0", path = "../transports/tcp" } +libp2p-mplex = { version = "0.17.0", path = "../muxers/mplex" } +libp2p-secio = { version = "0.17.0", path = "../protocols/secio" } +libp2p-tcp = { version = "0.17.0", path = "../transports/tcp" } quickcheck = "0.9.0" wasm-timer = "0.2" diff --git a/misc/core-derive/Cargo.toml b/misc/core-derive/Cargo.toml index 85fa2318..b181d745 100644 --- a/misc/core-derive/Cargo.toml +++ b/misc/core-derive/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-core-derive" edition = "2018" description = "Procedural macros of libp2p-core" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -17,4 +17,4 @@ syn = { version = "1.0.8", default-features = false, features = ["clone-impls", quote = "1.0" [dev-dependencies] -libp2p = { version = "0.16.0", path = "../.." } +libp2p = { version = "0.17.0", path = "../.." } diff --git a/misc/multistream-select/Cargo.toml b/misc/multistream-select/Cargo.toml index 33857b11..fa4f03e9 100644 --- a/misc/multistream-select/Cargo.toml +++ b/misc/multistream-select/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "multistream-select" description = "Multistream-select negotiation protocol for libp2p" -version = "0.7.0" +version = "0.8.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/misc/peer-id-generator/Cargo.toml b/misc/peer-id-generator/Cargo.toml index f5159d49..aa6d19b6 100644 --- a/misc/peer-id-generator/Cargo.toml +++ b/misc/peer-id-generator/Cargo.toml @@ -11,5 +11,5 @@ categories = ["network-programming", "asynchronous"] publish = false [dependencies] -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } num_cpus = "1.8" diff --git a/muxers/mplex/Cargo.toml b/muxers/mplex/Cargo.toml index f789b9fe..b661e38f 100644 --- a/muxers/mplex/Cargo.toml +++ b/muxers/mplex/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-mplex" edition = "2018" description = "Mplex multiplexing protocol for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -14,11 +14,11 @@ bytes = "0.5" fnv = "1.0" futures = "0.3.1" futures_codec = "0.3.4" -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } log = "0.4" parking_lot = "0.10" unsigned-varint = { version = "0.3", features = ["futures-codec"] } [dev-dependencies] async-std = "1.0" -libp2p-tcp = { version = "0.16.0", path = "../../transports/tcp" } +libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } diff --git a/muxers/yamux/Cargo.toml b/muxers/yamux/Cargo.toml index 6c59f932..a79d49f7 100644 --- a/muxers/yamux/Cargo.toml +++ b/muxers/yamux/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-yamux" edition = "2018" description = "Yamux multiplexing protocol for libp2p" -version = "0.16.2" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,7 +11,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } parking_lot = "0.10" thiserror = "1.0" yamux = "0.4.5" diff --git a/protocols/deflate/Cargo.toml b/protocols/deflate/Cargo.toml index bdf502b6..c4aa2262 100644 --- a/protocols/deflate/Cargo.toml +++ b/protocols/deflate/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-deflate" edition = "2018" description = "Deflate encryption protocol for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,11 +11,11 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } flate2 = "1.0" [dev-dependencies] async-std = "1.0" -libp2p-tcp = { version = "0.16.0", path = "../../transports/tcp" } +libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } rand = "0.7" quickcheck = "0.9" diff --git a/protocols/floodsub/Cargo.toml b/protocols/floodsub/Cargo.toml index 8b58fa2c..f1ea3a0b 100644 --- a/protocols/floodsub/Cargo.toml +++ b/protocols/floodsub/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-floodsub" edition = "2018" description = "Floodsub protocol for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -13,8 +13,8 @@ categories = ["network-programming", "asynchronous"] cuckoofilter = "0.3.2" fnv = "1.0" futures = "0.3.1" -libp2p-core = { version = "0.16.0", path = "../../core" } -libp2p-swarm = { version = "0.16.0", path = "../../swarm" } +libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-swarm = { version = "0.17.0", path = "../../swarm" } prost = "0.6.1" rand = "0.7" smallvec = "1.0" diff --git a/protocols/gossipsub/Cargo.toml b/protocols/gossipsub/Cargo.toml index 5ff571fc..8826b9b1 100644 --- a/protocols/gossipsub/Cargo.toml +++ b/protocols/gossipsub/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-gossipsub" edition = "2018" description = "Gossipsub protocol for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Age Manning "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -10,8 +10,8 @@ keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] [dependencies] -libp2p-swarm = { version = "0.16.0", path = "../../swarm" } -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-swarm = { version = "0.17.0", path = "../../swarm" } +libp2p-core = { version = "0.17.0", path = "../../core" } bytes = "0.5.4" byteorder = "1.3.2" fnv = "1.0.6" @@ -30,8 +30,8 @@ prost = "0.6.1" [dev-dependencies] async-std = "1.4.0" env_logger = "0.7.1" -libp2p-plaintext = { version = "0.16.0", path = "../plaintext" } -libp2p-yamux = { version = "0.16.0", path = "../../muxers/yamux" } +libp2p-plaintext = { version = "0.17.0", path = "../plaintext" } +libp2p-yamux = { version = "0.17.0", path = "../../muxers/yamux" } quickcheck = "0.9.2" [build-dependencies] diff --git a/protocols/identify/Cargo.toml b/protocols/identify/Cargo.toml index f60a2610..07aa7500 100644 --- a/protocols/identify/Cargo.toml +++ b/protocols/identify/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-identify" edition = "2018" description = "Nodes identifcation protocol for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,8 +11,8 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.16.0", path = "../../core" } -libp2p-swarm = { version = "0.16.0", path = "../../swarm" } +libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-swarm = { version = "0.17.0", path = "../../swarm" } log = "0.4.1" prost = "0.6.1" smallvec = "1.0" @@ -20,9 +20,9 @@ wasm-timer = "0.2" [dev-dependencies] async-std = "1.0" -libp2p-mplex = { version = "0.16.0", path = "../../muxers/mplex" } -libp2p-secio = { version = "0.16.0", path = "../../protocols/secio" } -libp2p-tcp = { version = "0.16.0", path = "../../transports/tcp" } +libp2p-mplex = { version = "0.17.0", path = "../../muxers/mplex" } +libp2p-secio = { version = "0.17.0", path = "../../protocols/secio" } +libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } [build-dependencies] prost-build = "0.6" diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index 0e049e1c..ac241b52 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-kad" edition = "2018" description = "Kademlia protocol for libp2p" -version = "0.16.2" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -17,8 +17,8 @@ fnv = "1.0" futures_codec = "0.3.4" futures = "0.3.1" log = "0.4" -libp2p-core = { version = "0.16.0", path = "../../core" } -libp2p-swarm = { version = "0.16.0", path = "../../swarm" } +libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-swarm = { version = "0.17.0", path = "../../swarm" } multihash = "0.10" prost = "0.6.1" rand = "0.7.2" @@ -30,8 +30,8 @@ unsigned-varint = { version = "0.3", features = ["futures-codec"] } void = "1.0" [dev-dependencies] -libp2p-secio = { version = "0.16.0", path = "../secio" } -libp2p-yamux = { version = "0.16.0", path = "../../muxers/yamux" } +libp2p-secio = { version = "0.17.0", path = "../secio" } +libp2p-yamux = { version = "0.17.0", path = "../../muxers/yamux" } quickcheck = "0.9.0" [build-dependencies] diff --git a/protocols/mdns/Cargo.toml b/protocols/mdns/Cargo.toml index 4bbc3660..5d21d3fa 100644 --- a/protocols/mdns/Cargo.toml +++ b/protocols/mdns/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "libp2p-mdns" edition = "2018" -version = "0.16.0" +version = "0.17.0" description = "Implementation of the libp2p mDNS discovery method" authors = ["Parity Technologies "] license = "MIT" @@ -16,8 +16,8 @@ dns-parser = "0.8" either = "1.5.3" futures = "0.3.1" lazy_static = "1.2" -libp2p-core = { version = "0.16.0", path = "../../core" } -libp2p-swarm = { version = "0.16.0", path = "../../swarm" } +libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-swarm = { version = "0.17.0", path = "../../swarm" } log = "0.4" net2 = "0.2" rand = "0.7" diff --git a/protocols/noise/Cargo.toml b/protocols/noise/Cargo.toml index 19ac80e4..802d5885 100644 --- a/protocols/noise/Cargo.toml +++ b/protocols/noise/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "libp2p-noise" description = "Cryptographic handshake protocol using the noise framework." -version = "0.16.2" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,7 +11,7 @@ edition = "2018" curve25519-dalek = "2.0.0" futures = "0.3.1" lazy_static = "1.2" -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } log = "0.4" prost = "0.6.1" rand = "0.7.2" @@ -28,7 +28,7 @@ snow = { version = "0.6.1", features = ["default-resolver"], default-features = [dev-dependencies] env_logger = "0.7.1" -libp2p-tcp = { version = "0.16.0", path = "../../transports/tcp" } +libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } quickcheck = "0.9.0" sodiumoxide = "^0.2.5" diff --git a/protocols/ping/Cargo.toml b/protocols/ping/Cargo.toml index dc6c292c..942ae48a 100644 --- a/protocols/ping/Cargo.toml +++ b/protocols/ping/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-ping" edition = "2018" description = "Ping protocol for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,8 +11,8 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.16.0", path = "../../core" } -libp2p-swarm = { version = "0.16.0", path = "../../swarm" } +libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-swarm = { version = "0.17.0", path = "../../swarm" } log = "0.4.1" rand = "0.7.2" void = "1.0" @@ -20,7 +20,7 @@ wasm-timer = "0.2" [dev-dependencies] async-std = "1.0" -libp2p-tcp = { version = "0.16.0", path = "../../transports/tcp" } -libp2p-secio = { version = "0.16.0", path = "../../protocols/secio" } -libp2p-yamux = { version = "0.16.0", path = "../../muxers/yamux" } +libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } +libp2p-secio = { version = "0.17.0", path = "../../protocols/secio" } +libp2p-yamux = { version = "0.17.0", path = "../../muxers/yamux" } quickcheck = "0.9.0" diff --git a/protocols/plaintext/Cargo.toml b/protocols/plaintext/Cargo.toml index e1935a29..b8df5dc8 100644 --- a/protocols/plaintext/Cargo.toml +++ b/protocols/plaintext/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-plaintext" edition = "2018" description = "Plaintext encryption dummy protocol for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -13,7 +13,7 @@ categories = ["network-programming", "asynchronous"] bytes = "0.5" futures = "0.3.1" futures_codec = "0.3.4" -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } log = "0.4.8" prost = "0.6.1" rw-stream-sink = "0.2.0" diff --git a/protocols/pnet/Cargo.toml b/protocols/pnet/Cargo.toml index 0b5e69ad..ca55ac1f 100644 --- a/protocols/pnet/Cargo.toml +++ b/protocols/pnet/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-pnet" edition = "2018" description = "Private swarm support for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/secio/Cargo.toml b/protocols/secio/Cargo.toml index 7874f807..66448676 100644 --- a/protocols/secio/Cargo.toml +++ b/protocols/secio/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-secio" edition = "2018" description = "Secio encryption protocol for libp2p" -version = "0.16.1" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -16,7 +16,7 @@ ctr = "0.3" futures = "0.3.1" hmac = "0.7.0" lazy_static = "1.2.0" -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } log = "0.4.6" prost = "0.6.1" pin-project = "0.4.6" @@ -48,8 +48,8 @@ aes-all = ["aesni"] [dev-dependencies] async-std = "1.0" criterion = "0.3" -libp2p-mplex = { version = "0.16.0", path = "../../muxers/mplex" } -libp2p-tcp = { version = "0.16.0", path = "../../transports/tcp" } +libp2p-mplex = { version = "0.17.0", path = "../../muxers/mplex" } +libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } [[bench]] name = "bench" diff --git a/swarm/Cargo.toml b/swarm/Cargo.toml index 29bccee8..cb37d555 100644 --- a/swarm/Cargo.toml +++ b/swarm/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-swarm" edition = "2018" description = "The libp2p swarm" -version = "0.16.1" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,7 +11,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.16.0", path = "../core" } +libp2p-core = { version = "0.17.0", path = "../core" } log = "0.4" rand = "0.7" smallvec = "1.0" @@ -19,6 +19,6 @@ wasm-timer = "0.2" void = "1" [dev-dependencies] -libp2p-mplex = { version = "0.16.0", path = "../muxers/mplex" } +libp2p-mplex = { version = "0.17.0", path = "../muxers/mplex" } quickcheck = "0.9.0" rand = "0.7.2" diff --git a/transports/dns/Cargo.toml b/transports/dns/Cargo.toml index b93f2c09..d476eac5 100644 --- a/transports/dns/Cargo.toml +++ b/transports/dns/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-dns" edition = "2018" description = "DNS transport implementation for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -10,6 +10,6 @@ keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] [dependencies] -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } log = "0.4.1" futures = "0.3.1" diff --git a/transports/tcp/Cargo.toml b/transports/tcp/Cargo.toml index b04330da..69d9063e 100644 --- a/transports/tcp/Cargo.toml +++ b/transports/tcp/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-tcp" edition = "2018" description = "TCP/IP transport protocol for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -15,7 +15,7 @@ futures = "0.3.1" futures-timer = "3.0" get_if_addrs = "0.5.3" ipnet = "2.0.0" -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } log = "0.4.1" tokio = { version = "0.2", default-features = false, features = ["tcp"], optional = true } diff --git a/transports/uds/Cargo.toml b/transports/uds/Cargo.toml index 0b1b98d0..5068577c 100644 --- a/transports/uds/Cargo.toml +++ b/transports/uds/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-uds" edition = "2018" description = "Unix domain sockets transport for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,7 +11,7 @@ categories = ["network-programming", "asynchronous"] [target.'cfg(all(unix, not(any(target_os = "emscripten", target_os = "unknown"))))'.dependencies] async-std = { version = "1.0", optional = true } -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } log = "0.4.1" futures = "0.3.1" tokio = { version = "0.2", default-features = false, features = ["uds"], optional = true } diff --git a/transports/wasm-ext/Cargo.toml b/transports/wasm-ext/Cargo.toml index 024d0b0d..299df214 100644 --- a/transports/wasm-ext/Cargo.toml +++ b/transports/wasm-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-wasm-ext" -version = "0.16.2" +version = "0.17.0" authors = ["Pierre Krieger "] edition = "2018" description = "Allows passing in an external transport in a WASM environment" @@ -12,7 +12,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" js-sys = "0.3.19" -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } parity-send-wrapper = "0.1.0" wasm-bindgen = "0.2.42" wasm-bindgen-futures = "0.4.4" diff --git a/transports/websocket/Cargo.toml b/transports/websocket/Cargo.toml index 6a0dfe2b..ab2a0f38 100644 --- a/transports/websocket/Cargo.toml +++ b/transports/websocket/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-websocket" edition = "2018" description = "WebSocket transport for libp2p" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -14,7 +14,7 @@ async-tls = "0.6" bytes = "0.5" either = "1.5.3" futures = "0.3.1" -libp2p-core = { version = "0.16.0", path = "../../core" } +libp2p-core = { version = "0.17.0", path = "../../core" } log = "0.4.8" quicksink = "0.1" rustls = "0.16" @@ -25,4 +25,4 @@ webpki = "0.21" webpki-roots = "0.18" [dev-dependencies] -libp2p-tcp = { version = "0.16.0", path = "../tcp" } +libp2p-tcp = { version = "0.17.0", path = "../tcp" } From a203db884ce7c4ce63d2ca6dd3f238defe9c72c4 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 2 Apr 2020 10:17:36 +0200 Subject: [PATCH 10/22] Bump parity-multiaddr (#1540) --- Cargo.toml | 4 ++-- core/Cargo.toml | 4 ++-- misc/multiaddr/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index baf50452..5ebbe427 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ secp256k1 = ["libp2p-core/secp256k1", "libp2p-secio/secp256k1"] [dependencies] bytes = "0.5" futures = "0.3.1" -multiaddr = { package = "parity-multiaddr", version = "0.7.2", path = "misc/multiaddr" } +multiaddr = { package = "parity-multiaddr", version = "0.8.0", path = "misc/multiaddr" } multihash = "0.10" lazy_static = "1.2" libp2p-mplex = { version = "0.17.0", path = "muxers/mplex", optional = true } @@ -65,7 +65,7 @@ libp2p-gossipsub = { version = "0.17.0", path = "./protocols/gossipsub", optiona libp2p-ping = { version = "0.17.0", path = "protocols/ping", optional = true } libp2p-plaintext = { version = "0.17.0", path = "protocols/plaintext", optional = true } libp2p-pnet = { version = "0.17.0", path = "protocols/pnet", optional = true } -libp2p-core = { version = "0.17.0", path = "core" } +libp2p-core = { version = "0.17.1", path = "core" } libp2p-core-derive = { version = "0.17.0", path = "misc/core-derive" } libp2p-secio = { version = "0.17.0", path = "protocols/secio", default-features = false, optional = true } libp2p-swarm = { version = "0.17.0", path = "swarm" } diff --git a/core/Cargo.toml b/core/Cargo.toml index f97b3f47..47f60c04 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-core" edition = "2018" description = "Core traits and structs of libp2p" -version = "0.17.0" +version = "0.17.1" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -20,7 +20,7 @@ futures-timer = "3" lazy_static = "1.2" libsecp256k1 = { version = "0.3.1", optional = true } log = "0.4" -multiaddr = { package = "parity-multiaddr", version = "0.7.3", path = "../misc/multiaddr" } +multiaddr = { package = "parity-multiaddr", version = "0.8.0", path = "../misc/multiaddr" } multihash = "0.10" multistream-select = { version = "0.8.0", path = "../misc/multistream-select" } parking_lot = "0.10.0" diff --git a/misc/multiaddr/Cargo.toml b/misc/multiaddr/Cargo.toml index c30b4504..239407ae 100644 --- a/misc/multiaddr/Cargo.toml +++ b/misc/multiaddr/Cargo.toml @@ -6,7 +6,7 @@ description = "Implementation of the multiaddr format" homepage = "https://github.com/libp2p/rust-libp2p" keywords = ["multiaddr", "ipfs"] license = "MIT" -version = "0.7.3" +version = "0.8.0" [dependencies] arrayref = "0.3" From 3eec3cc85c8c8ac50049e9097616d27354a5b5eb Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 2 Apr 2020 15:51:41 +0200 Subject: [PATCH 11/22] Update CHANGELOG.md with 0.17.0 (#1541) --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bac2d7b2..05e66be6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Version ??? + +# Version 0.17.0 (2020-04-02) + - `libp2p-core`: Finished "identity hashing" for peer IDs migration. [PR 1460](https://github.com/libp2p/rust-libp2p/pull/1460) - `libp2p-core`: Remove `poll_broadcast`. From 84110878b8e5eddf7c715e858a28a268c35af654 Mon Sep 17 00:00:00 2001 From: Demi Obenour Date: Fri, 3 Apr 2020 15:48:14 +0000 Subject: [PATCH 12/22] Bump async-tls (#1532) * Bump async-tls * Bump rustls version --- transports/websocket/Cargo.toml | 4 ++-- transports/websocket/src/framed.rs | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/transports/websocket/Cargo.toml b/transports/websocket/Cargo.toml index ab2a0f38..c1d4e03d 100644 --- a/transports/websocket/Cargo.toml +++ b/transports/websocket/Cargo.toml @@ -10,14 +10,14 @@ keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] [dependencies] -async-tls = "0.6" +async-tls = "0.7.0" bytes = "0.5" either = "1.5.3" futures = "0.3.1" libp2p-core = { version = "0.17.0", path = "../../core" } log = "0.4.8" quicksink = "0.1" -rustls = "0.16" +rustls = "0.17.0" rw-stream-sink = "0.2.0" soketto = { version = "0.3", features = ["deflate"] } url = "2.1" diff --git a/transports/websocket/src/framed.rs b/transports/websocket/src/framed.rs index 830b645d..191636a0 100644 --- a/transports/websocket/src/framed.rs +++ b/transports/websocket/src/framed.rs @@ -308,11 +308,6 @@ where let dns_name = dns_name.expect("for use_tls we have checked that dns_name is some"); trace!("starting TLS handshake with {}", address); let stream = self.tls_config.client.connect(&dns_name, stream) - .map_err(|e| { - // We should never enter here as we passed a `DNSNameRef` to `connect`. - debug!("invalid domain name: {:?}", dns_name); - Error::Tls(e.into()) - })? .map_err(|e| { debug!("TLS handshake with {} failed: {}", address, e); Error::Tls(tls::Error::from(e)) From c1191d5f8368e25bbf4b90365a26861947fdfeb9 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 8 Apr 2020 10:31:45 +0200 Subject: [PATCH 13/22] protocols/kad/behaviour: Return peers independent of record existence (#1544) A node receiving a `GetRecord` request first checks whether it has the given record. If it does have the record it does not return closer nodes. A node that knows the record for the given key is likely within a neighborhood of nodes that know the record as well. In addition the node likely knows its neighboorhood well. When querying for a key with a quorum of 1 the above behavior of only returning the record but not any close peers is fine. Once one queries with a higher quorum having a node respond with the record as well as close nodes is likely going to speed up the query, given that the returned peers probably know the record as well. --- protocols/kad/src/behaviour.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index fab9fbd6..d243aff3 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1278,13 +1278,7 @@ where None => None }; - // If no record is found, at least report known closer peers. - let closer_peers = - if record.is_none() { - self.find_closest(&kbucket::Key::new(key), &source) - } else { - Vec::new() - }; + let closer_peers = self.find_closest(&kbucket::Key::new(key), &source); self.queued_events.push_back(NetworkBehaviourAction::NotifyHandler { peer_id: source, From 80ea2f6fd7eb8484318506254935c7c5fb0237ba Mon Sep 17 00:00:00 2001 From: Joonas Koivunen Date: Thu, 9 Apr 2020 16:15:17 +0300 Subject: [PATCH 14/22] feat: allow sent messages seen as subscribed (#1520) * feat: allow sent messages seen as subscribed minor feature to allow mimicing the behaviour expected by ipfs api tests. * refactor: rename per review comments * refactor: rename Floodsub::options to config * chore: update changelog * Update CHANGELOG.md Co-Authored-By: Max Inden Co-authored-by: Max Inden Co-authored-by: Pierre Krieger --- CHANGELOG.md | 2 ++ protocols/floodsub/src/layer.rs | 23 ++++++++++++++++------- protocols/floodsub/src/lib.rs | 21 +++++++++++++++++++++ protocols/floodsub/src/protocol.rs | 14 +++++++------- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05e66be6..53459981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Version ??? +- `libp2p-floodsub`: Allow sent messages seen as subscribed. + [PR 1520](https://github.com/libp2p/rust-libp2p/pull/1520) # Version 0.17.0 (2020-04-02) diff --git a/protocols/floodsub/src/layer.rs b/protocols/floodsub/src/layer.rs index 1c837b2d..4bb6aa08 100644 --- a/protocols/floodsub/src/layer.rs +++ b/protocols/floodsub/src/layer.rs @@ -18,8 +18,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::protocol::{FloodsubConfig, FloodsubMessage, FloodsubRpc, FloodsubSubscription, FloodsubSubscriptionAction}; +use crate::protocol::{FloodsubProtocol, FloodsubMessage, FloodsubRpc, FloodsubSubscription, FloodsubSubscriptionAction}; use crate::topic::Topic; +use crate::FloodsubConfig; use cuckoofilter::CuckooFilter; use fnv::FnvHashSet; use libp2p_core::{Multiaddr, PeerId, connection::ConnectionId}; @@ -43,8 +44,7 @@ pub struct Floodsub { /// Events that need to be yielded to the outside when polling. events: VecDeque>, - /// Peer id of the local node. Used for the source of the messages that we publish. - local_peer_id: PeerId, + config: FloodsubConfig, /// List of peers to send messages to. target_peers: FnvHashSet, @@ -64,11 +64,16 @@ pub struct Floodsub { } impl Floodsub { - /// Creates a `Floodsub`. + /// Creates a `Floodsub` with default configuration. pub fn new(local_peer_id: PeerId) -> Self { + Self::from_config(FloodsubConfig::new(local_peer_id)) + } + + /// Creates a `Floodsub` with the given configuration. + pub fn from_config(config: FloodsubConfig) -> Self { Floodsub { events: VecDeque::new(), - local_peer_id, + config, target_peers: FnvHashSet::default(), connected_peers: HashMap::new(), subscribed_topics: SmallVec::new(), @@ -190,7 +195,7 @@ impl Floodsub { fn publish_many_inner(&mut self, topic: impl IntoIterator>, data: impl Into>, check_self_subscriptions: bool) { let message = FloodsubMessage { - source: self.local_peer_id.clone(), + source: self.config.local_peer_id.clone(), data: data.into(), // If the sequence numbers are predictable, then an attacker could flood the network // with packets with the predetermined sequence numbers and absorb our legitimate @@ -202,6 +207,10 @@ impl Floodsub { let self_subscribed = self.subscribed_topics.iter().any(|t| message.topics.iter().any(|u| t == u)); if self_subscribed { self.received.add(&message); + if self.config.subscribe_local_messages { + self.events.push_back( + NetworkBehaviourAction::GenerateEvent(FloodsubEvent::Message(message.clone()))); + } } // Don't publish the message if we have to check subscriptions // and we're not subscribed ourselves to any of the topics. @@ -228,7 +237,7 @@ impl Floodsub { } impl NetworkBehaviour for Floodsub { - type ProtocolsHandler = OneShotHandler; + type ProtocolsHandler = OneShotHandler; type OutEvent = FloodsubEvent; fn new_handler(&mut self) -> Self::ProtocolsHandler { diff --git a/protocols/floodsub/src/lib.rs b/protocols/floodsub/src/lib.rs index cc9e840a..8e7014be 100644 --- a/protocols/floodsub/src/lib.rs +++ b/protocols/floodsub/src/lib.rs @@ -21,6 +21,8 @@ //! Implements the floodsub protocol, see also the: //! [spec](https://github.com/libp2p/specs/tree/master/pubsub). +use libp2p_core::PeerId; + pub mod protocol; mod layer; @@ -33,3 +35,22 @@ mod rpc_proto { pub use self::layer::{Floodsub, FloodsubEvent}; pub use self::protocol::{FloodsubMessage, FloodsubRpc}; pub use self::topic::Topic; + +/// Configuration options for the Floodsub protocol. +pub struct FloodsubConfig { + /// Peer id of the local node. Used for the source of the messages that we publish. + pub local_peer_id: PeerId, + + /// `true` if messages published by local node should be propagated as messages received from + /// the network, `false` by default. + pub subscribe_local_messages: bool, +} + +impl FloodsubConfig { + pub fn new(local_peer_id: PeerId) -> Self { + Self { + local_peer_id, + subscribe_local_messages: false + } + } +} diff --git a/protocols/floodsub/src/protocol.rs b/protocols/floodsub/src/protocol.rs index 4df3975e..046c72d8 100644 --- a/protocols/floodsub/src/protocol.rs +++ b/protocols/floodsub/src/protocol.rs @@ -27,16 +27,16 @@ use futures::{Future, io::{AsyncRead, AsyncWrite}}; /// Implementation of `ConnectionUpgrade` for the floodsub protocol. #[derive(Debug, Clone, Default)] -pub struct FloodsubConfig {} +pub struct FloodsubProtocol {} -impl FloodsubConfig { - /// Builds a new `FloodsubConfig`. - pub fn new() -> FloodsubConfig { - FloodsubConfig {} +impl FloodsubProtocol { + /// Builds a new `FloodsubProtocol`. + pub fn new() -> FloodsubProtocol { + FloodsubProtocol {} } } -impl UpgradeInfo for FloodsubConfig { +impl UpgradeInfo for FloodsubProtocol { type Info = &'static [u8]; type InfoIter = iter::Once; @@ -45,7 +45,7 @@ impl UpgradeInfo for FloodsubConfig { } } -impl InboundUpgrade for FloodsubConfig +impl InboundUpgrade for FloodsubProtocol where TSocket: AsyncRead + AsyncWrite + Send + Unpin + 'static, { From 8adf474b38d04ba07c0e2c2ccc846813e430feae Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 9 Apr 2020 15:36:52 +0200 Subject: [PATCH 15/22] Fix parsed is null error (#1535) * Fix parsed is null error * Update CHANGELOG --- CHANGELOG.md | 3 +++ transports/wasm-ext/src/websockets.js | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53459981..4886e6f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ - `libp2p-floodsub`: Allow sent messages seen as subscribed. [PR 1520](https://github.com/libp2p/rust-libp2p/pull/1520) +- `libp2p-wasm-ext`: Fix "parsed is null" errors being thrown. + [PR 1535](https://github.com/libp2p/rust-libp2p/pull/1535) + # Version 0.17.0 (2020-04-02) - `libp2p-core`: Finished "identity hashing" for peer IDs migration. diff --git a/transports/wasm-ext/src/websockets.js b/transports/wasm-ext/src/websockets.js index ec868bff..7b96409c 100644 --- a/transports/wasm-ext/src/websockets.js +++ b/transports/wasm-ext/src/websockets.js @@ -33,12 +33,12 @@ export const websocket_transport = () => { // TODO: support dns addresses as well const multiaddr_to_ws = (addr) => { let parsed = addr.match(/^\/(ip4|ip6|dns4|dns6)\/(.*?)\/tcp\/(.*?)\/(ws|wss|x-parity-ws\/(.*)|x-parity-wss\/(.*))$/); - let proto = 'wss'; - if (parsed[4] == 'ws' || parsed[4] == 'x-parity-ws') { - proto = 'ws'; - } - let url = decodeURIComponent(parsed[5] || parsed[6] || ''); if (parsed != null) { + let proto = 'wss'; + if (parsed[4] == 'ws' || parsed[4] == 'x-parity-ws') { + proto = 'ws'; + } + let url = decodeURIComponent(parsed[5] || parsed[6] || ''); if (parsed[1] == 'ip6') { return proto + "://[" + parsed[2] + "]:" + parsed[3] + url; } else { From 803eb10dcdb7db23409e65cd735ccbe54caa2df5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 10 Apr 2020 00:01:26 +1000 Subject: [PATCH 16/22] Disambiguate calls to NetworkBehaviour::inject_event (#1543) * Disambiguate calls to NetworkBehaviour::inject_event There is a gnarly edge-case with the custom-derive where rustc cannot disambiguate the call if: - The NetworkBehaviourEventProcess trait is imported - We nest NetworkBehaviours that use the custom-derive * Update misc/core-derive/src/lib.rs Co-Authored-By: Pierre Krieger * Fix build and add CHANGELOG Co-authored-by: Pierre Krieger --- CHANGELOG.md | 3 +++ misc/core-derive/src/lib.rs | 4 ++-- misc/core-derive/tests/test.rs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4886e6f8..22b9bb1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Version ??? +- `libp2p-core-derive`: Disambiguate calls to `NetworkBehaviour::inject_event`. + [PR 1543](https://github.com/libp2p/rust-libp2p/pull/1543) + - `libp2p-floodsub`: Allow sent messages seen as subscribed. [PR 1520](https://github.com/libp2p/rust-libp2p/pull/1520) diff --git a/misc/core-derive/src/lib.rs b/misc/core-derive/src/lib.rs index 1153276f..d7d28f15 100644 --- a/misc/core-derive/src/lib.rs +++ b/misc/core-derive/src/lib.rs @@ -293,8 +293,8 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { } Some(match field.ident { - Some(ref i) => quote!{ #elem => self.#i.inject_event(peer_id, connection_id, ev) }, - None => quote!{ #elem => self.#field_n.inject_event(peer_id, connection_id, ev) }, + Some(ref i) => quote!{ #elem => #trait_to_impl::inject_event(&mut self.#i, peer_id, connection_id, ev) }, + None => quote!{ #elem => #trait_to_impl::inject_event(&mut self.#field_n, peer_id, connection_id, ev) }, }) }); diff --git a/misc/core-derive/tests/test.rs b/misc/core-derive/tests/test.rs index e0c5cfdf..b65fb16e 100644 --- a/misc/core-derive/tests/test.rs +++ b/misc/core-derive/tests/test.rs @@ -204,3 +204,35 @@ fn where_clause() { bar: T, } } + +#[test] +fn nested_derives_with_import() { + use libp2p::swarm::NetworkBehaviourEventProcess; + + #[allow(dead_code)] + #[derive(NetworkBehaviour)] + struct Foo { + ping: libp2p::ping::Ping, + } + + #[allow(dead_code)] + #[derive(NetworkBehaviour)] + struct Bar { + foo: Foo, + } + + impl NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::ping::PingEvent) { + } + } + + impl NetworkBehaviourEventProcess<()> for Bar { + fn inject_event(&mut self, _: ()) { + } + } + + #[allow(dead_code)] + fn bar() { + require_net_behaviour::(); + } +} From d2eebf26192a1acc9062af04e130e69d49d3797a Mon Sep 17 00:00:00 2001 From: Roman Borschel Date: Thu, 9 Apr 2020 16:22:49 +0200 Subject: [PATCH 17/22] Treat connection limit errors as pending connection errors. (#1546) * Treat connection limit errors as pending connection errors. * Remove handler from network event. Co-authored-by: Pierre Krieger --- core/src/connection/error.rs | 14 +++++++------- core/src/connection/pool.rs | 16 ++++++++-------- core/src/network.rs | 32 +++++++++++++++++++------------- core/src/network/event.rs | 4 ---- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/core/src/connection/error.rs b/core/src/connection/error.rs index 0d291c37..1836965e 100644 --- a/core/src/connection/error.rs +++ b/core/src/connection/error.rs @@ -29,10 +29,6 @@ pub enum ConnectionError { // TODO: Eventually this should also be a custom error? IO(io::Error), - /// The connection was dropped because the connection limit - /// for a peer has been reached. - ConnectionLimit(ConnectionLimit), - /// The connection handler produced an error. Handler(THandlerErr), } @@ -48,8 +44,6 @@ where write!(f, "Connection error: I/O error: {}", err), ConnectionError::Handler(err) => write!(f, "Connection error: Handler error: {}", err), - ConnectionError::ConnectionLimit(l) => - write!(f, "Connection error: Connection limit: {}.", l) } } } @@ -63,7 +57,6 @@ where match self { ConnectionError::IO(err) => Some(err), ConnectionError::Handler(err) => Some(err), - ConnectionError::ConnectionLimit(..) => None, } } } @@ -78,6 +71,10 @@ pub enum PendingConnectionError { /// match the one that was expected or is otherwise invalid. InvalidPeerId, + /// The connection was dropped because the connection limit + /// for a peer has been reached. + ConnectionLimit(ConnectionLimit), + /// An I/O error occurred on the connection. // TODO: Eventually this should also be a custom error? IO(io::Error), @@ -96,6 +93,8 @@ where write!(f, "Pending connection: Transport error: {}", err), PendingConnectionError::InvalidPeerId => write!(f, "Pending connection: Invalid peer ID."), + PendingConnectionError::ConnectionLimit(l) => + write!(f, "Connection error: Connection limit: {}.", l), } } } @@ -110,6 +109,7 @@ where PendingConnectionError::IO(err) => Some(err), PendingConnectionError::Transport(err) => Some(err), PendingConnectionError::InvalidPeerId => None, + PendingConnectionError::ConnectionLimit(..) => None, } } } diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 56e11236..f8c9e1a7 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -112,7 +112,7 @@ pub enum PoolEvent<'a, TInEvent, TOutEvent, THandler, TTransErr, THandlerErr, TC error: PendingConnectionError, /// The handler that was supposed to handle the connection, /// if the connection failed before the handler was consumed. - handler: THandler, + handler: Option, /// The (expected) peer of the failed connection. peer: Option, /// A reference to the pool that managed the connection. @@ -558,7 +558,7 @@ where id, endpoint, error, - handler, + handler: Some(handler), peer, pool: self }) @@ -588,13 +588,13 @@ where .map_or(0, |conns| conns.len()); if let Err(e) = self.limits.check_established(current) { let connected = entry.close(); - let num_established = u32::try_from(e.current).unwrap(); - return Poll::Ready(PoolEvent::ConnectionError { + return Poll::Ready(PoolEvent::PendingConnectionError { id, - connected, - error: ConnectionError::ConnectionLimit(e), - num_established, - pool: self, + endpoint: connected.endpoint, + error: PendingConnectionError::ConnectionLimit(e), + handler: None, + peer, + pool: self }) } // Peer ID checks must already have happened. See `add_pending`. diff --git a/core/src/network.rs b/core/src/network.rs index 10b6e063..ccb21e84 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -512,7 +512,7 @@ fn on_connection_failed<'a, TTrans, TInEvent, TOutEvent, THandler, TConnInfo, TP id: ConnectionId, endpoint: ConnectedPoint, error: PendingConnectionError, - handler: THandler, + handler: Option, ) -> (Option>, NetworkEvent<'a, TTrans, TInEvent, TOutEvent, THandler, TConnInfo, TPeerId>) where TTrans: Transport, @@ -533,22 +533,29 @@ where let num_remain = u32::try_from(attempt.next.len()).unwrap(); let failed_addr = attempt.current.clone(); - let opts = + let (opts, attempts_remaining) = if num_remain > 0 { - let next_attempt = attempt.next.remove(0); - let opts = DialingOpts { - peer: peer_id.clone(), - handler, - address: next_attempt, - remaining: attempt.next - }; - Some(opts) + if let Some(handler) = handler { + let next_attempt = attempt.next.remove(0); + let opts = DialingOpts { + peer: peer_id.clone(), + handler, + address: next_attempt, + remaining: attempt.next + }; + (Some(opts), num_remain) + } else { + // The error is "fatal" for the dialing attempt, since + // the handler was already consumed. All potential + // remaining connection attempts are thus void. + (None, 0) + } } else { - None + (None, 0) }; (opts, NetworkEvent::DialError { - attempts_remaining: num_remain, + attempts_remaining, peer_id, multiaddr: failed_addr, error, @@ -560,7 +567,6 @@ where (None, NetworkEvent::UnknownPeerDialError { multiaddr: address, error, - handler, }), ConnectedPoint::Listener { local_addr, send_back_addr } => (None, NetworkEvent::IncomingConnectionError { diff --git a/core/src/network/event.rs b/core/src/network/event.rs index afbedcf7..a63dc479 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -146,10 +146,6 @@ where /// The error that happened. error: PendingConnectionError, - - /// The handler that was passed to `dial()`, if the - /// connection failed before the handler was consumed. - handler: THandler, }, /// An established connection produced an event. From aa71158e5cc33e669f1cac3396bad780cfdece59 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 9 Apr 2020 16:54:01 +0200 Subject: [PATCH 18/22] Publish 0.18.0 (#1547) --- CHANGELOG.md | 9 +++++++ Cargo.toml | 44 +++++++++++++++---------------- core/Cargo.toml | 8 +++--- misc/core-derive/Cargo.toml | 4 +-- misc/peer-id-generator/Cargo.toml | 2 +- muxers/mplex/Cargo.toml | 6 ++--- muxers/yamux/Cargo.toml | 4 +-- protocols/deflate/Cargo.toml | 6 ++--- protocols/floodsub/Cargo.toml | 6 ++--- protocols/gossipsub/Cargo.toml | 10 +++---- protocols/identify/Cargo.toml | 12 ++++----- protocols/kad/Cargo.toml | 10 +++---- protocols/mdns/Cargo.toml | 6 ++--- protocols/noise/Cargo.toml | 6 ++--- protocols/ping/Cargo.toml | 12 ++++----- protocols/plaintext/Cargo.toml | 4 +-- protocols/pnet/Cargo.toml | 2 +- protocols/secio/Cargo.toml | 8 +++--- swarm/Cargo.toml | 6 ++--- transports/dns/Cargo.toml | 4 +-- transports/tcp/Cargo.toml | 4 +-- transports/uds/Cargo.toml | 4 +-- transports/wasm-ext/Cargo.toml | 4 +-- transports/websocket/Cargo.toml | 6 ++--- 24 files changed, 98 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b9bb1f..7fc8577b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,20 @@ # Version ??? + +# Version 0.18.0 (2020-04-09) + +- `libp2p-core`: Treat connection limit errors as pending connection errors. + [PR 1546](https://github.com/libp2p/rust-libp2p/pull/1546) + - `libp2p-core-derive`: Disambiguate calls to `NetworkBehaviour::inject_event`. [PR 1543](https://github.com/libp2p/rust-libp2p/pull/1543) - `libp2p-floodsub`: Allow sent messages seen as subscribed. [PR 1520](https://github.com/libp2p/rust-libp2p/pull/1520) +- `libp2p-kad`: Return peers independent of record existence. + [PR 1544](https://github.com/libp2p/rust-libp2p/pull/1544) + - `libp2p-wasm-ext`: Fix "parsed is null" errors being thrown. [PR 1535](https://github.com/libp2p/rust-libp2p/pull/1535) diff --git a/Cargo.toml b/Cargo.toml index 5ebbe427..9f34ae20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p" edition = "2018" description = "Peer-to-peer networking library" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -57,33 +57,33 @@ futures = "0.3.1" multiaddr = { package = "parity-multiaddr", version = "0.8.0", path = "misc/multiaddr" } multihash = "0.10" lazy_static = "1.2" -libp2p-mplex = { version = "0.17.0", path = "muxers/mplex", optional = true } -libp2p-identify = { version = "0.17.0", path = "protocols/identify", optional = true } -libp2p-kad = { version = "0.17.0", path = "protocols/kad", optional = true } -libp2p-floodsub = { version = "0.17.0", path = "protocols/floodsub", optional = true } -libp2p-gossipsub = { version = "0.17.0", path = "./protocols/gossipsub", optional = true } -libp2p-ping = { version = "0.17.0", path = "protocols/ping", optional = true } -libp2p-plaintext = { version = "0.17.0", path = "protocols/plaintext", optional = true } -libp2p-pnet = { version = "0.17.0", path = "protocols/pnet", optional = true } -libp2p-core = { version = "0.17.1", path = "core" } -libp2p-core-derive = { version = "0.17.0", path = "misc/core-derive" } -libp2p-secio = { version = "0.17.0", path = "protocols/secio", default-features = false, optional = true } -libp2p-swarm = { version = "0.17.0", path = "swarm" } -libp2p-uds = { version = "0.17.0", path = "transports/uds", optional = true } -libp2p-wasm-ext = { version = "0.17.0", path = "transports/wasm-ext", optional = true } -libp2p-yamux = { version = "0.17.0", path = "muxers/yamux", optional = true } -libp2p-noise = { version = "0.17.0", path = "protocols/noise", optional = true } +libp2p-mplex = { version = "0.18.0", path = "muxers/mplex", optional = true } +libp2p-identify = { version = "0.18.0", path = "protocols/identify", optional = true } +libp2p-kad = { version = "0.18.0", path = "protocols/kad", optional = true } +libp2p-floodsub = { version = "0.18.0", path = "protocols/floodsub", optional = true } +libp2p-gossipsub = { version = "0.18.0", path = "./protocols/gossipsub", optional = true } +libp2p-ping = { version = "0.18.0", path = "protocols/ping", optional = true } +libp2p-plaintext = { version = "0.18.0", path = "protocols/plaintext", optional = true } +libp2p-pnet = { version = "0.18.0", path = "protocols/pnet", optional = true } +libp2p-core = { version = "0.18.0", path = "core" } +libp2p-core-derive = { version = "0.18.0", path = "misc/core-derive" } +libp2p-secio = { version = "0.18.0", path = "protocols/secio", default-features = false, optional = true } +libp2p-swarm = { version = "0.18.0", path = "swarm" } +libp2p-uds = { version = "0.18.0", path = "transports/uds", optional = true } +libp2p-wasm-ext = { version = "0.18.0", path = "transports/wasm-ext", optional = true } +libp2p-yamux = { version = "0.18.0", path = "muxers/yamux", optional = true } +libp2p-noise = { version = "0.18.0", path = "protocols/noise", optional = true } parking_lot = "0.10.0" pin-project = "0.4.6" smallvec = "1.0" wasm-timer = "0.2.4" [target.'cfg(not(any(target_os = "emscripten", target_os = "unknown")))'.dependencies] -libp2p-deflate = { version = "0.17.0", path = "protocols/deflate", optional = true } -libp2p-dns = { version = "0.17.0", path = "transports/dns", optional = true } -libp2p-mdns = { version = "0.17.0", path = "protocols/mdns", optional = true } -libp2p-tcp = { version = "0.17.0", path = "transports/tcp", optional = true } -libp2p-websocket = { version = "0.17.0", path = "transports/websocket", optional = true } +libp2p-deflate = { version = "0.18.0", path = "protocols/deflate", optional = true } +libp2p-dns = { version = "0.18.0", path = "transports/dns", optional = true } +libp2p-mdns = { version = "0.18.0", path = "protocols/mdns", optional = true } +libp2p-tcp = { version = "0.18.0", path = "transports/tcp", optional = true } +libp2p-websocket = { version = "0.18.0", path = "transports/websocket", optional = true } [dev-dependencies] async-std = "1.0" diff --git a/core/Cargo.toml b/core/Cargo.toml index 47f60c04..dd7f0461 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-core" edition = "2018" description = "Core traits and structs of libp2p" -version = "0.17.1" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -40,9 +40,9 @@ ring = { version = "0.16.9", features = ["alloc", "std"], default-features = fal [dev-dependencies] async-std = "1.0" -libp2p-mplex = { version = "0.17.0", path = "../muxers/mplex" } -libp2p-secio = { version = "0.17.0", path = "../protocols/secio" } -libp2p-tcp = { version = "0.17.0", path = "../transports/tcp" } +libp2p-mplex = { version = "0.18.0", path = "../muxers/mplex" } +libp2p-secio = { version = "0.18.0", path = "../protocols/secio" } +libp2p-tcp = { version = "0.18.0", path = "../transports/tcp" } quickcheck = "0.9.0" wasm-timer = "0.2" diff --git a/misc/core-derive/Cargo.toml b/misc/core-derive/Cargo.toml index b181d745..166156cd 100644 --- a/misc/core-derive/Cargo.toml +++ b/misc/core-derive/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-core-derive" edition = "2018" description = "Procedural macros of libp2p-core" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -17,4 +17,4 @@ syn = { version = "1.0.8", default-features = false, features = ["clone-impls", quote = "1.0" [dev-dependencies] -libp2p = { version = "0.17.0", path = "../.." } +libp2p = { version = "0.18.0", path = "../.." } diff --git a/misc/peer-id-generator/Cargo.toml b/misc/peer-id-generator/Cargo.toml index aa6d19b6..f3efec74 100644 --- a/misc/peer-id-generator/Cargo.toml +++ b/misc/peer-id-generator/Cargo.toml @@ -11,5 +11,5 @@ categories = ["network-programming", "asynchronous"] publish = false [dependencies] -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } num_cpus = "1.8" diff --git a/muxers/mplex/Cargo.toml b/muxers/mplex/Cargo.toml index b661e38f..4be96b7e 100644 --- a/muxers/mplex/Cargo.toml +++ b/muxers/mplex/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-mplex" edition = "2018" description = "Mplex multiplexing protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -14,11 +14,11 @@ bytes = "0.5" fnv = "1.0" futures = "0.3.1" futures_codec = "0.3.4" -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } log = "0.4" parking_lot = "0.10" unsigned-varint = { version = "0.3", features = ["futures-codec"] } [dev-dependencies] async-std = "1.0" -libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } +libp2p-tcp = { version = "0.18.0", path = "../../transports/tcp" } diff --git a/muxers/yamux/Cargo.toml b/muxers/yamux/Cargo.toml index a79d49f7..4255bea4 100644 --- a/muxers/yamux/Cargo.toml +++ b/muxers/yamux/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-yamux" edition = "2018" description = "Yamux multiplexing protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,7 +11,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } parking_lot = "0.10" thiserror = "1.0" yamux = "0.4.5" diff --git a/protocols/deflate/Cargo.toml b/protocols/deflate/Cargo.toml index c4aa2262..0d2552f5 100644 --- a/protocols/deflate/Cargo.toml +++ b/protocols/deflate/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-deflate" edition = "2018" description = "Deflate encryption protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,11 +11,11 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } flate2 = "1.0" [dev-dependencies] async-std = "1.0" -libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } +libp2p-tcp = { version = "0.18.0", path = "../../transports/tcp" } rand = "0.7" quickcheck = "0.9" diff --git a/protocols/floodsub/Cargo.toml b/protocols/floodsub/Cargo.toml index f1ea3a0b..4c8fa279 100644 --- a/protocols/floodsub/Cargo.toml +++ b/protocols/floodsub/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-floodsub" edition = "2018" description = "Floodsub protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -13,8 +13,8 @@ categories = ["network-programming", "asynchronous"] cuckoofilter = "0.3.2" fnv = "1.0" futures = "0.3.1" -libp2p-core = { version = "0.17.0", path = "../../core" } -libp2p-swarm = { version = "0.17.0", path = "../../swarm" } +libp2p-core = { version = "0.18.0", path = "../../core" } +libp2p-swarm = { version = "0.18.0", path = "../../swarm" } prost = "0.6.1" rand = "0.7" smallvec = "1.0" diff --git a/protocols/gossipsub/Cargo.toml b/protocols/gossipsub/Cargo.toml index 8826b9b1..8f1b297d 100644 --- a/protocols/gossipsub/Cargo.toml +++ b/protocols/gossipsub/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-gossipsub" edition = "2018" description = "Gossipsub protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Age Manning "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -10,8 +10,8 @@ keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] [dependencies] -libp2p-swarm = { version = "0.17.0", path = "../../swarm" } -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-swarm = { version = "0.18.0", path = "../../swarm" } +libp2p-core = { version = "0.18.0", path = "../../core" } bytes = "0.5.4" byteorder = "1.3.2" fnv = "1.0.6" @@ -30,8 +30,8 @@ prost = "0.6.1" [dev-dependencies] async-std = "1.4.0" env_logger = "0.7.1" -libp2p-plaintext = { version = "0.17.0", path = "../plaintext" } -libp2p-yamux = { version = "0.17.0", path = "../../muxers/yamux" } +libp2p-plaintext = { version = "0.18.0", path = "../plaintext" } +libp2p-yamux = { version = "0.18.0", path = "../../muxers/yamux" } quickcheck = "0.9.2" [build-dependencies] diff --git a/protocols/identify/Cargo.toml b/protocols/identify/Cargo.toml index 07aa7500..31fb5e9a 100644 --- a/protocols/identify/Cargo.toml +++ b/protocols/identify/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-identify" edition = "2018" description = "Nodes identifcation protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,8 +11,8 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.17.0", path = "../../core" } -libp2p-swarm = { version = "0.17.0", path = "../../swarm" } +libp2p-core = { version = "0.18.0", path = "../../core" } +libp2p-swarm = { version = "0.18.0", path = "../../swarm" } log = "0.4.1" prost = "0.6.1" smallvec = "1.0" @@ -20,9 +20,9 @@ wasm-timer = "0.2" [dev-dependencies] async-std = "1.0" -libp2p-mplex = { version = "0.17.0", path = "../../muxers/mplex" } -libp2p-secio = { version = "0.17.0", path = "../../protocols/secio" } -libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } +libp2p-mplex = { version = "0.18.0", path = "../../muxers/mplex" } +libp2p-secio = { version = "0.18.0", path = "../../protocols/secio" } +libp2p-tcp = { version = "0.18.0", path = "../../transports/tcp" } [build-dependencies] prost-build = "0.6" diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index ac241b52..a83df53d 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-kad" edition = "2018" description = "Kademlia protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -17,8 +17,8 @@ fnv = "1.0" futures_codec = "0.3.4" futures = "0.3.1" log = "0.4" -libp2p-core = { version = "0.17.0", path = "../../core" } -libp2p-swarm = { version = "0.17.0", path = "../../swarm" } +libp2p-core = { version = "0.18.0", path = "../../core" } +libp2p-swarm = { version = "0.18.0", path = "../../swarm" } multihash = "0.10" prost = "0.6.1" rand = "0.7.2" @@ -30,8 +30,8 @@ unsigned-varint = { version = "0.3", features = ["futures-codec"] } void = "1.0" [dev-dependencies] -libp2p-secio = { version = "0.17.0", path = "../secio" } -libp2p-yamux = { version = "0.17.0", path = "../../muxers/yamux" } +libp2p-secio = { version = "0.18.0", path = "../secio" } +libp2p-yamux = { version = "0.18.0", path = "../../muxers/yamux" } quickcheck = "0.9.0" [build-dependencies] diff --git a/protocols/mdns/Cargo.toml b/protocols/mdns/Cargo.toml index 5d21d3fa..a0684422 100644 --- a/protocols/mdns/Cargo.toml +++ b/protocols/mdns/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "libp2p-mdns" edition = "2018" -version = "0.17.0" +version = "0.18.0" description = "Implementation of the libp2p mDNS discovery method" authors = ["Parity Technologies "] license = "MIT" @@ -16,8 +16,8 @@ dns-parser = "0.8" either = "1.5.3" futures = "0.3.1" lazy_static = "1.2" -libp2p-core = { version = "0.17.0", path = "../../core" } -libp2p-swarm = { version = "0.17.0", path = "../../swarm" } +libp2p-core = { version = "0.18.0", path = "../../core" } +libp2p-swarm = { version = "0.18.0", path = "../../swarm" } log = "0.4" net2 = "0.2" rand = "0.7" diff --git a/protocols/noise/Cargo.toml b/protocols/noise/Cargo.toml index 802d5885..50392188 100644 --- a/protocols/noise/Cargo.toml +++ b/protocols/noise/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "libp2p-noise" description = "Cryptographic handshake protocol using the noise framework." -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,7 +11,7 @@ edition = "2018" curve25519-dalek = "2.0.0" futures = "0.3.1" lazy_static = "1.2" -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } log = "0.4" prost = "0.6.1" rand = "0.7.2" @@ -28,7 +28,7 @@ snow = { version = "0.6.1", features = ["default-resolver"], default-features = [dev-dependencies] env_logger = "0.7.1" -libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } +libp2p-tcp = { version = "0.18.0", path = "../../transports/tcp" } quickcheck = "0.9.0" sodiumoxide = "^0.2.5" diff --git a/protocols/ping/Cargo.toml b/protocols/ping/Cargo.toml index 942ae48a..b9323e81 100644 --- a/protocols/ping/Cargo.toml +++ b/protocols/ping/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-ping" edition = "2018" description = "Ping protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,8 +11,8 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.17.0", path = "../../core" } -libp2p-swarm = { version = "0.17.0", path = "../../swarm" } +libp2p-core = { version = "0.18.0", path = "../../core" } +libp2p-swarm = { version = "0.18.0", path = "../../swarm" } log = "0.4.1" rand = "0.7.2" void = "1.0" @@ -20,7 +20,7 @@ wasm-timer = "0.2" [dev-dependencies] async-std = "1.0" -libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } -libp2p-secio = { version = "0.17.0", path = "../../protocols/secio" } -libp2p-yamux = { version = "0.17.0", path = "../../muxers/yamux" } +libp2p-tcp = { version = "0.18.0", path = "../../transports/tcp" } +libp2p-secio = { version = "0.18.0", path = "../../protocols/secio" } +libp2p-yamux = { version = "0.18.0", path = "../../muxers/yamux" } quickcheck = "0.9.0" diff --git a/protocols/plaintext/Cargo.toml b/protocols/plaintext/Cargo.toml index b8df5dc8..07321552 100644 --- a/protocols/plaintext/Cargo.toml +++ b/protocols/plaintext/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-plaintext" edition = "2018" description = "Plaintext encryption dummy protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -13,7 +13,7 @@ categories = ["network-programming", "asynchronous"] bytes = "0.5" futures = "0.3.1" futures_codec = "0.3.4" -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } log = "0.4.8" prost = "0.6.1" rw-stream-sink = "0.2.0" diff --git a/protocols/pnet/Cargo.toml b/protocols/pnet/Cargo.toml index ca55ac1f..6897cfb4 100644 --- a/protocols/pnet/Cargo.toml +++ b/protocols/pnet/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-pnet" edition = "2018" description = "Private swarm support for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/secio/Cargo.toml b/protocols/secio/Cargo.toml index 66448676..a5be9059 100644 --- a/protocols/secio/Cargo.toml +++ b/protocols/secio/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-secio" edition = "2018" description = "Secio encryption protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -16,7 +16,7 @@ ctr = "0.3" futures = "0.3.1" hmac = "0.7.0" lazy_static = "1.2.0" -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } log = "0.4.6" prost = "0.6.1" pin-project = "0.4.6" @@ -48,8 +48,8 @@ aes-all = ["aesni"] [dev-dependencies] async-std = "1.0" criterion = "0.3" -libp2p-mplex = { version = "0.17.0", path = "../../muxers/mplex" } -libp2p-tcp = { version = "0.17.0", path = "../../transports/tcp" } +libp2p-mplex = { version = "0.18.0", path = "../../muxers/mplex" } +libp2p-tcp = { version = "0.18.0", path = "../../transports/tcp" } [[bench]] name = "bench" diff --git a/swarm/Cargo.toml b/swarm/Cargo.toml index cb37d555..27994090 100644 --- a/swarm/Cargo.toml +++ b/swarm/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-swarm" edition = "2018" description = "The libp2p swarm" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,7 +11,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" -libp2p-core = { version = "0.17.0", path = "../core" } +libp2p-core = { version = "0.18.0", path = "../core" } log = "0.4" rand = "0.7" smallvec = "1.0" @@ -19,6 +19,6 @@ wasm-timer = "0.2" void = "1" [dev-dependencies] -libp2p-mplex = { version = "0.17.0", path = "../muxers/mplex" } +libp2p-mplex = { version = "0.18.0", path = "../muxers/mplex" } quickcheck = "0.9.0" rand = "0.7.2" diff --git a/transports/dns/Cargo.toml b/transports/dns/Cargo.toml index d476eac5..11d1958f 100644 --- a/transports/dns/Cargo.toml +++ b/transports/dns/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-dns" edition = "2018" description = "DNS transport implementation for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -10,6 +10,6 @@ keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] [dependencies] -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } log = "0.4.1" futures = "0.3.1" diff --git a/transports/tcp/Cargo.toml b/transports/tcp/Cargo.toml index 69d9063e..b1d5d996 100644 --- a/transports/tcp/Cargo.toml +++ b/transports/tcp/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-tcp" edition = "2018" description = "TCP/IP transport protocol for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -15,7 +15,7 @@ futures = "0.3.1" futures-timer = "3.0" get_if_addrs = "0.5.3" ipnet = "2.0.0" -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } log = "0.4.1" tokio = { version = "0.2", default-features = false, features = ["tcp"], optional = true } diff --git a/transports/uds/Cargo.toml b/transports/uds/Cargo.toml index 5068577c..ee83f852 100644 --- a/transports/uds/Cargo.toml +++ b/transports/uds/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-uds" edition = "2018" description = "Unix domain sockets transport for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -11,7 +11,7 @@ categories = ["network-programming", "asynchronous"] [target.'cfg(all(unix, not(any(target_os = "emscripten", target_os = "unknown"))))'.dependencies] async-std = { version = "1.0", optional = true } -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } log = "0.4.1" futures = "0.3.1" tokio = { version = "0.2", default-features = false, features = ["uds"], optional = true } diff --git a/transports/wasm-ext/Cargo.toml b/transports/wasm-ext/Cargo.toml index 299df214..4661e8b8 100644 --- a/transports/wasm-ext/Cargo.toml +++ b/transports/wasm-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-wasm-ext" -version = "0.17.0" +version = "0.18.0" authors = ["Pierre Krieger "] edition = "2018" description = "Allows passing in an external transport in a WASM environment" @@ -12,7 +12,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.1" js-sys = "0.3.19" -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } parity-send-wrapper = "0.1.0" wasm-bindgen = "0.2.42" wasm-bindgen-futures = "0.4.4" diff --git a/transports/websocket/Cargo.toml b/transports/websocket/Cargo.toml index c1d4e03d..33d0419b 100644 --- a/transports/websocket/Cargo.toml +++ b/transports/websocket/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-websocket" edition = "2018" description = "WebSocket transport for libp2p" -version = "0.17.0" +version = "0.18.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -14,7 +14,7 @@ async-tls = "0.7.0" bytes = "0.5" either = "1.5.3" futures = "0.3.1" -libp2p-core = { version = "0.17.0", path = "../../core" } +libp2p-core = { version = "0.18.0", path = "../../core" } log = "0.4.8" quicksink = "0.1" rustls = "0.17.0" @@ -25,4 +25,4 @@ webpki = "0.21" webpki-roots = "0.18" [dev-dependencies] -libp2p-tcp = { version = "0.17.0", path = "../tcp" } +libp2p-tcp = { version = "0.18.0", path = "../tcp" } From 77a34c0a0d5756c9bfd22c2d7040717fe80bd195 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 16 Apr 2020 15:33:09 +0200 Subject: [PATCH 19/22] protocols/kad/query/peers/closest: Make at_capacity use max (#1548) When not making progress for `parallelism` time a `ClosestPeersIter` becomes `State::Stalled`. When stalled an iterator is allowed to make more parallel requests up to `num_results`. If `num_results` is smaller than `parallelism` make sure to still allow up to `parallelism` requests in-flight. Co-Authored-By: Roman Borschel --- protocols/kad/src/query/peers/closest.rs | 29 +++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/protocols/kad/src/query/peers/closest.rs b/protocols/kad/src/query/peers/closest.rs index f4e37e3d..987d0c36 100644 --- a/protocols/kad/src/query/peers/closest.rs +++ b/protocols/kad/src/query/peers/closest.rs @@ -369,7 +369,9 @@ impl ClosestPeersIter { /// k closest nodes it has not already queried". fn at_capacity(&self) -> bool { match self.state { - State::Stalled => self.num_waiting >= self.config.num_results, + State::Stalled => self.num_waiting >= usize::max( + self.config.num_results, self.config.parallelism + ), State::Iterating { .. } => self.num_waiting >= self.config.parallelism, State::Finished => true } @@ -691,4 +693,29 @@ mod tests { QuickCheck::new().tests(10).quickcheck(prop as fn(_) -> _) } + + #[test] + fn stalled_at_capacity() { + fn prop(mut iter: ClosestPeersIter) { + iter.state = State::Stalled; + + for i in 0..usize::max(iter.config.parallelism, iter.config.num_results) { + iter.num_waiting = i; + assert!( + !iter.at_capacity(), + "Iterator should not be at capacity if less than \ + `max(parallelism, num_results)` requests are waiting.", + ) + } + + iter.num_waiting = usize::max(iter.config.parallelism, iter.config.num_results); + assert!( + iter.at_capacity(), + "Iterator should be at capacity if `max(parallelism, num_results)` requests are \ + waiting.", + ) + } + + QuickCheck::new().tests(10).quickcheck(prop as fn(_)) + } } From 87b5efb3e8394b7609d894954b9af6cf4d49798b Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Fri, 17 Apr 2020 18:13:16 +0200 Subject: [PATCH 20/22] Make sure inject_dial_failure is called (#1549) * Make sure inject_dial_failure is called * Update CHANGELOG * Make libp2p-kad tests pass * Fix again * Update swarm/src/lib.rs Co-Authored-By: Toralf Wittner * Revert "Fix again" This reverts commit 80c0d3d908aff282d492f213d2ce34d12489167d. * Bump versions and CHANGELOG * Oops, didn't bump libp2p-swarm in libp2p Co-authored-by: Toralf Wittner --- CHANGELOG.md | 5 +++++ Cargo.toml | 4 ++-- swarm/Cargo.toml | 2 +- swarm/src/lib.rs | 54 ++++++++++++++++++++++++++---------------------- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fc8577b..94790dae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Version ??? +# Version 0.18.1 (2020-04-17) + +- `libp2p-swarm`: Make sure inject_dial_failure is called in all situations. + [PR 1549](https://github.com/libp2p/rust-libp2p/pull/1549) + # Version 0.18.0 (2020-04-09) - `libp2p-core`: Treat connection limit errors as pending connection errors. diff --git a/Cargo.toml b/Cargo.toml index 9f34ae20..c00945dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p" edition = "2018" description = "Peer-to-peer networking library" -version = "0.18.0" +version = "0.18.1" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -68,7 +68,7 @@ libp2p-pnet = { version = "0.18.0", path = "protocols/pnet", optional = true } libp2p-core = { version = "0.18.0", path = "core" } libp2p-core-derive = { version = "0.18.0", path = "misc/core-derive" } libp2p-secio = { version = "0.18.0", path = "protocols/secio", default-features = false, optional = true } -libp2p-swarm = { version = "0.18.0", path = "swarm" } +libp2p-swarm = { version = "0.18.1", path = "swarm" } libp2p-uds = { version = "0.18.0", path = "transports/uds", optional = true } libp2p-wasm-ext = { version = "0.18.0", path = "transports/wasm-ext", optional = true } libp2p-yamux = { version = "0.18.0", path = "muxers/yamux", optional = true } diff --git a/swarm/Cargo.toml b/swarm/Cargo.toml index 27994090..7feaa30a 100644 --- a/swarm/Cargo.toml +++ b/swarm/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-swarm" edition = "2018" description = "The libp2p swarm" -version = "0.18.0" +version = "0.18.1" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 11bb1a05..1c62d8d9 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -403,6 +403,12 @@ where TBehaviour: NetworkBehaviour, return Err(error) } } + } else { + log::debug!( + "New dialing attempt to disconnected peer {:?} failed: no address.", + peer_id + ); + me.behaviour.inject_dial_failure(&peer_id); } Ok(false) }, @@ -419,6 +425,12 @@ where TBehaviour: NetworkBehaviour, return Err(error) } } + } else { + log::debug!( + "New dialing attempt to disconnected peer {:?} failed: no address.", + peer_id + ); + me.behaviour.inject_dial_failure(&peer_id); } Ok(false) } @@ -427,6 +439,7 @@ where TBehaviour: NetworkBehaviour, Ok(false) }, Peer::Local => { + me.behaviour.inject_dial_failure(&peer_id); Err(ConnectionLimit { current: 0, limit: 0 }) } } @@ -701,34 +714,25 @@ where TBehaviour: NetworkBehaviour, if this.banned_peers.contains(&peer_id) { this.behaviour.inject_dial_failure(&peer_id); } else { - let result = match condition { + let condition_matched = match condition { DialPeerCondition::Disconnected - if this.network.is_disconnected(&peer_id) => - { - ExpandedSwarm::dial(this, &peer_id) - } + if this.network.is_disconnected(&peer_id) => true, DialPeerCondition::NotDialing - if !this.network.is_dialing(&peer_id) => - { - ExpandedSwarm::dial(this, &peer_id) - } - _ => { - log::trace!("Condition for new dialing attempt to {:?} not met: {:?}", - peer_id, condition); - if let Some(mut peer) = this.network.peer(peer_id.clone()).into_dialing() { - let addrs = this.behaviour.addresses_of_peer(peer.id()); - peer.connection().add_addresses(addrs); - } - Ok(false) - } + if !this.network.is_dialing(&peer_id) => true, + _ => false }; - match result { - Ok(false) => {}, - Ok(true) => return Poll::Ready(SwarmEvent::Dialing(peer_id)), - Err(err) => { - log::debug!("Initiating dialing attempt to {:?} failed: {:?}", - &peer_id, err); - this.behaviour.inject_dial_failure(&peer_id); + + if condition_matched { + if let Ok(true) = ExpandedSwarm::dial(this, &peer_id) { + return Poll::Ready(SwarmEvent::Dialing(peer_id)); + } + + } else { + log::trace!("Condition for new dialing attempt to {:?} not met: {:?}", + peer_id, condition); + if let Some(mut peer) = this.network.peer(peer_id.clone()).into_dialing() { + let addrs = this.behaviour.addresses_of_peer(peer.id()); + peer.connection().add_addresses(addrs); } } } From 9f981a4bb68aa29af109467a94b73d31b53eb19f Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 17 Apr 2020 19:57:35 +0200 Subject: [PATCH 21/22] src/query/peers/closest: Consider K_VALUE peers at initialization (#1536) * protocols/kad/query/peers/closest: Consider K_VALUE nodes at init By considering `K_VALUE` at `ClosestPeersIter` initialization, the initial peer set length is independent of `num_results` and thus of the `replication_factor`. * protocols/kad/src/behaviour/test: Enable building single nodes Introduces the `build_node` function to build a single not connected node. Along the way replace the notion of a `port_base` with returning the actual `Multiaddr` of the node. * protocols/kad/behaviour/test: Fix bootstrap test initialization When looking for the closest node to a key, Kademlia considers ALPHA_VALUE nodes to query at initialization. If `num_groups` is larger than ALPHA_VALUE the remaining locally known nodes will not be considered. Given that no other node is aware of them other than node 1, they would be lost entirely. To prevent the above restrict `num_groups` to be equal or smaller than ALPHA_VALUE. * protocols/kad/behaviour/test: Fix put_record and get_provider In the past, when trying to find the closest nodes to a key, Kademlia would consider `num_result` amount of nodes to query out of all the nodes it is aware of. Both the `put_record` and the `get_provider` tests initialized their swarms in the same way. The tests took the replication factor to use as an input. The number of results to get was equal to the replication factor. The amount of swarms to start was twice the replication factor. Nodes would be grouped in two groups a replication factor nodes. The first node would be aware of all of the nodes in the first group. The last node of the first group would be aware of all the nodes in the second group. By coincidence (I assume) these numbers played together very well. At initialization the first node would consider `num_results` amount of peers (see first paragraph). It would then contact each of them. As the first node is aware of the last node of the first group which in turn is aware of all nodes in the second group, the first node would eventually discover all nodes. Recently the amount of nodes Kademlia considers at initialization when looking for the nodes closest to a key was changed to only consider ALPHA nodes. With this in mind playing through the test setup above again would result in (1) `replication_factor - ALPHA` nodes being entirely lost as the first node would never consider them and (2) the first node probably never contacting the last node out of the first group and thus not discovering any nodes of the second group. To keep the multi hop discovery in place while not basing ones test setup on the lucky assumption of Kademlia considering replication factor amount of nodes at initialization, this patch alters the two tests: Build a fully connected set of nodes and one addition node (the first node). Connect the first node to a single node of the fully connected set (simulating a boot node). Continue as done previously. Co-authored-by: Roman Borschel --- protocols/kad/src/behaviour/test.rs | 268 ++++++++++++++++------- protocols/kad/src/query/peers/closest.rs | 22 +- 2 files changed, 212 insertions(+), 78 deletions(-) diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index 22436811..afeda26a 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -22,7 +22,7 @@ use super::*; -use crate::K_VALUE; +use crate::{ALPHA_VALUE, K_VALUE}; use crate::kbucket::Distance; use crate::record::store::MemoryStore; use futures::{ @@ -35,7 +35,7 @@ use libp2p_core::{ Transport, identity, transport::MemoryTransport, - multiaddr::{Protocol, multiaddr}, + multiaddr::{Protocol, Multiaddr, multiaddr}, muxing::StreamMuxerBox, upgrade }; @@ -49,61 +49,83 @@ use multihash::{wrap, Code, Multihash}; type TestSwarm = Swarm>; +fn build_node() -> (Multiaddr, TestSwarm) { + build_node_with_config(Default::default()) +} + +fn build_node_with_config(cfg: KademliaConfig) -> (Multiaddr, TestSwarm) { + let local_key = identity::Keypair::generate_ed25519(); + let local_public_key = local_key.public(); + let transport = MemoryTransport::default() + .upgrade(upgrade::Version::V1) + .authenticate(SecioConfig::new(local_key)) + .multiplex(yamux::Config::default()) + .map(|(p, m), _| (p, StreamMuxerBox::new(m))) + .map_err(|e| -> io::Error { panic!("Failed to create transport: {:?}", e); }) + .boxed(); + + let local_id = local_public_key.clone().into_peer_id(); + let store = MemoryStore::new(local_id.clone()); + let behaviour = Kademlia::with_config(local_id.clone(), store, cfg.clone()); + + let mut swarm = Swarm::new(transport, behaviour, local_id); + + let address: Multiaddr = Protocol::Memory(random::()).into(); + Swarm::listen_on(&mut swarm, address.clone()).unwrap(); + + (address, swarm) +} + /// Builds swarms, each listening on a port. Does *not* connect the nodes together. -fn build_nodes(num: usize) -> (u64, Vec) { +fn build_nodes(num: usize) -> Vec<(Multiaddr, TestSwarm)> { build_nodes_with_config(num, Default::default()) } /// Builds swarms, each listening on a port. Does *not* connect the nodes together. -fn build_nodes_with_config(num: usize, cfg: KademliaConfig) -> (u64, Vec) { - let port_base = 1 + random::() % (u64::MAX - num as u64); - let mut result: Vec> = Vec::with_capacity(num); - - for _ in 0 .. num { - let local_key = identity::Keypair::generate_ed25519(); - let local_public_key = local_key.public(); - let transport = MemoryTransport::default() - .upgrade(upgrade::Version::V1) - .authenticate(SecioConfig::new(local_key)) - .multiplex(yamux::Config::default()) - .map(|(p, m), _| (p, StreamMuxerBox::new(m))) - .map_err(|e| -> io::Error { panic!("Failed to create transport: {:?}", e); }) - .boxed(); - - let local_id = local_public_key.clone().into_peer_id(); - let store = MemoryStore::new(local_id.clone()); - let behaviour = Kademlia::with_config(local_id.clone(), store, cfg.clone()); - result.push(Swarm::new(transport, behaviour, local_id)); - } - - for (i, s) in result.iter_mut().enumerate() { - Swarm::listen_on(s, Protocol::Memory(port_base + i as u64).into()).unwrap(); - } - - (port_base, result) +fn build_nodes_with_config(num: usize, cfg: KademliaConfig) -> Vec<(Multiaddr, TestSwarm)> { + (0..num).map(|_| build_node_with_config(cfg.clone())).collect() } -fn build_connected_nodes(total: usize, step: usize) -> (Vec, Vec) { +fn build_connected_nodes(total: usize, step: usize) -> Vec<(Multiaddr, TestSwarm)> { build_connected_nodes_with_config(total, step, Default::default()) } fn build_connected_nodes_with_config(total: usize, step: usize, cfg: KademliaConfig) - -> (Vec, Vec) + -> Vec<(Multiaddr, TestSwarm)> { - let (port_base, mut swarms) = build_nodes_with_config(total, cfg); - let swarm_ids: Vec<_> = swarms.iter().map(Swarm::local_peer_id).cloned().collect(); + let mut swarms = build_nodes_with_config(total, cfg); + let swarm_ids: Vec<_> = swarms.iter() + .map(|(addr, swarm)| (addr.clone(), Swarm::local_peer_id(swarm).clone())) + .collect(); let mut i = 0; - for (j, peer) in swarm_ids.iter().enumerate().skip(1) { + for (j, (addr, peer_id)) in swarm_ids.iter().enumerate().skip(1) { if i < swarm_ids.len() { - swarms[i].add_address(&peer, Protocol::Memory(port_base + j as u64).into()); + swarms[i].1.add_address(peer_id, addr.clone()); } if j % step == 0 { i += step; } } - (swarm_ids, swarms) + swarms +} + +fn build_fully_connected_nodes_with_config(total: usize, cfg: KademliaConfig) + -> Vec<(Multiaddr, TestSwarm)> +{ + let mut swarms = build_nodes_with_config(total, cfg); + let swarm_addr_and_peer_id: Vec<_> = swarms.iter() + .map(|(addr, swarm)| (addr.clone(), Swarm::local_peer_id(swarm).clone())) + .collect(); + + for (_addr, swarm) in swarms.iter_mut() { + for (addr, peer) in &swarm_addr_and_peer_id { + swarm.add_address(&peer, addr.clone()); + } + } + + swarms } fn random_multihash() -> Multihash { @@ -114,8 +136,17 @@ fn random_multihash() -> Multihash { fn bootstrap() { fn run(rng: &mut impl Rng) { let num_total = rng.gen_range(2, 20); - let num_group = rng.gen_range(1, num_total); - let (swarm_ids, mut swarms) = build_connected_nodes(num_total, num_group); + // When looking for the closest node to a key, Kademlia considers ALPHA_VALUE nodes to query + // at initialization. If `num_groups` is larger than ALPHA_VALUE the remaining locally known + // nodes will not be considered. Given that no other node is aware of them, they would be + // lost entirely. To prevent the above restrict `num_groups` to be equal or smaller than + // ALPHA_VALUE. + let num_group = rng.gen_range(1, (num_total % ALPHA_VALUE.get()) + 2); + + let mut swarms = build_connected_nodes(num_total, num_group).into_iter() + .map(|(_a, s)| s) + .collect::>(); + let swarm_ids: Vec<_> = swarms.iter().map(Swarm::local_peer_id).cloned().collect(); swarms[0].bootstrap(); @@ -166,7 +197,10 @@ fn query_iter() { fn run(rng: &mut impl Rng) { let num_total = rng.gen_range(2, 20); - let (swarm_ids, mut swarms) = build_connected_nodes(num_total, 1); + let mut swarms = build_connected_nodes(num_total, 1).into_iter() + .map(|(_a, s)| s) + .collect::>(); + let swarm_ids: Vec<_> = swarms.iter().map(Swarm::local_peer_id).cloned().collect(); // Ask the first peer in the list to search a random peer. The search should // propagate forwards through the list of peers. @@ -218,7 +252,9 @@ fn unresponsive_not_returned_direct() { // Build one node. It contains fake addresses to non-existing nodes. We ask it to find a // random peer. We make sure that no fake address is returned. - let (_, mut swarms) = build_nodes(1); + let mut swarms = build_nodes(1).into_iter() + .map(|(_a, s)| s) + .collect::>(); // Add fake addresses. for _ in 0 .. 10 { @@ -258,16 +294,20 @@ fn unresponsive_not_returned_indirect() { // non-existing nodes. We ask node #2 to find a random peer. We make sure that no fake address // is returned. - let (port_base, mut swarms) = build_nodes(2); + let mut swarms = build_nodes(2); // Add fake addresses to first. - let first_peer_id = Swarm::local_peer_id(&swarms[0]).clone(); for _ in 0 .. 10 { - swarms[0].add_address(&PeerId::random(), multiaddr![Udp(10u16)]); + swarms[0].1.add_address(&PeerId::random(), multiaddr![Udp(10u16)]); } // Connect second to first. - swarms[1].add_address(&first_peer_id, Protocol::Memory(port_base).into()); + let first_peer_id = Swarm::local_peer_id(&swarms[0].1).clone(); + let first_address = swarms[0].0.clone(); + swarms[1].1.add_address(&first_peer_id, first_address); + + // Drop the swarm addresses. + let mut swarms = swarms.into_iter().map(|(_addr, swarm)| swarm).collect::>(); // Ask second to search a random value. let search_target = PeerId::random(); @@ -299,12 +339,19 @@ fn unresponsive_not_returned_indirect() { #[test] fn get_record_not_found() { - let (port_base, mut swarms) = build_nodes(3); + let mut swarms = build_nodes(3); - let swarm_ids: Vec<_> = swarms.iter().map(Swarm::local_peer_id).cloned().collect(); + let swarm_ids: Vec<_> = swarms.iter() + .map(|(_addr, swarm)| Swarm::local_peer_id(swarm)) + .cloned() + .collect(); - swarms[0].add_address(&swarm_ids[1], Protocol::Memory(port_base + 1).into()); - swarms[1].add_address(&swarm_ids[2], Protocol::Memory(port_base + 2).into()); + let (second, third) = (swarms[1].0.clone(), swarms[2].0.clone()); + swarms[0].1.add_address(&swarm_ids[1], second); + swarms[1].1.add_address(&swarm_ids[2], third); + + // Drop the swarm addresses. + let mut swarms = swarms.into_iter().map(|(_addr, swarm)| swarm).collect::>(); let target_key = record::Key::from(random_multihash()); swarms[0].get_record(&target_key, Quorum::One); @@ -338,16 +385,35 @@ fn get_record_not_found() { ) } +/// A node joining a fully connected network via a single bootnode should be able to put a record to +/// the X closest nodes of the network where X is equal to the configured replication factor. #[test] fn put_record() { fn prop(replication_factor: usize, records: Vec) { let replication_factor = NonZeroUsize::new(replication_factor % (K_VALUE.get() / 2) + 1).unwrap(); let num_total = replication_factor.get() * 2; - let num_group = replication_factor.get(); let mut config = KademliaConfig::default(); config.set_replication_factor(replication_factor); - let (swarm_ids, mut swarms) = build_connected_nodes_with_config(num_total, num_group, config); + + let mut swarms = { + let mut fully_connected_swarms = build_fully_connected_nodes_with_config( + num_total - 1, + config.clone(), + ); + + let mut single_swarm = build_node_with_config(config); + single_swarm.1.add_address( + Swarm::local_peer_id(&fully_connected_swarms[0].1), + fully_connected_swarms[0].0.clone(), + ); + + let mut swarms = vec![single_swarm]; + swarms.append(&mut fully_connected_swarms); + + // Drop the swarm addresses. + swarms.into_iter().map(|(_addr, swarm)| swarm).collect::>() + }; let records = records.into_iter() .take(num_total) @@ -378,7 +444,7 @@ fn put_record() { Poll::Ready(Some(KademliaEvent::PutRecordResult(res))) | Poll::Ready(Some(KademliaEvent::RepublishRecordResult(res))) => { match res { - Err(e) => panic!(e), + Err(e) => panic!("{:?}", e), Ok(ok) => { assert!(records.contains_key(&ok.key)); let record = swarm.store.get(&ok.key).unwrap(); @@ -408,10 +474,14 @@ fn put_record() { assert_eq!(r.key, expected.key); assert_eq!(r.value, expected.value); assert_eq!(r.expires, expected.expires); - assert_eq!(r.publisher.as_ref(), Some(&swarm_ids[0])); + assert_eq!(r.publisher.as_ref(), Some(Swarm::local_peer_id(&swarms[0]))); let key = kbucket::Key::new(r.key.clone()); - let mut expected = swarm_ids.clone().split_off(1); + let mut expected = swarms.iter() + .skip(1) + .map(Swarm::local_peer_id) + .cloned() + .collect::>(); expected.sort_by(|id1, id2| kbucket::Key::new(id1.clone()).distance(&key).cmp( &kbucket::Key::new(id2.clone()).distance(&key))); @@ -421,17 +491,32 @@ fn put_record() { .take(replication_factor.get()) .collect::>(); - let actual = swarms.iter().enumerate().skip(1) - .filter_map(|(i, s)| - if s.store.get(key.preimage()).is_some() { - Some(swarm_ids[i].clone()) + let actual = swarms.iter() + .skip(1) + .filter_map(|swarm| + if swarm.store.get(key.preimage()).is_some() { + Some(Swarm::local_peer_id(swarm).clone()) } else { None }) .collect::>(); assert_eq!(actual.len(), replication_factor.get()); - assert_eq!(actual, expected); + + let actual_not_expected = actual.difference(&expected) + .collect::>(); + assert!( + actual_not_expected.is_empty(), + "Did not expect records to be stored on nodes {:?}.", + actual_not_expected, + ); + + let expected_not_actual = expected.difference(&actual) + .collect::>(); + assert!(expected_not_actual.is_empty(), + "Expected record to be stored on nodes {:?}.", + expected_not_actual, + ); } if republished { @@ -452,17 +537,21 @@ fn put_record() { ) } - QuickCheck::new().tests(3).quickcheck(prop as fn(_,_)) + QuickCheck::new().tests(3).quickcheck(prop as fn(_,_) -> _) } #[test] fn get_value() { - let (port_base, mut swarms) = build_nodes(3); + let mut swarms = build_nodes(3); - let swarm_ids: Vec<_> = swarms.iter().map(Swarm::local_peer_id).cloned().collect(); + // Let first peer know of second peer and second peer know of third peer. + for i in 0..2 { + let (peer_id, address) = (Swarm::local_peer_id(&swarms[i+1].1).clone(), swarms[i+1].0.clone()); + swarms[i].1.add_address(&peer_id, address); + } - swarms[0].add_address(&swarm_ids[1], Protocol::Memory(port_base + 1).into()); - swarms[1].add_address(&swarm_ids[2], Protocol::Memory(port_base + 2).into()); + // Drop the swarm addresses. + let mut swarms = swarms.into_iter().map(|(_addr, swarm)| swarm).collect::>(); let record = Record::new(random_multihash(), vec![4,5,6]); @@ -496,7 +585,9 @@ fn get_value() { fn get_value_many() { // TODO: Randomise let num_nodes = 12; - let (_, mut swarms) = build_connected_nodes(num_nodes, num_nodes); + let mut swarms = build_connected_nodes(num_nodes, 3).into_iter() + .map(|(_addr, swarm)| swarm) + .collect::>(); let num_results = 10; let record = Record::new(random_multihash(), vec![4,5,6]); @@ -530,17 +621,36 @@ fn get_value_many() { ) } +/// A node joining a fully connected network via a single bootnode should be able to add itself as a +/// provider to the X closest nodes of the network where X is equal to the configured replication +/// factor. #[test] fn add_provider() { fn prop(replication_factor: usize, keys: Vec) { let replication_factor = NonZeroUsize::new(replication_factor % (K_VALUE.get() / 2) + 1).unwrap(); let num_total = replication_factor.get() * 2; - let num_group = replication_factor.get(); let mut config = KademliaConfig::default(); config.set_replication_factor(replication_factor); - let (swarm_ids, mut swarms) = build_connected_nodes_with_config(num_total, num_group, config); + let mut swarms = { + let mut fully_connected_swarms = build_fully_connected_nodes_with_config( + num_total - 1, + config.clone(), + ); + + let mut single_swarm = build_node_with_config(config); + single_swarm.1.add_address( + Swarm::local_peer_id(&fully_connected_swarms[0].1), + fully_connected_swarms[0].0.clone(), + ); + + let mut swarms = vec![single_swarm]; + swarms.append(&mut fully_connected_swarms); + + // Drop addresses before returning. + swarms.into_iter().map(|(_addr, swarm)| swarm).collect::>() + }; let keys: HashSet<_> = keys.into_iter().take(num_total).collect(); @@ -594,10 +704,10 @@ fn add_provider() { // each key was published to the `replication_factor` closest peers. while let Some(key) = results.pop() { // Collect the nodes that have a provider record for `key`. - let actual = swarms.iter().enumerate().skip(1) - .filter_map(|(i, s)| - if s.store.providers(&key).len() == 1 { - Some(swarm_ids[i].clone()) + let actual = swarms.iter().skip(1) + .filter_map(|swarm| + if swarm.store.providers(&key).len() == 1 { + Some(Swarm::local_peer_id(&swarm).clone()) } else { None }) @@ -609,7 +719,11 @@ fn add_provider() { return Poll::Pending } - let mut expected = swarm_ids.clone().split_off(1); + let mut expected = swarms.iter() + .skip(1) + .map(Swarm::local_peer_id) + .cloned() + .collect::>(); let kbucket_key = kbucket::Key::new(key); expected.sort_by(|id1, id2| kbucket::Key::new(id1.clone()).distance(&kbucket_key).cmp( @@ -625,8 +739,8 @@ fn add_provider() { // One round of publishing is complete. assert!(results.is_empty()); - for s in &swarms { - assert_eq!(s.queries.size(), 0); + for swarm in &swarms { + assert_eq!(swarm.queries.size(), 0); } if republished { @@ -656,19 +770,19 @@ fn add_provider() { /// arithmetic overflow, see https://github.com/libp2p/rust-libp2p/issues/1290. #[test] fn exceed_jobs_max_queries() { - let (_, mut swarms) = build_nodes(1); + let (_addr, mut swarm) = build_node(); let num = JOBS_MAX_QUERIES + 1; for _ in 0 .. num { - swarms[0].bootstrap(); + swarm.bootstrap(); } - assert_eq!(swarms[0].queries.size(), num); + assert_eq!(swarm.queries.size(), num); block_on( poll_fn(move |ctx| { for _ in 0 .. num { // There are no other nodes, so the queries finish instantly. - if let Poll::Ready(Some(e)) = swarms[0].poll_next_unpin(ctx) { + if let Poll::Ready(Some(e)) = swarm.poll_next_unpin(ctx) { if let KademliaEvent::BootstrapResult(r) = e { assert!(r.is_ok(), "Unexpected error") } else { diff --git a/protocols/kad/src/query/peers/closest.rs b/protocols/kad/src/query/peers/closest.rs index 987d0c36..3e9b6a62 100644 --- a/protocols/kad/src/query/peers/closest.rs +++ b/protocols/kad/src/query/peers/closest.rs @@ -108,7 +108,7 @@ impl ClosestPeersIter { let state = PeerState::NotContacted; (distance, Peer { key, state }) }) - .take(config.num_results)); + .take(K_VALUE.into())); // The iterator initially makes progress by iterating towards the target. let state = State::Iterating { no_progress : 0 }; @@ -695,6 +695,26 @@ mod tests { } #[test] + fn without_success_try_up_to_k_peers() { + fn prop(mut iter: ClosestPeersIter) { + let now = Instant::now(); + + for _ in 0..(usize::min(iter.closest_peers.len(), K_VALUE.get())) { + match iter.next(now) { + PeersIterState::Waiting(Some(p)) => { + let peer = p.clone().into_owned(); + iter.on_failure(&peer); + }, + _ => panic!("Expected iterator to yield another peer to query."), + } + } + + assert_eq!(PeersIterState::Finished, iter.next(now)); + } + + QuickCheck::new().tests(10).quickcheck(prop as fn(_)) + } + fn stalled_at_capacity() { fn prop(mut iter: ClosestPeersIter) { iter.state = State::Stalled; From f187fd4d1cb5621873263ac51d7df4abb450a0a3 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 20 Apr 2020 10:05:17 +0200 Subject: [PATCH 22/22] CHANGELOG: Add changelog entry for 9f981a4bb6 (#1550) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94790dae..c49535ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Version ??? +- `libp2p-kad`: Consider fixed (K_VALUE) amount of peers at closest query + initialization. Unless `KademliaConfig::set_replication_factor` is used change + has no effect. + [PR 1536](https://github.com/libp2p/rust-libp2p/pull/1536) # Version 0.18.1 (2020-04-17)