Upgrade to libp2p 0.51.3 (#13587)

* client/network: upgrade to libp2p 0.51.0

* make discovery.rs compile

* make peer_info.rs compile

* changes to notifications and request-response proto

* make service.rs compile

* towards making request_responses.rs compile

* make request_responses.rs compile

* make request_responses.rs compile

* fix notifications/behaviour.rs tests

* fix warnings

* remove old code

* allow deprecated code (temporary)

* upgrade to libp2p 0.51.1

* add TODO for behaviour tests

* return empty vec if peer_id is absent

https://github.com/paritytech/substrate/pull/13587#discussion_r1141695167

fyi: I don't really know what the old behaviour was.

* update comment to reflect new defaults

Closes #13338

* Revert "update comment to reflect new defaults"

This reverts commit 7a981abd69308e9d522ec94905f181439a1b1dba.

* remove config.rs (from wrong merge)

* upgrade to libp2p 0.51.2

* fix formatting

* use handle_pending_outbound_connection in networt_state RPC

* update deps

* use re-exports when we use other libp2p packages

* Apply suggestions from code review

Co-authored-by: Dmitry Markin <dmitry@markin.tech>

* format code

* handle potential errors in network_state RPC

* only update libp2p crate

* update libp2p-core

* fix docs

* use libp2p-identity instead of libp2p

where it's possible. libp2p-identity is much smaller, hence makes sense
to use it instead of larger libp2p crate.

* Update client/network/src/discovery.rs

Co-authored-by: Aaro Altonen <48052676+altonen@users.noreply.github.com>

* update Cargo.lock

* add comment for per_connection_event_buffer_size

current value is somewhat arbitrary and needs to be tweaked depending on
memory usage and network worker sleep stats.

* fix link format

* update Cargo.lock

* upgrade to libp2p 0.51.3

* deprecate mplex

* Revert "deprecate mplex"

This reverts commit 9e25820e706e464a0e962a8604861fcb2a7641eb.

* Revert "upgrade to libp2p 0.51.3"

This reverts commit 6544dd4138e2f89517bd7c7281fc78a638ec7040.

* use new libp2p version in `statement` crate

* pin version temporarily

* libp2p 0.51.3

* deprecate mplex

* deprecate legacy noise handshake

* fix build error

* update libp2p-identity

* enable libp2p-identity:ed25519 feature in sc-consensus

* enable ed25519 for peerset as well

---------

Co-authored-by: Dmitry Markin <dmitry@markin.tech>
Co-authored-by: Aaro Altonen <48052676+altonen@users.noreply.github.com>
Co-authored-by: parity-processbot <>
This commit is contained in:
Anton
2023-05-12 11:12:51 +04:00
committed by GitHub
parent 56940bc874
commit e4b1aa1811
48 changed files with 1005 additions and 927 deletions
+145 -153
View File
@@ -18,8 +18,8 @@
//! Collection of request-response protocols.
//!
//! The [`RequestResponse`] struct defined in this module provides support for zero or more
//! so-called "request-response" protocols.
//! The [`RequestResponsesBehaviour`] struct defined in this module provides support for zero or
//! more so-called "request-response" protocols.
//!
//! A request-response protocol works in the following way:
//!
@@ -41,17 +41,15 @@ use futures::{
prelude::*,
};
use libp2p::{
core::{connection::ConnectionId, Multiaddr, PeerId},
request_response::{
handler::RequestResponseHandler, ProtocolSupport, RequestResponse, RequestResponseCodec,
RequestResponseEvent, RequestResponseMessage, ResponseChannel,
},
core::{Endpoint, Multiaddr},
request_response::{self, Behaviour, Codec, Message, ProtocolSupport, ResponseChannel},
swarm::{
behaviour::{ConnectionClosed, DialFailure, FromSwarm, ListenFailure},
behaviour::{ConnectionClosed, FromSwarm},
handler::multi::MultiHandler,
ConnectionHandler, IntoConnectionHandler, NetworkBehaviour, NetworkBehaviourAction,
PollParameters,
ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters, THandler,
THandlerInEvent, THandlerOutEvent, ToSwarm,
},
PeerId,
};
use sc_peerset::{PeersetHandle, BANNED_THRESHOLD};
@@ -64,9 +62,7 @@ use std::{
time::{Duration, Instant},
};
pub use libp2p::request_response::{
InboundFailure, OutboundFailure, RequestId, RequestResponseConfig,
};
pub use libp2p::request_response::{Config, InboundFailure, OutboundFailure, RequestId};
/// Error in a request.
#[derive(Debug, thiserror::Error)]
@@ -260,14 +256,13 @@ impl From<(ProtocolName, RequestId)> for ProtocolRequestId {
/// Implementation of `NetworkBehaviour` that provides support for request-response protocols.
pub struct RequestResponsesBehaviour {
/// The multiple sub-protocols, by name.
/// Contains the underlying libp2p `RequestResponse` behaviour, plus an optional
///
/// Contains the underlying libp2p request-response [`Behaviour`], plus an optional
/// "response builder" used to build responses for incoming requests.
protocols: HashMap<
ProtocolName,
(RequestResponse<GenericCodec>, Option<mpsc::Sender<IncomingRequest>>),
>,
protocols:
HashMap<ProtocolName, (Behaviour<GenericCodec>, Option<mpsc::Sender<IncomingRequest>>)>,
/// Pending requests, passed down to a [`RequestResponse`] behaviour, awaiting a reply.
/// Pending requests, passed down to a request-response [`Behaviour`], awaiting a reply.
pending_requests:
HashMap<ProtocolRequestId, (Instant, oneshot::Sender<Result<Vec<u8>, RequestFailure>>)>,
@@ -324,7 +319,7 @@ impl RequestResponsesBehaviour {
) -> Result<Self, RegisterError> {
let mut protocols = HashMap::new();
for protocol in list {
let mut cfg = RequestResponseConfig::default();
let mut cfg = Config::default();
cfg.set_connection_keep_alive(Duration::from_secs(10));
cfg.set_request_timeout(protocol.request_timeout);
@@ -334,7 +329,7 @@ impl RequestResponsesBehaviour {
ProtocolSupport::Outbound
};
let rq_rp = RequestResponse::new(
let rq_rp = Behaviour::new(
GenericCodec {
max_request_size: protocol.max_request_size,
max_response_size: protocol.max_response_size,
@@ -401,50 +396,79 @@ impl RequestResponsesBehaviour {
);
}
}
fn new_handler_with_replacement(
&mut self,
protocol: String,
handler: RequestResponseHandler<GenericCodec>,
) -> <RequestResponsesBehaviour as NetworkBehaviour>::ConnectionHandler {
let mut handlers: HashMap<_, _> = self
.protocols
.iter_mut()
.map(|(p, (r, _))| (p.to_string(), NetworkBehaviour::new_handler(r)))
.collect();
if let Some(h) = handlers.get_mut(&protocol) {
*h = handler
}
MultiHandler::try_from_iter(handlers).expect(
"Protocols are in a HashMap and there can be at most one handler per protocol name, \
which is the only possible error; qed",
)
}
}
impl NetworkBehaviour for RequestResponsesBehaviour {
type ConnectionHandler = MultiHandler<
String,
<RequestResponse<GenericCodec> as NetworkBehaviour>::ConnectionHandler,
>;
type ConnectionHandler =
MultiHandler<String, <Behaviour<GenericCodec> as NetworkBehaviour>::ConnectionHandler>;
type OutEvent = Event;
fn new_handler(&mut self) -> Self::ConnectionHandler {
let iter = self
.protocols
.iter_mut()
.map(|(p, (r, _))| (p.to_string(), NetworkBehaviour::new_handler(r)));
MultiHandler::try_from_iter(iter).expect(
"Protocols are in a HashMap and there can be at most one handler per protocol name, \
which is the only possible error; qed",
)
fn handle_pending_inbound_connection(
&mut self,
_connection_id: ConnectionId,
_local_addr: &Multiaddr,
_remote_addr: &Multiaddr,
) -> Result<(), ConnectionDenied> {
Ok(())
}
fn addresses_of_peer(&mut self, _: &PeerId) -> Vec<Multiaddr> {
Vec::new()
fn handle_pending_outbound_connection(
&mut self,
_connection_id: ConnectionId,
_maybe_peer: Option<PeerId>,
_addresses: &[Multiaddr],
_effective_role: Endpoint,
) -> Result<Vec<Multiaddr>, ConnectionDenied> {
Ok(Vec::new())
}
fn handle_established_inbound_connection(
&mut self,
connection_id: ConnectionId,
peer: PeerId,
local_addr: &Multiaddr,
remote_addr: &Multiaddr,
) -> Result<THandler<Self>, ConnectionDenied> {
let iter = self.protocols.iter_mut().filter_map(|(p, (r, _))| {
if let Ok(handler) = r.handle_established_inbound_connection(
connection_id,
peer,
local_addr,
remote_addr,
) {
Some((p.to_string(), handler))
} else {
None
}
});
Ok(MultiHandler::try_from_iter(iter).expect(
"Protocols are in a HashMap and there can be at most one handler per protocol name, \
which is the only possible error; qed",
))
}
fn handle_established_outbound_connection(
&mut self,
connection_id: ConnectionId,
peer: PeerId,
addr: &Multiaddr,
role_override: Endpoint,
) -> Result<THandler<Self>, ConnectionDenied> {
let iter = self.protocols.iter_mut().filter_map(|(p, (r, _))| {
if let Ok(handler) =
r.handle_established_outbound_connection(connection_id, peer, addr, role_override)
{
Some((p.to_string(), handler))
} else {
None
}
});
Ok(MultiHandler::try_from_iter(iter).expect(
"Protocols are in a HashMap and there can be at most one handler per protocol name, \
which is the only possible error; qed",
))
}
fn on_swarm_event(&mut self, event: FromSwarm<Self::ConnectionHandler>) {
@@ -477,42 +501,17 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
)
}
},
FromSwarm::DialFailure(DialFailure { peer_id, error, handler }) => {
for (p_name, p_handler) in handler.into_iter() {
if let Some((proto, _)) = self.protocols.get_mut(p_name.as_str()) {
proto.on_swarm_event(FromSwarm::DialFailure(DialFailure {
peer_id,
handler: p_handler,
error,
}));
} else {
log::error!(
target: "sub-libp2p",
"on_swarm_event/dial_failure: no request-response instance registered for protocol {:?}",
p_name,
)
}
}
},
FromSwarm::DialFailure(e) =>
for (p, _) in self.protocols.values_mut() {
NetworkBehaviour::on_swarm_event(p, FromSwarm::DialFailure(e));
},
FromSwarm::ListenerClosed(e) =>
for (p, _) in self.protocols.values_mut() {
NetworkBehaviour::on_swarm_event(p, FromSwarm::ListenerClosed(e));
},
FromSwarm::ListenFailure(ListenFailure { local_addr, send_back_addr, handler }) =>
for (p_name, p_handler) in handler.into_iter() {
if let Some((proto, _)) = self.protocols.get_mut(p_name.as_str()) {
proto.on_swarm_event(FromSwarm::ListenFailure(ListenFailure {
local_addr,
send_back_addr,
handler: p_handler,
}));
} else {
log::error!(
target: "sub-libp2p",
"on_swarm_event/listen_failure: no request-response instance registered for protocol {:?}",
p_name,
)
}
FromSwarm::ListenFailure(e) =>
for (p, _) in self.protocols.values_mut() {
NetworkBehaviour::on_swarm_event(p, FromSwarm::ListenFailure(e));
},
FromSwarm::ListenerError(e) =>
for (p, _) in self.protocols.values_mut() {
@@ -549,25 +548,25 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
&mut self,
peer_id: PeerId,
connection_id: ConnectionId,
(p_name, event): <<Self::ConnectionHandler as IntoConnectionHandler>::Handler as
ConnectionHandler>::OutEvent,
event: THandlerOutEvent<Self>,
) {
if let Some((proto, _)) = self.protocols.get_mut(&*p_name) {
return proto.on_connection_handler_event(peer_id, connection_id, event)
let p_name = event.0;
if let Some((proto, _)) = self.protocols.get_mut(p_name.as_str()) {
return proto.on_connection_handler_event(peer_id, connection_id, event.1)
} else {
log::warn!(
target: "sub-libp2p",
"on_connection_handler_event: no request-response instance registered for protocol {:?}",
p_name
);
}
log::warn!(
target: "sub-libp2p",
"on_connection_handler_event: no request-response instance registered for protocol {:?}",
p_name
);
}
fn poll(
&mut self,
cx: &mut Context,
params: &mut impl PollParameters,
) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ConnectionHandler>> {
) -> Poll<ToSwarm<Self::OutEvent, THandlerInEvent<Self>>> {
'poll_all: loop {
if let Some(message_request) = self.message_request.take() {
// Now we can can poll `MessageRequest` until we get the reputation
@@ -621,8 +620,8 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
// initialization.
if let Some(mut resp_builder) = resp_builder {
// If the response builder is too busy, silently drop `tx`. This
// will be reported by the corresponding `RequestResponse` through
// an `InboundFailure::Omission` event.
// will be reported by the corresponding request-response [`Behaviour`]
// through an `InboundFailure::Omission` event.
let _ = resp_builder.try_send(IncomingRequest {
peer,
payload: request,
@@ -674,7 +673,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
if let Some((protocol, _)) = self.protocols.get_mut(&*protocol_name) {
if protocol.send_response(inner_channel, Ok(payload)).is_err() {
// Note: Failure is handled further below when receiving
// `InboundFailure` event from `RequestResponse` behaviour.
// `InboundFailure` event from request-response [`Behaviour`].
log::debug!(
target: "sub-libp2p",
"Failed to send response for {:?} on protocol {:?} due to a \
@@ -690,9 +689,10 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
}
if !reputation_changes.is_empty() {
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(
Event::ReputationChanges { peer, changes: reputation_changes },
))
return Poll::Ready(ToSwarm::GenerateEvent(Event::ReputationChanges {
peer,
changes: reputation_changes,
}))
}
}
@@ -701,44 +701,35 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
while let Poll::Ready(ev) = behaviour.poll(cx, params) {
let ev = match ev {
// Main events we are interested in.
NetworkBehaviourAction::GenerateEvent(ev) => ev,
ToSwarm::GenerateEvent(ev) => ev,
// Other events generated by the underlying behaviour are transparently
// passed through.
NetworkBehaviourAction::Dial { opts, handler } => {
ToSwarm::Dial { opts } => {
if opts.get_peer_id().is_none() {
log::error!(
"The request-response isn't supposed to start dialing addresses"
);
}
let protocol = protocol.to_string();
let handler = self.new_handler_with_replacement(protocol, handler);
return Poll::Ready(NetworkBehaviourAction::Dial { opts, handler })
return Poll::Ready(ToSwarm::Dial { opts })
},
NetworkBehaviourAction::NotifyHandler { peer_id, handler, event } =>
return Poll::Ready(NetworkBehaviourAction::NotifyHandler {
ToSwarm::NotifyHandler { peer_id, handler, event } =>
return Poll::Ready(ToSwarm::NotifyHandler {
peer_id,
handler,
event: ((*protocol).to_string(), event),
}),
NetworkBehaviourAction::ReportObservedAddr { address, score } =>
return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr {
address,
score,
}),
NetworkBehaviourAction::CloseConnection { peer_id, connection } =>
return Poll::Ready(NetworkBehaviourAction::CloseConnection {
peer_id,
connection,
}),
ToSwarm::ReportObservedAddr { address, score } =>
return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }),
ToSwarm::CloseConnection { peer_id, connection } =>
return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }),
};
match ev {
// Received a request from a remote.
RequestResponseEvent::Message {
request_response::Event::Message {
peer,
message:
RequestResponseMessage::Request { request_id, request, channel, .. },
message: Message::Request { request_id, request, channel, .. },
} => {
self.pending_responses_arrival_time
.insert((protocol.clone(), request_id).into(), Instant::now());
@@ -765,9 +756,9 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
},
// Received a response from a remote to one of our requests.
RequestResponseEvent::Message {
request_response::Event::Message {
peer,
message: RequestResponseMessage::Response { request_id, response },
message: Message::Response { request_id, response },
..
} => {
let (started, delivered) = match self
@@ -798,12 +789,15 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
result: delivered,
};
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(out))
return Poll::Ready(ToSwarm::GenerateEvent(out))
},
// One of our requests has failed.
RequestResponseEvent::OutboundFailure {
peer, request_id, error, ..
request_response::Event::OutboundFailure {
peer,
request_id,
error,
..
} => {
let started = match self
.pending_requests
@@ -841,12 +835,12 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
result: Err(RequestFailure::Network(error)),
};
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(out))
return Poll::Ready(ToSwarm::GenerateEvent(out))
},
// An inbound request failed, either while reading the request or due to
// failing to send a response.
RequestResponseEvent::InboundFailure {
request_response::Event::InboundFailure {
request_id, peer, error, ..
} => {
self.pending_responses_arrival_time
@@ -857,11 +851,11 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
protocol: protocol.clone(),
result: Err(ResponseFailure::Network(error)),
};
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(out))
return Poll::Ready(ToSwarm::GenerateEvent(out))
},
// A response to an inbound request has been sent.
RequestResponseEvent::ResponseSent { request_id, peer } => {
request_response::Event::ResponseSent { request_id, peer } => {
let arrival_time = self
.pending_responses_arrival_time
.remove(&(protocol.clone(), request_id).into())
@@ -886,7 +880,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
result: Ok(arrival_time),
};
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(out))
return Poll::Ready(ToSwarm::GenerateEvent(out))
},
};
}
@@ -913,7 +907,7 @@ pub enum ResponseFailure {
Network(InboundFailure),
}
/// Implements the libp2p [`RequestResponseCodec`] trait. Defines how streams of bytes are turned
/// Implements the libp2p [`Codec`] trait. Defines how streams of bytes are turned
/// into requests and responses and vice-versa.
#[derive(Debug, Clone)]
#[doc(hidden)] // Needs to be public in order to satisfy the Rust compiler.
@@ -923,7 +917,7 @@ pub struct GenericCodec {
}
#[async_trait::async_trait]
impl RequestResponseCodec for GenericCodec {
impl Codec for GenericCodec {
type Protocol = Vec<u8>;
type Request = Vec<u8>;
type Response = Result<Vec<u8>, ()>;
@@ -1054,7 +1048,7 @@ mod tests {
},
identity::Keypair,
noise,
swarm::{Executor, Swarm, SwarmEvent},
swarm::{Executor, Swarm, SwarmBuilder, SwarmEvent},
Multiaddr,
};
use sc_peerset::{Peerset, PeersetConfig, SetConfig};
@@ -1072,13 +1066,10 @@ mod tests {
) -> (Swarm<RequestResponsesBehaviour>, Multiaddr, Peerset) {
let keypair = Keypair::generate_ed25519();
let noise_keys =
noise::Keypair::<noise::X25519Spec>::new().into_authentic(&keypair).unwrap();
let transport = MemoryTransport::new()
.upgrade(upgrade::Version::V1)
.authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated())
.multiplex(libp2p::yamux::YamuxConfig::default())
.authenticate(noise::Config::new(&keypair).unwrap())
.multiplex(libp2p::yamux::Config::default())
.boxed();
let config = PeersetConfig {
@@ -1096,12 +1087,13 @@ mod tests {
let behaviour = RequestResponsesBehaviour::new(list, handle).unwrap();
let runtime = tokio::runtime::Runtime::new().unwrap();
let mut swarm = Swarm::with_executor(
let mut swarm = SwarmBuilder::with_executor(
transport,
behaviour,
keypair.public().to_peer_id(),
TokioExecutor(runtime),
);
)
.build();
let listen_addr: Multiaddr = format!("/memory/{}", rand::random::<u64>()).parse().unwrap();
swarm.listen_on(listen_addr.clone()).unwrap();
@@ -1117,7 +1109,7 @@ mod tests {
let protocol_name = "/test/req-resp/1";
let mut pool = LocalPool::new();
// Build swarms whose behaviour is `RequestResponsesBehaviour`.
// Build swarms whose behaviour is [`RequestResponsesBehaviour`].
let mut swarms = (0..2)
.map(|_| {
let (tx, mut rx) = mpsc::channel::<IncomingRequest>(64);
@@ -1220,7 +1212,7 @@ mod tests {
let protocol_name = "/test/req-resp/1";
let mut pool = LocalPool::new();
// Build swarms whose behaviour is `RequestResponsesBehaviour`.
// Build swarms whose behaviour is [`RequestResponsesBehaviour`].
let mut swarms = (0..2)
.map(|_| {
let (tx, mut rx) = mpsc::channel::<IncomingRequest>(64);
@@ -1322,10 +1314,10 @@ mod tests {
}
/// A [`RequestId`] is a unique identifier among either all inbound or all outbound requests for
/// a single [`RequestResponse`] behaviour. It is not guaranteed to be unique across multiple
/// [`RequestResponse`] behaviours. Thus when handling [`RequestId`] in the context of multiple
/// [`RequestResponse`] behaviours, one needs to couple the protocol name with the [`RequestId`]
/// to get a unique request identifier.
/// a single [`RequestResponsesBehaviour`] behaviour. It is not guaranteed to be unique across
/// multiple [`RequestResponsesBehaviour`] behaviours. Thus when handling [`RequestId`] in the
/// context of multiple [`RequestResponsesBehaviour`] behaviours, one needs to couple the
/// protocol name with the [`RequestId`] to get a unique request identifier.
///
/// This test ensures that two requests on different protocols can be handled concurrently
/// without a [`RequestId`] collision.