From 9c694dc4bacbdeb2d665fe11087f62a460a9d28b Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 31 May 2023 16:54:45 +0300 Subject: [PATCH 01/26] response_request::Codec implementation --- Cargo.lock | 3 + protocols/rendezvous/Cargo.toml | 5 +- protocols/rendezvous/src/codec.rs | 191 ++++++++++++++++++- protocols/rendezvous/src/handler.rs | 3 - protocols/rendezvous/src/handler/inbound.rs | 2 +- protocols/rendezvous/src/handler/outbound.rs | 2 +- protocols/rendezvous/src/lib.rs | 8 + 7 files changed, 198 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c39664be258..3d7b8890728 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2957,12 +2957,14 @@ dependencies = [ name = "libp2p-rendezvous" version = "0.13.0" dependencies = [ + "async-std", "async-trait", "asynchronous-codec", "bimap", "env_logger 0.10.0", "futures", "futures-timer", + "futures_ringbuf", "instant", "libp2p-core", "libp2p-identify", @@ -2970,6 +2972,7 @@ dependencies = [ "libp2p-mplex", "libp2p-noise", "libp2p-ping", + "libp2p-request-response", "libp2p-swarm", "libp2p-swarm-test", "libp2p-tcp", diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 7e421a5b17a..82d22910541 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -12,6 +12,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] asynchronous-codec = "0.6" +async-trait = "0.1" bimap = "0.6.3" futures = { version = "0.3", default-features = false, features = ["std"] } futures-timer = "3.0.2" @@ -19,6 +20,7 @@ instant = "0.1.12" libp2p-core = { workspace = true } libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } +libp2p-request-response = { workspace = true } log = "0.4" quick-protobuf = "0.8" quick-protobuf-codec = { workspace = true } @@ -27,7 +29,6 @@ thiserror = "1" void = "1" [dev-dependencies] -async-trait = "0.1" env_logger = "0.10.0" libp2p-swarm = { workspace = true, features = ["macros", "tokio"] } libp2p-mplex = { workspace = true } @@ -39,6 +40,8 @@ libp2p-tcp = { workspace = true, features = ["tokio"] } rand = "0.8" tokio = { version = "1.28", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } libp2p-swarm-test = { workspace = true } +futures_ringbuf = "0.3.1" +async-std = { version = "1.6.2", features = ["attributes"] } # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 716ad79893f..9523297a679 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -23,12 +23,14 @@ use asynchronous_codec::{BytesMut, Decoder, Encoder}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; use rand::RngCore; use std::convert::{TryFrom, TryInto}; -use std::fmt; +use std::{fmt, io}; pub type Ttl = u64; +const MAX_MESSAGE_LEN_BYTES: usize = 1024 * 1024; + #[allow(clippy::large_enum_variant)] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Message { Register(NewRegistration), RegisterResponse(Result), @@ -49,7 +51,7 @@ impl Namespace { /// /// This will panic if the namespace is too long. We accepting panicking in this case because we are enforcing a `static lifetime which means this value can only be a constant in the program and hence we hope the developer checked that it is of an acceptable length. pub fn from_static(value: &'static str) -> Self { - if value.len() > 255 { + if value.len() > crate::MAX_NAMESPACE { panic!("Namespace '{value}' is too long!") } @@ -57,7 +59,7 @@ impl Namespace { } pub fn new(value: String) -> Result { - if value.len() > 255 { + if value.len() > crate::MAX_NAMESPACE { return Err(NamespaceTooLong); } @@ -160,7 +162,7 @@ impl Cookie { #[error("The cookie was malformed")] pub struct InvalidCookie; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct NewRegistration { pub namespace: Namespace, pub record: PeerRecord, @@ -206,7 +208,7 @@ pub struct RendezvousCodec { impl Default for RendezvousCodec { fn default() -> Self { Self { - inner: quick_protobuf_codec::Codec::new(1024 * 1024), // 1MB + inner: quick_protobuf_codec::Codec::new(MAX_MESSAGE_LEN_BYTES), // 1MB } } } @@ -236,6 +238,165 @@ impl Decoder for RendezvousCodec { } } +pub mod request_response { + use crate::codec::{Message, RendezvousCodec}; + use async_trait::async_trait; + use asynchronous_codec::{FramedRead, FramedWrite}; + use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; + use libp2p_swarm::StreamProtocol; + use std::io; + + #[derive(Clone)] + pub struct Codec {} + + impl Default for Codec { + fn default() -> Self { + Codec {} + } + } + + #[async_trait] + impl libp2p_request_response::Codec for Codec { + type Protocol = StreamProtocol; + type Request = Message; + type Response = Message; + + async fn read_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let mut frame = FramedRead::new(io, RendezvousCodec::default()); + if let Some(result) = frame.next().await { + return Ok(result?); + } + + Err(io::ErrorKind::InvalidInput.into()) + } + + async fn read_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let mut frame = FramedRead::new(io, RendezvousCodec::default()); + if let Some(result) = frame.next().await { + return Ok(result?); + } + + Err(io::ErrorKind::InvalidInput.into()) + } + + async fn write_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let mut framer = FramedWrite::new(io, RendezvousCodec::default()); + framer.send(req).await?; + + Ok(()) + } + + async fn write_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + res: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let mut framer = FramedWrite::new(io, RendezvousCodec::default()); + framer.send(res).await?; + + Ok(()) + } + } + + #[cfg(test)] + mod tests { + use crate::codec::{Message, NewRegistration}; + use crate::Namespace; + use async_std; + use futures::AsyncWriteExt; + use futures_ringbuf::Endpoint; + use libp2p_core::PeerRecord; + use libp2p_identity; + use libp2p_request_response::Codec; + + #[async_std::test] + async fn codec_test() { + let identity = libp2p_identity::Keypair::generate_ed25519(); + + let exp_msg = Message::Register(NewRegistration { + namespace: Namespace::from_static("wonderland"), + record: PeerRecord::new( + &identity, + vec!["/ip4/127.0.0.1/tcp/1234".parse().unwrap()], + ) + .unwrap(), + ttl: None, + }); + + let mut codec = super::Codec {}; + let protocol = &crate::PROTOCOL_IDENT; + + // Write/read request + + let (mut a, mut b) = Endpoint::pair( + crate::codec::MAX_MESSAGE_LEN_BYTES, + crate::codec::MAX_MESSAGE_LEN_BYTES, + ); + + codec + .write_request(protocol, &mut a, exp_msg.clone()) + .await + .expect("Should write"); + a.close().await.unwrap(); + + let act_msg = codec + .read_request(protocol, &mut b) + .await + .expect("Should read"); + b.close().await.unwrap(); + + assert_eq!(exp_msg, act_msg); + + // Write/read response + + let (mut a, mut b) = Endpoint::pair( + crate::codec::MAX_MESSAGE_LEN_BYTES, + crate::codec::MAX_MESSAGE_LEN_BYTES, + ); + + codec + .write_response(protocol, &mut a, exp_msg.clone()) + .await + .expect("Should write"); + a.close().await.unwrap(); + + let act_msg = codec + .read_response(protocol, &mut b) + .await + .expect("Should read"); + b.close().await.unwrap(); + + assert_eq!(exp_msg, act_msg); + } + } +} + #[derive(Debug, thiserror::Error)] pub enum Error { #[error(transparent)] @@ -246,14 +407,24 @@ pub enum Error { Conversion(#[from] ConversionError), } +impl From for std::io::Error { + fn from(value: Error) -> Self { + match value { + Error::Io(e) => e, + Error::Codec(e) => io::Error::from(e), + Error::Conversion(e) => io::Error::new(io::ErrorKind::InvalidInput, e), + } + } +} + impl From for proto::Message { fn from(message: Message) -> Self { match message { Message::Register(NewRegistration { - namespace, - record, - ttl, - }) => proto::Message { + namespace, + record, + ttl, + }) => proto::Message { type_pb: Some(proto::MessageType::REGISTER), register: Some(proto::Register { ns: Some(namespace.into()), diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index ccf765c9c65..2d363f0a2ad 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -20,11 +20,8 @@ use crate::codec; use crate::codec::Message; -use libp2p_swarm::StreamProtocol; use void::Void; -const PROTOCOL_IDENT: StreamProtocol = StreamProtocol::new("/rendezvous/1.0.0"); - pub(crate) mod inbound; pub(crate) mod outbound; /// Errors that can occur while interacting with a substream. diff --git a/protocols/rendezvous/src/handler/inbound.rs b/protocols/rendezvous/src/handler/inbound.rs index bf0083780c5..d02df33a21b 100644 --- a/protocols/rendezvous/src/handler/inbound.rs +++ b/protocols/rendezvous/src/handler/inbound.rs @@ -22,8 +22,8 @@ use crate::codec::{ Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, RendezvousCodec, Ttl, }; use crate::handler::Error; -use crate::handler::PROTOCOL_IDENT; use crate::substream_handler::{Next, PassthroughProtocol, SubstreamHandler}; +use crate::PROTOCOL_IDENT; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; use libp2p_swarm::SubstreamProtocol; diff --git a/protocols/rendezvous/src/handler/outbound.rs b/protocols/rendezvous/src/handler/outbound.rs index dd44bf8c2b4..7dfc1723f2a 100644 --- a/protocols/rendezvous/src/handler/outbound.rs +++ b/protocols/rendezvous/src/handler/outbound.rs @@ -20,8 +20,8 @@ use crate::codec::{Cookie, Message, NewRegistration, RendezvousCodec}; use crate::handler::Error; -use crate::handler::PROTOCOL_IDENT; use crate::substream_handler::{FutureSubstream, Next, PassthroughProtocol, SubstreamHandler}; +use crate::PROTOCOL_IDENT; use crate::{ErrorCode, Namespace, Registration, Ttl}; use asynchronous_codec::Framed; use futures::{SinkExt, TryFutureExt, TryStreamExt}; diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 337e554ea00..7ad96201364 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -23,6 +23,7 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub use self::codec::{Cookie, ErrorCode, Namespace, NamespaceTooLong, Registration, Ttl}; +use libp2p_swarm::StreamProtocol; mod codec; mod handler; @@ -43,5 +44,12 @@ pub const MIN_TTL: Ttl = 60 * 60 * 2; /// . pub const MAX_TTL: Ttl = 60 * 60 * 72; +/// The maximum namespace length. +/// +/// . +pub const MAX_NAMESPACE: usize = 255; + +pub const PROTOCOL_IDENT: StreamProtocol = StreamProtocol::new("/rendezvous/1.0.0"); + pub mod client; pub mod server; From 9da802fb751e55ded1f7757e9f39d4d312d0ba3d Mon Sep 17 00:00:00 2001 From: dgarus Date: Thu, 8 Jun 2023 16:40:27 +0300 Subject: [PATCH 02/26] Rendezvous based on request_response behaviour --- protocols/rendezvous/src/client.rs | 537 +++++++++-------- protocols/rendezvous/src/codec.rs | 287 +++++---- protocols/rendezvous/src/handler.rs | 47 -- protocols/rendezvous/src/handler/inbound.rs | 192 ------ protocols/rendezvous/src/handler/outbound.rs | 134 ----- protocols/rendezvous/src/lib.rs | 4 +- protocols/rendezvous/src/server.rs | 304 +++++----- protocols/rendezvous/src/substream_handler.rs | 556 ------------------ 8 files changed, 572 insertions(+), 1489 deletions(-) delete mode 100644 protocols/rendezvous/src/handler.rs delete mode 100644 protocols/rendezvous/src/handler/inbound.rs delete mode 100644 protocols/rendezvous/src/handler/outbound.rs delete mode 100644 protocols/rendezvous/src/substream_handler.rs diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index d1a514f1820..3d9f89bb620 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -18,32 +18,74 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::codec::{Cookie, ErrorCode, Namespace, NewRegistration, Registration, Ttl}; -use crate::handler; -use crate::handler::outbound; -use crate::handler::outbound::OpenInfo; -use crate::substream_handler::{InEvent, SubstreamConnectionHandler}; +use crate::codec::Message::*; +use crate::codec::{Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl}; use futures::future::BoxFuture; use futures::future::FutureExt; use futures::stream::FuturesUnordered; use futures::stream::StreamExt; -use instant::Duration; use libp2p_core::{Endpoint, Multiaddr, PeerRecord}; use libp2p_identity::{Keypair, PeerId, SigningError}; -use libp2p_swarm::behaviour::FromSwarm; +use libp2p_request_response::{ProtocolSupport, RequestId}; use libp2p_swarm::{ - CloseConnection, ConnectionDenied, ConnectionId, ExternalAddresses, NetworkBehaviour, - NotifyHandler, PollParameters, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, + ConnectionDenied, ConnectionId, ExternalAddresses, FromSwarm, NetworkBehaviour, PollParameters, + THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, }; use std::collections::{HashMap, VecDeque}; -use std::iter::FromIterator; +use std::iter; use std::task::{Context, Poll}; -use void::Void; +use std::time::Duration; + +#[derive(Debug, thiserror::Error)] +pub enum RegisterError { + #[error("We don't know about any externally reachable addresses of ours")] + NoExternalAddresses, + #[error("Failed to make a new PeerRecord")] + FailedToMakeRecord(#[from] SigningError), + #[error("Failed to register with Rendezvous node")] + Remote { + rendezvous_node: PeerId, + namespace: Namespace, + error: ErrorCode, + }, +} + +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +pub enum Event { + /// We successfully discovered other nodes with using the contained rendezvous node. + Discovered { + rendezvous_node: PeerId, + registrations: Vec, + cookie: Cookie, + }, + /// We failed to discover other nodes on the contained rendezvous node. + DiscoverFailed { + rendezvous_node: PeerId, + namespace: Option, + error: ErrorCode, + }, + /// We successfully registered with the contained rendezvous node. + Registered { + rendezvous_node: PeerId, + ttl: Ttl, + namespace: Namespace, + }, + /// We failed to register with the contained rendezvous node. + RegisterFailed(RegisterError), + /// The connection details we learned from this node expired. + Expired { peer: PeerId }, +} pub struct Behaviour { - events: VecDeque>>, + inner: libp2p_request_response::Behaviour, + keypair: Keypair, - pending_register_requests: Vec<(Namespace, PeerId, Option)>, + + error_events: VecDeque, + + waiting_for_register: HashMap, + waiting_for_discovery: HashMap)>, /// Hold addresses of all peers that we have discovered so far. /// @@ -60,9 +102,15 @@ impl Behaviour { /// Create a new instance of the rendezvous [`NetworkBehaviour`]. pub fn new(keypair: Keypair) -> Self { Self { - events: Default::default(), + inner: libp2p_request_response::Behaviour::with_codec( + crate::codec::Codec::default(), + iter::once((crate::PROTOCOL_IDENT, ProtocolSupport::Full)), + libp2p_request_response::Config::default(), + ), + error_events: Default::default(), keypair, - pending_register_requests: vec![], + waiting_for_register: Default::default(), + waiting_for_discovery: Default::default(), discovered_peers: Default::default(), expiring_registrations: FuturesUnordered::from_iter(vec![ futures::future::pending().boxed() @@ -76,19 +124,38 @@ impl Behaviour { /// External addresses are either manually added via [`libp2p_swarm::Swarm::add_external_address`] or reported /// by other [`NetworkBehaviour`]s via [`ToSwarm::ExternalAddrConfirmed`]. pub fn register(&mut self, namespace: Namespace, rendezvous_node: PeerId, ttl: Option) { - self.pending_register_requests - .push((namespace, rendezvous_node, ttl)); + let external_addresses = self.external_addresses.iter().cloned().collect::>(); + if external_addresses.is_empty() { + self.error_events + .push_back(Event::RegisterFailed(RegisterError::NoExternalAddresses)); + + return; + } + + let opt_req_id = match PeerRecord::new(&self.keypair, external_addresses) { + Ok(peer_record) => Some(self.inner.send_request( + &rendezvous_node, + Register(NewRegistration::new(namespace.clone(), peer_record, ttl)), + )), + Err(signing_error) => { + self.error_events.push_back(Event::RegisterFailed( + RegisterError::FailedToMakeRecord(signing_error), + )); + + None + } + }; + + if let Some(req_id) = opt_req_id { + self.waiting_for_register + .insert(req_id, (rendezvous_node, namespace)); + } } /// Unregister ourselves from the given namespace with the given rendezvous peer. pub fn unregister(&mut self, namespace: Namespace, rendezvous_node: PeerId) { - self.events.push_back(ToSwarm::NotifyHandler { - peer_id: rendezvous_node, - event: handler::OutboundInEvent::NewSubstream { - open_info: OpenInfo::UnregisterRequest(namespace), - }, - handler: NotifyHandler::Any, - }); + self.inner + .send_request(&rendezvous_node, Unregister(namespace)); } /// Discover other peers at a given rendezvous peer. @@ -100,277 +167,249 @@ impl Behaviour { /// the cookie was acquired. pub fn discover( &mut self, - ns: Option, + namespace: Option, cookie: Option, limit: Option, rendezvous_node: PeerId, ) { - self.events.push_back(ToSwarm::NotifyHandler { - peer_id: rendezvous_node, - event: handler::OutboundInEvent::NewSubstream { - open_info: OpenInfo::DiscoverRequest { - namespace: ns, - cookie, - limit, - }, + let req_id = self.inner.send_request( + &rendezvous_node, + Discover { + namespace: namespace.clone(), + cookie: cookie.clone(), + limit, }, - handler: NotifyHandler::Any, - }); - } -} + ); -#[derive(Debug, thiserror::Error)] -pub enum RegisterError { - #[error("We don't know about any externally reachable addresses of ours")] - NoExternalAddresses, - #[error("Failed to make a new PeerRecord")] - FailedToMakeRecord(#[from] SigningError), - #[error("Failed to register with Rendezvous node")] - Remote { - rendezvous_node: PeerId, - namespace: Namespace, - error: ErrorCode, - }, -} - -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -pub enum Event { - /// We successfully discovered other nodes with using the contained rendezvous node. - Discovered { - rendezvous_node: PeerId, - registrations: Vec, - cookie: Cookie, - }, - /// We failed to discover other nodes on the contained rendezvous node. - DiscoverFailed { - rendezvous_node: PeerId, - namespace: Option, - error: ErrorCode, - }, - /// We successfully registered with the contained rendezvous node. - Registered { - rendezvous_node: PeerId, - ttl: Ttl, - namespace: Namespace, - }, - /// We failed to register with the contained rendezvous node. - RegisterFailed(RegisterError), - /// The connection details we learned from this node expired. - Expired { peer: PeerId }, + self.waiting_for_discovery + .insert(req_id, (rendezvous_node, namespace)); + } } impl NetworkBehaviour for Behaviour { - type ConnectionHandler = - SubstreamConnectionHandler; + type ConnectionHandler = as NetworkBehaviour>::ConnectionHandler; + type ToSwarm = Event; fn handle_established_inbound_connection( - &mut self, - _: ConnectionId, - _: PeerId, - _: &Multiaddr, - _: &Multiaddr, - ) -> Result, ConnectionDenied> { - Ok(SubstreamConnectionHandler::new_outbound_only( - Duration::from_secs(30), - )) - } - - fn handle_pending_outbound_connection( &mut self, _connection_id: ConnectionId, - maybe_peer: Option, - _addresses: &[Multiaddr], - _effective_role: Endpoint, - ) -> Result, ConnectionDenied> { - let peer = match maybe_peer { - None => return Ok(vec![]), - Some(peer) => peer, - }; - - let addresses = self - .discovered_peers - .iter() - .filter_map(|((candidate, _), addresses)| (candidate == &peer).then_some(addresses)) - .flatten() - .cloned() - .collect(); - - Ok(addresses) + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + self.inner.handle_established_inbound_connection( + _connection_id, + peer, + local_addr, + remote_addr, + ) } fn handle_established_outbound_connection( &mut self, - _: ConnectionId, - _: PeerId, - _: &Multiaddr, - _: Endpoint, + _connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, ) -> Result, ConnectionDenied> { - Ok(SubstreamConnectionHandler::new_outbound_only( - Duration::from_secs(30), - )) + self.inner + .handle_established_outbound_connection(_connection_id, peer, addr, role_override) } fn on_connection_handler_event( &mut self, - peer_id: PeerId, - connection_id: ConnectionId, - event: THandlerOutEvent, + _peer_id: PeerId, + _connection_id: ConnectionId, + _event: THandlerOutEvent, ) { - let new_events = match event { - handler::OutboundOutEvent::InboundEvent { message, .. } => void::unreachable(message), - handler::OutboundOutEvent::OutboundEvent { message, .. } => handle_outbound_event( - message, - peer_id, - &mut self.discovered_peers, - &mut self.expiring_registrations, - ), - handler::OutboundOutEvent::InboundError { error, .. } => void::unreachable(error), - handler::OutboundOutEvent::OutboundError { error, .. } => { - log::warn!("Connection with peer {} failed: {}", peer_id, error); - - vec![ToSwarm::CloseConnection { - peer_id, - connection: CloseConnection::One(connection_id), - }] - } - }; + self.inner + .on_connection_handler_event(_peer_id, _connection_id, _event); + } - self.events.extend(new_events); + fn on_swarm_event(&mut self, event: FromSwarm) { + self.external_addresses.on_swarm_event(&event); + + self.inner.on_swarm_event(event); } fn poll( &mut self, cx: &mut Context<'_>, - _: &mut impl PollParameters, + params: &mut impl PollParameters, ) -> Poll>> { - if let Some(event) = self.events.pop_front() { - return Poll::Ready(event); + if let Some(event) = self.error_events.pop_front() { + return Poll::Ready(ToSwarm::GenerateEvent(event)); } - if let Some((namespace, rendezvous_node, ttl)) = self.pending_register_requests.pop() { - // Update our external addresses based on the Swarm's current knowledge. - // It doesn't make sense to register addresses on which we are not reachable, hence this should not be configurable from the outside. + let poll_res = self.inner.poll(cx, params); - let external_addresses = self.external_addresses.iter().cloned().collect::>(); - - if external_addresses.is_empty() { - return Poll::Ready(ToSwarm::GenerateEvent(Event::RegisterFailed( - RegisterError::NoExternalAddresses, - ))); - } + if let Poll::Ready(to_swarm) = &poll_res { + if let ToSwarm::GenerateEvent(event) = to_swarm { + if let libp2p_request_response::Event::Message { + peer: _, + message: libp2p_request_response::Message::Request { .. }, + } = event + { + return Poll::Pending; + } - let action = match PeerRecord::new(&self.keypair, external_addresses) { - Ok(peer_record) => ToSwarm::NotifyHandler { - peer_id: rendezvous_node, - event: handler::OutboundInEvent::NewSubstream { - open_info: OpenInfo::RegisterRequest(NewRegistration { - namespace, - record: peer_record, - ttl, - }), - }, - handler: NotifyHandler::Any, - }, - Err(signing_error) => ToSwarm::GenerateEvent(Event::RegisterFailed( - RegisterError::FailedToMakeRecord(signing_error), - )), + if let libp2p_request_response::Event::InboundFailure { .. } = event { + return Poll::Pending; + } }; - - return Poll::Ready(action); } - if let Some(expired_registration) = - futures::ready!(self.expiring_registrations.poll_next_unpin(cx)) - { - self.discovered_peers.remove(&expired_registration); - return Poll::Ready(ToSwarm::GenerateEvent(Event::Expired { - peer: expired_registration.0, - })); + let result = poll_res.map(|to_swarm| { + to_swarm.map_out(|event| match event { + libp2p_request_response::Event::Message { + peer: _, + message: + libp2p_request_response::Message::Response { + request_id, + response, + }, + } => self.handle_response(&request_id, response), + libp2p_request_response::Event::OutboundFailure { + peer: _, + request_id, + error: _, + } => { + let (rendezvous_node, namespace) = self + .waiting_for_register + .remove(&request_id) + .expect(format!("unknown request_id: {request_id}").as_str()); + Event::RegisterFailed(RegisterError::Remote { + rendezvous_node, + namespace, + error: ErrorCode::Unavailable, + }) + } + libp2p_request_response::Event::InboundFailure { .. } => { + unreachable!() + } + libp2p_request_response::Event::ResponseSent { .. } => { + unreachable!() + } + libp2p_request_response::Event::Message { + peer: _, + message: libp2p_request_response::Message::Request { .. }, + } => { + unreachable!() + } + }) + }); + + if let Poll::Pending = &result { + if let Some(expired_registration) = + futures::ready!(self.expiring_registrations.poll_next_unpin(cx)) + { + self.discovered_peers.remove(&expired_registration); + return Poll::Ready(ToSwarm::GenerateEvent(Event::Expired { + peer: expired_registration.0, + })); + } } - Poll::Pending + result } - fn on_swarm_event(&mut self, event: FromSwarm) { - self.external_addresses.on_swarm_event(&event); + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + let peer = match maybe_peer { + None => return Ok(vec![]), + Some(peer) => peer, + }; - match event { - FromSwarm::ConnectionEstablished(_) - | FromSwarm::ConnectionClosed(_) - | FromSwarm::AddressChange(_) - | FromSwarm::DialFailure(_) - | FromSwarm::ListenFailure(_) - | FromSwarm::NewListener(_) - | FromSwarm::NewListenAddr(_) - | FromSwarm::ExpiredListenAddr(_) - | FromSwarm::ListenerError(_) - | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddrCandidate(_) - | FromSwarm::ExternalAddrExpired(_) - | FromSwarm::ExternalAddrConfirmed(_) => {} - } + let addresses = self + .discovered_peers + .iter() + .filter_map(|((candidate, _), addresses)| (candidate == &peer).then_some(addresses)) + .flatten() + .cloned() + .collect(); + + Ok(addresses) } } -fn handle_outbound_event( - event: outbound::OutEvent, - peer_id: PeerId, - discovered_peers: &mut HashMap<(PeerId, Namespace), Vec>, - expiring_registrations: &mut FuturesUnordered>, -) -> Vec>> { - match event { - outbound::OutEvent::Registered { namespace, ttl } => { - vec![ToSwarm::GenerateEvent(Event::Registered { - rendezvous_node: peer_id, - ttl, - namespace, - })] - } - outbound::OutEvent::RegisterFailed(namespace, error) => { - vec![ToSwarm::GenerateEvent(Event::RegisterFailed( - RegisterError::Remote { - rendezvous_node: peer_id, - namespace, - error, - }, - ))] - } - outbound::OutEvent::Discovered { - registrations, - cookie, - } => { - discovered_peers.extend(registrations.iter().map(|registration| { - let peer_id = registration.record.peer_id(); - let namespace = registration.namespace.clone(); - - let addresses = registration.record.addresses().to_vec(); - - ((peer_id, namespace), addresses) - })); - expiring_registrations.extend(registrations.iter().cloned().map(|registration| { - async move { - // if the timer errors we consider it expired - futures_timer::Delay::new(Duration::from_secs(registration.ttl)).await; - - (registration.record.peer_id(), registration.namespace) - } - .boxed() - })); - - vec![ToSwarm::GenerateEvent(Event::Discovered { - rendezvous_node: peer_id, - registrations, - cookie, - })] - } - outbound::OutEvent::DiscoverFailed { namespace, error } => { - vec![ToSwarm::GenerateEvent(Event::DiscoverFailed { - rendezvous_node: peer_id, - namespace, - error, - })] +impl Behaviour { + fn handle_response(&mut self, request_id: &RequestId, response: Message) -> Event { + match response { + RegisterResponse(result) => { + let (rendezvous_node, namespace) = self + .waiting_for_register + .remove(&request_id) + .expect(format!("unknown request_id: {request_id}").as_str()); + let res = match result { + Ok(ttl) => Event::Registered { + rendezvous_node, + ttl, + namespace, + }, + Err(error_code) => Event::RegisterFailed(RegisterError::Remote { + rendezvous_node, + namespace, + error: error_code.clone(), + }), + }; + + res + } + DiscoverResponse(response) => { + let (rendezvous_node, ns) = self + .waiting_for_discovery + .remove(&request_id) + .expect(format!("unknown request_id: {request_id}").as_str()); + let res = match response { + Ok((registrations, cookie)) => { + self.discovered_peers + .extend(registrations.iter().map(|registration| { + let peer_id = registration.record.peer_id(); + let namespace = registration.namespace.clone(); + + let addresses = registration.record.addresses().to_vec(); + + ((peer_id, namespace), addresses) + })); + + self.expiring_registrations + .extend(registrations.iter().cloned().map(|registration| { + async move { + // if the timer errors we consider it expired + futures_timer::Delay::new(Duration::from_secs( + registration.ttl, + )) + .await; + + (registration.record.peer_id(), registration.namespace) + } + .boxed() + })); + + Event::Discovered { + rendezvous_node, + registrations, + cookie, + } + } + Err(error_code) => Event::DiscoverFailed { + rendezvous_node, + namespace: ns, + error: error_code, + }, + }; + + res + } + _ => unreachable!(), } } } diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 9523297a679..4037bc0f5a0 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -19,13 +19,18 @@ // DEALINGS IN THE SOFTWARE. use crate::DEFAULT_TTL; +use async_trait::async_trait; use asynchronous_codec::{BytesMut, Decoder, Encoder}; +use asynchronous_codec::{FramedRead, FramedWrite}; +use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; +use libp2p_swarm::StreamProtocol; use rand::RngCore; use std::convert::{TryFrom, TryInto}; use std::{fmt, io}; pub type Ttl = u64; +pub type Limit = u64; const MAX_MESSAGE_LEN_BYTES: usize = 1024 * 1024; @@ -38,7 +43,7 @@ pub enum Message { Discover { namespace: Option, cookie: Option, - limit: Option, + limit: Option, }, DiscoverResponse(Result<(Vec, Cookie), ErrorCode>), } @@ -238,162 +243,77 @@ impl Decoder for RendezvousCodec { } } -pub mod request_response { - use crate::codec::{Message, RendezvousCodec}; - use async_trait::async_trait; - use asynchronous_codec::{FramedRead, FramedWrite}; - use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; - use libp2p_swarm::StreamProtocol; - use std::io; +#[derive(Clone)] +pub struct Codec {} - #[derive(Clone)] - pub struct Codec {} - - impl Default for Codec { - fn default() -> Self { - Codec {} - } +impl Default for Codec { + fn default() -> Self { + Codec {} } +} - #[async_trait] - impl libp2p_request_response::Codec for Codec { - type Protocol = StreamProtocol; - type Request = Message; - type Response = Message; - - async fn read_request( - &mut self, - _: &Self::Protocol, - io: &mut T, - ) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let mut frame = FramedRead::new(io, RendezvousCodec::default()); - if let Some(result) = frame.next().await { - return Ok(result?); - } - - Err(io::ErrorKind::InvalidInput.into()) +#[async_trait] +impl libp2p_request_response::Codec for Codec { + type Protocol = StreamProtocol; + type Request = Message; + type Response = Message; + + async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let mut frame = FramedRead::new(io, RendezvousCodec::default()); + if let Some(result) = frame.next().await { + return Ok(result?); } - async fn read_response( - &mut self, - _: &Self::Protocol, - io: &mut T, - ) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let mut frame = FramedRead::new(io, RendezvousCodec::default()); - if let Some(result) = frame.next().await { - return Ok(result?); - } + Err(io::ErrorKind::InvalidInput.into()) + } - Err(io::ErrorKind::InvalidInput.into()) + async fn read_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let mut frame = FramedRead::new(io, RendezvousCodec::default()); + if let Some(result) = frame.next().await { + return Ok(result?); } - async fn write_request( - &mut self, - _: &Self::Protocol, - io: &mut T, - req: Self::Request, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - let mut framer = FramedWrite::new(io, RendezvousCodec::default()); - framer.send(req).await?; - - Ok(()) - } + Err(io::ErrorKind::InvalidInput.into()) + } - async fn write_response( - &mut self, - _: &Self::Protocol, - io: &mut T, - res: Self::Response, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - let mut framer = FramedWrite::new(io, RendezvousCodec::default()); - framer.send(res).await?; - - Ok(()) - } + async fn write_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let mut framer = FramedWrite::new(io, RendezvousCodec::default()); + framer.send(req).await?; + + Ok(()) } - #[cfg(test)] - mod tests { - use crate::codec::{Message, NewRegistration}; - use crate::Namespace; - use async_std; - use futures::AsyncWriteExt; - use futures_ringbuf::Endpoint; - use libp2p_core::PeerRecord; - use libp2p_identity; - use libp2p_request_response::Codec; - - #[async_std::test] - async fn codec_test() { - let identity = libp2p_identity::Keypair::generate_ed25519(); - - let exp_msg = Message::Register(NewRegistration { - namespace: Namespace::from_static("wonderland"), - record: PeerRecord::new( - &identity, - vec!["/ip4/127.0.0.1/tcp/1234".parse().unwrap()], - ) - .unwrap(), - ttl: None, - }); - - let mut codec = super::Codec {}; - let protocol = &crate::PROTOCOL_IDENT; - - // Write/read request - - let (mut a, mut b) = Endpoint::pair( - crate::codec::MAX_MESSAGE_LEN_BYTES, - crate::codec::MAX_MESSAGE_LEN_BYTES, - ); - - codec - .write_request(protocol, &mut a, exp_msg.clone()) - .await - .expect("Should write"); - a.close().await.unwrap(); - - let act_msg = codec - .read_request(protocol, &mut b) - .await - .expect("Should read"); - b.close().await.unwrap(); - - assert_eq!(exp_msg, act_msg); - - // Write/read response - - let (mut a, mut b) = Endpoint::pair( - crate::codec::MAX_MESSAGE_LEN_BYTES, - crate::codec::MAX_MESSAGE_LEN_BYTES, - ); - - codec - .write_response(protocol, &mut a, exp_msg.clone()) - .await - .expect("Should write"); - a.close().await.unwrap(); - - let act_msg = codec - .read_response(protocol, &mut b) - .await - .expect("Should read"); - b.close().await.unwrap(); - - assert_eq!(exp_msg, act_msg); - } + async fn write_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + res: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let mut framer = FramedWrite::new(io, RendezvousCodec::default()); + framer.send(res).await?; + + Ok(()) } } @@ -421,10 +341,10 @@ impl From for proto::Message { fn from(message: Message) -> Self { match message { Message::Register(NewRegistration { - namespace, - record, - ttl, - }) => proto::Message { + namespace, + record, + ttl, + }) => proto::Message { type_pb: Some(proto::MessageType::REGISTER), register: Some(proto::Register { ns: Some(namespace.into()), @@ -699,7 +619,7 @@ impl TryFrom for ErrorCode { E_UNAVAILABLE => ErrorCode::Unavailable, }; - Result::Ok(code) + Ok(code) } } @@ -738,6 +658,14 @@ mod proto { #[cfg(test)] mod tests { use super::*; + use crate::codec::{Message, NewRegistration}; + use crate::Namespace; + use async_std; + use futures::AsyncWriteExt; + use futures_ringbuf::Endpoint; + use libp2p_core::PeerRecord; + use libp2p_identity; + use libp2p_request_response::Codec; #[test] fn cookie_wire_encoding_roundtrip() { @@ -757,4 +685,61 @@ mod tests { assert_eq!(bytes.len(), 8 + 3) } + + #[async_std::test] + async fn codec_test() { + let identity = libp2p_identity::Keypair::generate_ed25519(); + + let exp_msg = Message::Register(NewRegistration { + namespace: Namespace::from_static("wonderland"), + record: PeerRecord::new(&identity, vec!["/ip4/127.0.0.1/tcp/1234".parse().unwrap()]) + .unwrap(), + ttl: None, + }); + + let mut codec = super::Codec {}; + let protocol = &crate::PROTOCOL_IDENT; + + // Write/read request + + let (mut a, mut b) = Endpoint::pair( + MAX_MESSAGE_LEN_BYTES, + MAX_MESSAGE_LEN_BYTES, + ); + + codec + .write_request(protocol, &mut a, exp_msg.clone()) + .await + .expect("Should write"); + a.close().await.unwrap(); + + let act_msg = codec + .read_request(protocol, &mut b) + .await + .expect("Should read"); + b.close().await.unwrap(); + + assert_eq!(exp_msg, act_msg); + + // Write/read response + + let (mut a, mut b) = Endpoint::pair( + MAX_MESSAGE_LEN_BYTES, + MAX_MESSAGE_LEN_BYTES, + ); + + codec + .write_response(protocol, &mut a, exp_msg.clone()) + .await + .expect("Should write"); + a.close().await.unwrap(); + + let act_msg = codec + .read_response(protocol, &mut b) + .await + .expect("Should read"); + b.close().await.unwrap(); + + assert_eq!(exp_msg, act_msg); + } } diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs deleted file mode 100644 index 2d363f0a2ad..00000000000 --- a/protocols/rendezvous/src/handler.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2021 COMIT Network. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use crate::codec; -use crate::codec::Message; -use void::Void; - -pub(crate) mod inbound; -pub(crate) mod outbound; -/// Errors that can occur while interacting with a substream. -#[allow(clippy::large_enum_variant)] -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Reading message {0:?} at this stage is a protocol violation")] - BadMessage(Message), - #[error("Failed to write message to substream")] - WriteMessage(#[source] codec::Error), - #[error("Failed to read message from substream")] - ReadMessage(#[source] codec::Error), - #[error("Substream ended unexpectedly mid-protocol")] - UnexpectedEndOfStream, -} - -pub(crate) type OutboundInEvent = crate::substream_handler::InEvent; -pub(crate) type OutboundOutEvent = - crate::substream_handler::OutEvent; - -pub(crate) type InboundInEvent = crate::substream_handler::InEvent<(), inbound::InEvent, Void>; -pub(crate) type InboundOutEvent = - crate::substream_handler::OutEvent; diff --git a/protocols/rendezvous/src/handler/inbound.rs b/protocols/rendezvous/src/handler/inbound.rs deleted file mode 100644 index d02df33a21b..00000000000 --- a/protocols/rendezvous/src/handler/inbound.rs +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2021 COMIT Network. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use crate::codec::{ - Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, RendezvousCodec, Ttl, -}; -use crate::handler::Error; -use crate::substream_handler::{Next, PassthroughProtocol, SubstreamHandler}; -use crate::PROTOCOL_IDENT; -use asynchronous_codec::Framed; -use futures::{SinkExt, StreamExt}; -use libp2p_swarm::SubstreamProtocol; -use std::fmt; -use std::task::{Context, Poll}; - -/// The state of an inbound substream (i.e. the remote node opened it). -#[allow(clippy::large_enum_variant)] -#[allow(clippy::enum_variant_names)] -pub enum Stream { - /// We are in the process of reading a message from the substream. - PendingRead(Framed), - /// We read a message, dispatched it to the behaviour and are waiting for the response. - PendingBehaviour(Framed), - /// We are in the process of sending a response. - PendingSend(Framed, Message), - /// We've sent the message and are now closing down the substream. - PendingClose(Framed), -} - -impl fmt::Debug for Stream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Stream::PendingRead(_) => write!(f, "Inbound::PendingRead"), - Stream::PendingBehaviour(_) => write!(f, "Inbound::PendingBehaviour"), - Stream::PendingSend(_, _) => write!(f, "Inbound::PendingSend"), - Stream::PendingClose(_) => write!(f, "Inbound::PendingClose"), - } - } -} - -#[allow(clippy::large_enum_variant)] -#[allow(clippy::enum_variant_names)] -#[derive(Debug, Clone)] -pub enum OutEvent { - RegistrationRequested(NewRegistration), - UnregisterRequested(Namespace), - DiscoverRequested { - namespace: Option, - cookie: Option, - limit: Option, - }, -} - -#[derive(Debug)] -pub enum InEvent { - RegisterResponse { - ttl: Ttl, - }, - DeclineRegisterRequest(ErrorCode), - DiscoverResponse { - discovered: Vec, - cookie: Cookie, - }, - DeclineDiscoverRequest(ErrorCode), -} - -impl SubstreamHandler for Stream { - type InEvent = InEvent; - type OutEvent = OutEvent; - type Error = Error; - type OpenInfo = (); - - fn upgrade( - open_info: Self::OpenInfo, - ) -> SubstreamProtocol { - SubstreamProtocol::new(PassthroughProtocol::new(PROTOCOL_IDENT), open_info) - } - - fn new(substream: libp2p_swarm::Stream, _: Self::OpenInfo) -> Self { - Stream::PendingRead(Framed::new(substream, RendezvousCodec::default())) - } - - fn on_event(self, event: Self::InEvent) -> Self { - match (event, self) { - (InEvent::RegisterResponse { ttl }, Stream::PendingBehaviour(substream)) => { - Stream::PendingSend(substream, Message::RegisterResponse(Ok(ttl))) - } - (InEvent::DeclineRegisterRequest(error), Stream::PendingBehaviour(substream)) => { - Stream::PendingSend(substream, Message::RegisterResponse(Err(error))) - } - ( - InEvent::DiscoverResponse { discovered, cookie }, - Stream::PendingBehaviour(substream), - ) => Stream::PendingSend( - substream, - Message::DiscoverResponse(Ok((discovered, cookie))), - ), - (InEvent::DeclineDiscoverRequest(error), Stream::PendingBehaviour(substream)) => { - Stream::PendingSend(substream, Message::DiscoverResponse(Err(error))) - } - (event, inbound) => { - debug_assert!(false, "{inbound:?} cannot handle event {event:?}"); - - inbound - } - } - } - - fn advance(self, cx: &mut Context<'_>) -> Result, Self::Error> { - let next_state = match self { - Stream::PendingRead(mut substream) => { - match substream.poll_next_unpin(cx).map_err(Error::ReadMessage)? { - Poll::Ready(Some(msg)) => { - let event = match msg { - Message::Register(registration) => { - OutEvent::RegistrationRequested(registration) - } - Message::Discover { - cookie, - namespace, - limit, - } => OutEvent::DiscoverRequested { - cookie, - namespace, - limit, - }, - Message::Unregister(namespace) => { - OutEvent::UnregisterRequested(namespace) - } - other => return Err(Error::BadMessage(other)), - }; - - Next::EmitEvent { - event, - next_state: Stream::PendingBehaviour(substream), - } - } - Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), - Poll::Pending => Next::Pending { - next_state: Stream::PendingRead(substream), - }, - } - } - Stream::PendingBehaviour(substream) => Next::Pending { - next_state: Stream::PendingBehaviour(substream), - }, - Stream::PendingSend(mut substream, message) => match substream - .poll_ready_unpin(cx) - .map_err(Error::WriteMessage)? - { - Poll::Ready(()) => { - substream - .start_send_unpin(message) - .map_err(Error::WriteMessage)?; - - Next::Continue { - next_state: Stream::PendingClose(substream), - } - } - Poll::Pending => Next::Pending { - next_state: Stream::PendingSend(substream, message), - }, - }, - Stream::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(Ok(())) => Next::Done, - Poll::Ready(Err(_)) => Next::Done, // there is nothing we can do about an error during close - Poll::Pending => Next::Pending { - next_state: Stream::PendingClose(substream), - }, - }, - }; - - Ok(next_state) - } -} diff --git a/protocols/rendezvous/src/handler/outbound.rs b/protocols/rendezvous/src/handler/outbound.rs deleted file mode 100644 index 7dfc1723f2a..00000000000 --- a/protocols/rendezvous/src/handler/outbound.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2021 COMIT Network. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use crate::codec::{Cookie, Message, NewRegistration, RendezvousCodec}; -use crate::handler::Error; -use crate::substream_handler::{FutureSubstream, Next, PassthroughProtocol, SubstreamHandler}; -use crate::PROTOCOL_IDENT; -use crate::{ErrorCode, Namespace, Registration, Ttl}; -use asynchronous_codec::Framed; -use futures::{SinkExt, TryFutureExt, TryStreamExt}; -use libp2p_swarm::SubstreamProtocol; -use std::task::Context; -use void::Void; - -pub struct Stream(FutureSubstream); - -impl SubstreamHandler for Stream { - type InEvent = Void; - type OutEvent = OutEvent; - type Error = Error; - type OpenInfo = OpenInfo; - - fn upgrade( - open_info: Self::OpenInfo, - ) -> SubstreamProtocol { - SubstreamProtocol::new(PassthroughProtocol::new(PROTOCOL_IDENT), open_info) - } - - fn new(substream: libp2p_swarm::Stream, info: Self::OpenInfo) -> Self { - let mut stream = Framed::new(substream, RendezvousCodec::default()); - let sent_message = match info { - OpenInfo::RegisterRequest(new_registration) => Message::Register(new_registration), - OpenInfo::UnregisterRequest(namespace) => Message::Unregister(namespace), - OpenInfo::DiscoverRequest { - namespace, - cookie, - limit, - } => Message::Discover { - namespace, - cookie, - limit, - }, - }; - - Self(FutureSubstream::new(async move { - use Message::*; - use OutEvent::*; - - stream - .send(sent_message.clone()) - .map_err(Error::WriteMessage) - .await?; - let received_message = stream.try_next().map_err(Error::ReadMessage).await?; - let received_message = received_message.ok_or(Error::UnexpectedEndOfStream)?; - - let event = match (sent_message, received_message) { - (Register(registration), RegisterResponse(Ok(ttl))) => Registered { - namespace: registration.namespace, - ttl, - }, - (Register(registration), RegisterResponse(Err(error))) => { - RegisterFailed(registration.namespace, error) - } - (Discover { .. }, DiscoverResponse(Ok((registrations, cookie)))) => Discovered { - registrations, - cookie, - }, - (Discover { namespace, .. }, DiscoverResponse(Err(error))) => { - DiscoverFailed { namespace, error } - } - (.., other) => return Err(Error::BadMessage(other)), - }; - - stream.close().map_err(Error::WriteMessage).await?; - - Ok(event) - })) - } - - fn on_event(self, event: Self::InEvent) -> Self { - void::unreachable(event) - } - - fn advance(self, cx: &mut Context<'_>) -> Result, Self::Error> { - Ok(self.0.advance(cx)?.map_state(Stream)) - } -} - -#[derive(Debug, Clone)] -pub enum OutEvent { - Registered { - namespace: Namespace, - ttl: Ttl, - }, - RegisterFailed(Namespace, ErrorCode), - Discovered { - registrations: Vec, - cookie: Cookie, - }, - DiscoverFailed { - namespace: Option, - error: ErrorCode, - }, -} - -#[allow(clippy::large_enum_variant)] -#[allow(clippy::enum_variant_names)] -#[derive(Debug)] -pub enum OpenInfo { - RegisterRequest(NewRegistration), - UnregisterRequest(Namespace), - DiscoverRequest { - namespace: Option, - cookie: Option, - limit: Option, - }, -} diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 7ad96201364..7c607085f20 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -26,8 +26,6 @@ pub use self::codec::{Cookie, ErrorCode, Namespace, NamespaceTooLong, Registrati use libp2p_swarm::StreamProtocol; mod codec; -mod handler; -mod substream_handler; /// If unspecified, rendezvous nodes should assume a TTL of 2h. /// @@ -49,7 +47,7 @@ pub const MAX_TTL: Ttl = 60 * 60 * 72; /// . pub const MAX_NAMESPACE: usize = 255; -pub const PROTOCOL_IDENT: StreamProtocol = StreamProtocol::new("/rendezvous/1.0.0"); +pub(crate) const PROTOCOL_IDENT: StreamProtocol = StreamProtocol::new("/rendezvous/1.0.0"); pub mod client; pub mod server; diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 6d64938ca3d..07a25b6e3f4 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -18,10 +18,8 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::codec::{Cookie, ErrorCode, Namespace, NewRegistration, Registration, Ttl}; -use crate::handler::inbound; -use crate::substream_handler::{InEvent, InboundSubstreamId, SubstreamConnectionHandler}; -use crate::{handler, MAX_TTL, MIN_TTL}; +use crate::codec::{Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl}; +use crate::{MAX_TTL, MIN_TTL}; use bimap::BiMap; use futures::future::BoxFuture; use futures::ready; @@ -29,19 +27,21 @@ use futures::stream::FuturesUnordered; use futures::{FutureExt, StreamExt}; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; +use libp2p_request_response::ProtocolSupport; use libp2p_swarm::behaviour::FromSwarm; use libp2p_swarm::{ - CloseConnection, ConnectionDenied, ConnectionId, NetworkBehaviour, NotifyHandler, - PollParameters, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, + ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters, THandler, THandlerInEvent, + THandlerOutEvent, ToSwarm, }; -use std::collections::{HashMap, HashSet, VecDeque}; +use std::collections::{HashMap, HashSet}; +use std::iter; use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::Duration; -use void::Void; pub struct Behaviour { - events: VecDeque>>, + inner: libp2p_request_response::Behaviour, + registrations: Registrations, } @@ -75,7 +75,12 @@ impl Behaviour { /// Create a new instance of the rendezvous [`NetworkBehaviour`]. pub fn new(config: Config) -> Self { Self { - events: Default::default(), + inner: libp2p_request_response::Behaviour::with_codec( + crate::codec::Codec::default(), + iter::once((crate::PROTOCOL_IDENT, ProtocolSupport::Full)), + libp2p_request_response::Config::default(), + ), + registrations: Registrations::with_config(config), } } @@ -109,31 +114,36 @@ pub enum Event { } impl NetworkBehaviour for Behaviour { - type ConnectionHandler = SubstreamConnectionHandler; + type ConnectionHandler = as NetworkBehaviour>::ConnectionHandler; + type ToSwarm = Event; fn handle_established_inbound_connection( &mut self, - _: ConnectionId, - _: PeerId, - _: &Multiaddr, - _: &Multiaddr, + _connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { - Ok(SubstreamConnectionHandler::new_inbound_only( - Duration::from_secs(30), - )) + self.inner.handle_established_inbound_connection( + _connection_id, + peer, + local_addr, + remote_addr, + ) } fn handle_established_outbound_connection( &mut self, - _: ConnectionId, - _: PeerId, - _: &Multiaddr, - _: Endpoint, + _connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, ) -> Result, ConnectionDenied> { - Ok(SubstreamConnectionHandler::new_inbound_only( - Duration::from_secs(30), - )) + self.inner + .handle_established_outbound_connection(_connection_id, peer, addr, role_override) } fn on_connection_handler_event( @@ -142,29 +152,14 @@ impl NetworkBehaviour for Behaviour { connection: ConnectionId, event: THandlerOutEvent, ) { - let new_events = match event { - handler::InboundOutEvent::InboundEvent { id, message } => { - handle_inbound_event(message, peer_id, connection, id, &mut self.registrations) - } - handler::InboundOutEvent::OutboundEvent { message, .. } => void::unreachable(message), - handler::InboundOutEvent::InboundError { error, .. } => { - log::warn!("Connection with peer {} failed: {}", peer_id, error); - - vec![ToSwarm::CloseConnection { - peer_id, - connection: CloseConnection::One(connection), - }] - } - handler::InboundOutEvent::OutboundError { error, .. } => void::unreachable(error), - }; - - self.events.extend(new_events); + self.inner + .on_connection_handler_event(peer_id, connection, event); } fn poll( &mut self, cx: &mut Context<'_>, - _: &mut impl PollParameters, + params: &mut impl PollParameters, ) -> Poll>> { if let Poll::Ready(ExpiredRegistration(registration)) = self.registrations.poll(cx) { return Poll::Ready(ToSwarm::GenerateEvent(Event::RegistrationExpired( @@ -172,106 +167,116 @@ impl NetworkBehaviour for Behaviour { ))); } - if let Some(event) = self.events.pop_front() { - return Poll::Ready(event); + let poll_res = self.inner.poll(cx, params); + if let Poll::Ready(to_swarm) = poll_res { + if let ToSwarm::GenerateEvent(event) = to_swarm { + let opt_event = match event { + libp2p_request_response::Event::Message { + peer: peer_id, + message: + libp2p_request_response::Message::Request { + request, channel, .. + }, + } => { + let (event, response) = + handle_request(peer_id, request, &mut self.registrations); + if let Some(resp) = response { + self.inner + .send_response(channel, resp) + .expect("Send response"); + } + + Some(event) + } + libp2p_request_response::Event::ResponseSent { .. } => None, + libp2p_request_response::Event::InboundFailure { + peer, + request_id, + error, + } => { + log::warn!("Inbound request {request_id} with peer {peer} failed: {error}"); + + None + } + libp2p_request_response::Event::Message { + peer: _, + message: libp2p_request_response::Message::Response { .. }, + } => None, + libp2p_request_response::Event::OutboundFailure { .. } => None, + }; + + if let Some(out_event) = opt_event { + return Poll::Ready(ToSwarm::GenerateEvent(out_event)); + } + } } Poll::Pending } fn on_swarm_event(&mut self, event: FromSwarm) { - match event { - FromSwarm::ConnectionEstablished(_) - | FromSwarm::ConnectionClosed(_) - | FromSwarm::AddressChange(_) - | FromSwarm::DialFailure(_) - | FromSwarm::ListenFailure(_) - | FromSwarm::NewListener(_) - | FromSwarm::NewListenAddr(_) - | FromSwarm::ExpiredListenAddr(_) - | FromSwarm::ListenerError(_) - | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddrCandidate(_) - | FromSwarm::ExternalAddrExpired(_) - | FromSwarm::ExternalAddrConfirmed(_) => {} - } + self.inner.on_swarm_event(event); } } -fn handle_inbound_event( - event: inbound::OutEvent, +fn handle_request( peer_id: PeerId, - connection: ConnectionId, - id: InboundSubstreamId, + message: Message, registrations: &mut Registrations, -) -> Vec>> { - match event { - // bad registration - inbound::OutEvent::RegistrationRequested(registration) - if registration.record.peer_id() != peer_id => - { - let error = ErrorCode::NotAuthorized; - - vec![ - ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: handler::InboundInEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::DeclineRegisterRequest(error), - }, - }, - ToSwarm::GenerateEvent(Event::PeerNotRegistered { +) -> (Event, Option) { + match message { + Message::Register(registration) => { + if registration.record.peer_id() != peer_id { + let error = ErrorCode::NotAuthorized; + + let event = Event::PeerNotRegistered { peer: peer_id, namespace: registration.namespace, error, - }), - ] - } - inbound::OutEvent::RegistrationRequested(registration) => { + }; + + return (event, Some(Message::RegisterResponse(Err(error)))); + } + let namespace = registration.namespace.clone(); match registrations.add(registration) { Ok(registration) => { - vec![ - ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: handler::InboundInEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::RegisterResponse { - ttl: registration.ttl, - }, - }, - }, - ToSwarm::GenerateEvent(Event::PeerRegistered { - peer: peer_id, - registration, - }), - ] + let response = Message::RegisterResponse(Ok(registration.ttl)); + + let event = Event::PeerRegistered { + peer: peer_id, + registration, + }; + + (event, Some(response)) } Err(TtlOutOfRange::TooLong { .. }) | Err(TtlOutOfRange::TooShort { .. }) => { let error = ErrorCode::InvalidTtl; - vec![ - ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: handler::InboundInEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::DeclineRegisterRequest(error), - }, - }, - ToSwarm::GenerateEvent(Event::PeerNotRegistered { - peer: peer_id, - namespace, - error, - }), - ] + let response = Message::RegisterResponse(Err(error)); + + let event = Event::PeerNotRegistered { + peer: peer_id, + namespace, + error, + }; + + (event, Some(response)) } } } - inbound::OutEvent::DiscoverRequested { + Message::Unregister(namespace) => { + registrations.remove(namespace.clone(), peer_id); + + let event = Event::PeerUnregistered { + peer: peer_id, + namespace, + }; + + (event, None) + } + Message::Discover { namespace, cookie, limit, @@ -279,50 +284,33 @@ fn handle_inbound_event( Ok((registrations, cookie)) => { let discovered = registrations.cloned().collect::>(); - vec![ - ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: handler::InboundInEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::DiscoverResponse { - discovered: discovered.clone(), - cookie, - }, - }, - }, - ToSwarm::GenerateEvent(Event::DiscoverServed { - enquirer: peer_id, - registrations: discovered, - }), - ] + let response = Message::DiscoverResponse(Ok((discovered.clone(), cookie.clone()))); + + let event = Event::DiscoverServed { + enquirer: peer_id, + registrations: discovered, + }; + + (event, Some(response)) } Err(_) => { let error = ErrorCode::InvalidCookie; - vec![ - ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: handler::InboundInEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::DeclineDiscoverRequest(error), - }, - }, - ToSwarm::GenerateEvent(Event::DiscoverNotServed { - enquirer: peer_id, - error, - }), - ] + let response = Message::DiscoverResponse(Err(error)); + + let event = Event::DiscoverNotServed { + enquirer: peer_id, + error, + }; + + (event, Some(response)) } }, - inbound::OutEvent::UnregisterRequested(namespace) => { - registrations.remove(namespace.clone(), peer_id); - - vec![ToSwarm::GenerateEvent(Event::PeerUnregistered { - peer: peer_id, - namespace, - })] + Message::RegisterResponse(_) => { + unreachable!() + } + Message::DiscoverResponse(_) => { + unreachable!() } } } @@ -395,6 +383,7 @@ impl Registrations { let namespace = new_registration.namespace; let registration_id = RegistrationId::new(); + // todo check can we save a `Registration` as the right part? if let Some(old_registration) = self .registrations_for_peer .get_by_left(&(new_registration.record.peer_id(), namespace.clone())) @@ -488,10 +477,11 @@ impl Registrations { self.cookies .insert(new_cookie.clone(), reggos_of_last_discover); + // todo if have it done like I want, this will be an excess block of code let reggos = &self.registrations; let registrations = ids .into_iter() - .map(move |id| reggos.get(&id).expect("bad internal datastructure")); + .map(move |id| reggos.get(&id).expect("bad internal data structure")); Ok((registrations, new_cookie)) } diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs deleted file mode 100644 index 2434b691fb2..00000000000 --- a/protocols/rendezvous/src/substream_handler.rs +++ /dev/null @@ -1,556 +0,0 @@ -// Copyright 2021 COMIT Network. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -//! A generic [`ConnectionHandler`] that delegates the handling of substreams to [`SubstreamHandler`]s. -//! -//! This module is an attempt to simplify the implementation of protocols by freeing implementations from dealing with aspects such as concurrent substreams. -//! Particularly for outbound substreams, it greatly simplifies the definition of protocols through the [`FutureSubstream`] helper. -//! -//! At the moment, this module is an implementation detail of the rendezvous protocol but the intent is for it to be provided as a generic module that is accessible to other protocols as well. - -use futures::future::{self, BoxFuture, Fuse, FusedFuture}; -use futures::FutureExt; -use instant::Instant; -use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; -use libp2p_swarm::handler::{ConnectionEvent, FullyNegotiatedInbound, FullyNegotiatedOutbound}; -use libp2p_swarm::{ - ConnectionHandler, ConnectionHandlerEvent, KeepAlive, Stream, StreamProtocol, SubstreamProtocol, -}; -use std::collections::{HashMap, VecDeque}; -use std::fmt; -use std::future::Future; -use std::hash::Hash; -use std::task::{Context, Poll}; -use std::time::Duration; -use void::Void; - -/// Handles a substream throughout its lifetime. -pub trait SubstreamHandler: Sized { - type InEvent; - type OutEvent; - type Error; - type OpenInfo; - - fn upgrade(open_info: Self::OpenInfo) - -> SubstreamProtocol; - fn new(substream: Stream, info: Self::OpenInfo) -> Self; - fn on_event(self, event: Self::InEvent) -> Self; - fn advance(self, cx: &mut Context<'_>) -> Result, Self::Error>; -} - -/// The result of advancing a [`SubstreamHandler`]. -pub enum Next { - /// Return the given event and set the handler into `next_state`. - EmitEvent { event: TEvent, next_state: TState }, - /// The handler currently cannot do any more work, set its state back into `next_state`. - Pending { next_state: TState }, - /// The handler performed some work and wants to continue in the given state. - /// - /// This variant is useful because it frees the handler from implementing a loop internally. - Continue { next_state: TState }, - /// The handler finished. - Done, -} - -impl Next { - pub fn map_state( - self, - map: impl FnOnce(TState) -> TNextState, - ) -> Next { - match self { - Next::EmitEvent { event, next_state } => Next::EmitEvent { - event, - next_state: map(next_state), - }, - Next::Pending { next_state } => Next::Pending { - next_state: map(next_state), - }, - Next::Continue { next_state } => Next::Pending { - next_state: map(next_state), - }, - Next::Done => Next::Done, - } - } -} - -#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] -pub struct InboundSubstreamId(u64); - -impl InboundSubstreamId { - fn fetch_and_increment(&mut self) -> Self { - let next_id = *self; - self.0 += 1; - - next_id - } -} - -impl fmt::Display for InboundSubstreamId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] -pub struct OutboundSubstreamId(u64); - -impl OutboundSubstreamId { - fn fetch_and_increment(&mut self) -> Self { - let next_id = *self; - self.0 += 1; - - next_id - } -} - -impl fmt::Display for OutboundSubstreamId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -pub struct PassthroughProtocol { - ident: Option, -} - -impl PassthroughProtocol { - pub fn new(ident: StreamProtocol) -> Self { - Self { ident: Some(ident) } - } -} - -impl UpgradeInfo for PassthroughProtocol { - type Info = StreamProtocol; - type InfoIter = std::option::IntoIter; - - fn protocol_info(&self) -> Self::InfoIter { - self.ident.clone().into_iter() - } -} - -impl InboundUpgrade for PassthroughProtocol { - type Output = C; - type Error = Void; - type Future = BoxFuture<'static, Result>; - - fn upgrade_inbound(self, socket: C, _: Self::Info) -> Self::Future { - match self.ident { - Some(_) => future::ready(Ok(socket)).boxed(), - None => future::pending().boxed(), - } - } -} - -impl OutboundUpgrade for PassthroughProtocol { - type Output = C; - type Error = Void; - type Future = BoxFuture<'static, Result>; - - fn upgrade_outbound(self, socket: C, _: Self::Info) -> Self::Future { - match self.ident { - Some(_) => future::ready(Ok(socket)).boxed(), - None => future::pending().boxed(), - } - } -} - -/// An implementation of [`ConnectionHandler`] that delegates to individual [`SubstreamHandler`]s. -pub struct SubstreamConnectionHandler { - inbound_substreams: HashMap, - outbound_substreams: HashMap, - next_inbound_substream_id: InboundSubstreamId, - next_outbound_substream_id: OutboundSubstreamId, - - new_substreams: VecDeque, - - initial_keep_alive_deadline: Instant, -} - -impl - SubstreamConnectionHandler -{ - pub fn new(initial_keep_alive: Duration) -> Self { - Self { - inbound_substreams: Default::default(), - outbound_substreams: Default::default(), - next_inbound_substream_id: InboundSubstreamId(0), - next_outbound_substream_id: OutboundSubstreamId(0), - new_substreams: Default::default(), - initial_keep_alive_deadline: Instant::now() + initial_keep_alive, - } - } -} - -impl - SubstreamConnectionHandler -{ - pub fn new_outbound_only(initial_keep_alive: Duration) -> Self { - Self { - inbound_substreams: Default::default(), - outbound_substreams: Default::default(), - next_inbound_substream_id: InboundSubstreamId(0), - next_outbound_substream_id: OutboundSubstreamId(0), - new_substreams: Default::default(), - initial_keep_alive_deadline: Instant::now() + initial_keep_alive, - } - } -} - -impl - SubstreamConnectionHandler -{ - pub fn new_inbound_only(initial_keep_alive: Duration) -> Self { - Self { - inbound_substreams: Default::default(), - outbound_substreams: Default::default(), - next_inbound_substream_id: InboundSubstreamId(0), - next_outbound_substream_id: OutboundSubstreamId(0), - new_substreams: Default::default(), - initial_keep_alive_deadline: Instant::now() + initial_keep_alive, - } - } -} - -/// Poll all substreams within the given HashMap. -/// -/// This is defined as a separate function because we call it with two different fields stored within [`SubstreamConnectionHandler`]. -fn poll_substreams( - substreams: &mut HashMap, - cx: &mut Context<'_>, -) -> Poll> -where - TSubstream: SubstreamHandler, - TId: Copy + Eq + Hash + fmt::Display, -{ - let substream_ids = substreams.keys().copied().collect::>(); - - 'loop_substreams: for id in substream_ids { - let mut handler = substreams - .remove(&id) - .expect("we just got the key out of the map"); - - let (next_state, poll) = 'loop_handler: loop { - match handler.advance(cx) { - Ok(Next::EmitEvent { next_state, event }) => { - break (next_state, Poll::Ready(Ok((id, event)))) - } - Ok(Next::Pending { next_state }) => break (next_state, Poll::Pending), - Ok(Next::Continue { next_state }) => { - handler = next_state; - continue 'loop_handler; - } - Ok(Next::Done) => { - log::debug!("Substream handler {} finished", id); - continue 'loop_substreams; - } - Err(e) => return Poll::Ready(Err((id, e))), - } - }; - - substreams.insert(id, next_state); - - return poll; - } - - Poll::Pending -} - -/// Event sent from the [`libp2p_swarm::NetworkBehaviour`] to the [`SubstreamConnectionHandler`]. -#[allow(clippy::enum_variant_names)] -#[derive(Debug)] -pub enum InEvent { - /// Open a new substream using the provided `open_info`. - /// - /// For "client-server" protocols, this is typically the initial message to be sent to the other party. - NewSubstream { open_info: I }, - NotifyInboundSubstream { - id: InboundSubstreamId, - message: TInboundEvent, - }, - NotifyOutboundSubstream { - id: OutboundSubstreamId, - message: TOutboundEvent, - }, -} - -/// Event produced by the [`SubstreamConnectionHandler`] for the corresponding [`libp2p_swarm::NetworkBehaviour`]. -#[derive(Debug)] -pub enum OutEvent { - /// An inbound substream produced an event. - InboundEvent { - id: InboundSubstreamId, - message: TInbound, - }, - /// An outbound substream produced an event. - OutboundEvent { - id: OutboundSubstreamId, - message: TOutbound, - }, - /// An inbound substream errored irrecoverably. - InboundError { - id: InboundSubstreamId, - error: TInboundError, - }, - /// An outbound substream errored irrecoverably. - OutboundError { - id: OutboundSubstreamId, - error: TOutboundError, - }, -} - -impl< - TInboundInEvent, - TInboundOutEvent, - TOutboundInEvent, - TOutboundOutEvent, - TOutboundOpenInfo, - TInboundError, - TOutboundError, - TInboundSubstreamHandler, - TOutboundSubstreamHandler, - > ConnectionHandler - for SubstreamConnectionHandler< - TInboundSubstreamHandler, - TOutboundSubstreamHandler, - TOutboundOpenInfo, - > -where - TInboundSubstreamHandler: SubstreamHandler< - InEvent = TInboundInEvent, - OutEvent = TInboundOutEvent, - Error = TInboundError, - OpenInfo = (), - >, - TOutboundSubstreamHandler: SubstreamHandler< - InEvent = TOutboundInEvent, - OutEvent = TOutboundOutEvent, - Error = TOutboundError, - OpenInfo = TOutboundOpenInfo, - >, - TInboundInEvent: fmt::Debug + Send + 'static, - TInboundOutEvent: fmt::Debug + Send + 'static, - TOutboundInEvent: fmt::Debug + Send + 'static, - TOutboundOutEvent: fmt::Debug + Send + 'static, - TOutboundOpenInfo: fmt::Debug + Send + 'static, - TInboundError: fmt::Debug + Send + 'static, - TOutboundError: fmt::Debug + Send + 'static, - TInboundSubstreamHandler: Send + 'static, - TOutboundSubstreamHandler: Send + 'static, -{ - type FromBehaviour = InEvent; - type ToBehaviour = OutEvent; - type Error = Void; - type InboundProtocol = PassthroughProtocol; - type OutboundProtocol = PassthroughProtocol; - type InboundOpenInfo = (); - type OutboundOpenInfo = TOutboundOpenInfo; - - fn listen_protocol(&self) -> SubstreamProtocol { - TInboundSubstreamHandler::upgrade(()) - } - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { - protocol, .. - }) => { - self.inbound_substreams.insert( - self.next_inbound_substream_id.fetch_and_increment(), - TInboundSubstreamHandler::new(protocol, ()), - ); - } - ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { - protocol, - info, - }) => { - self.outbound_substreams.insert( - self.next_outbound_substream_id.fetch_and_increment(), - TOutboundSubstreamHandler::new(protocol, info), - ); - } - // TODO: Handle upgrade errors properly - ConnectionEvent::AddressChange(_) - | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::DialUpgradeError(_) - | ConnectionEvent::LocalProtocolsChange(_) - | ConnectionEvent::RemoteProtocolsChange(_) => {} - } - } - - fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { - match event { - InEvent::NewSubstream { open_info } => self.new_substreams.push_back(open_info), - InEvent::NotifyInboundSubstream { id, message } => { - match self.inbound_substreams.remove(&id) { - Some(handler) => { - let new_handler = handler.on_event(message); - - self.inbound_substreams.insert(id, new_handler); - } - None => { - log::debug!("Substream with ID {} not found", id); - } - } - } - InEvent::NotifyOutboundSubstream { id, message } => { - match self.outbound_substreams.remove(&id) { - Some(handler) => { - let new_handler = handler.on_event(message); - - self.outbound_substreams.insert(id, new_handler); - } - None => { - log::debug!("Substream with ID {} not found", id); - } - } - } - } - } - - fn connection_keep_alive(&self) -> KeepAlive { - // Rudimentary keep-alive handling, to be extended as needed as this abstraction is used more by other protocols. - - if Instant::now() < self.initial_keep_alive_deadline { - return KeepAlive::Yes; - } - - if self.inbound_substreams.is_empty() - && self.outbound_substreams.is_empty() - && self.new_substreams.is_empty() - { - return KeepAlive::No; - } - - KeepAlive::Yes - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent< - Self::OutboundProtocol, - Self::OutboundOpenInfo, - Self::ToBehaviour, - Self::Error, - >, - > { - if let Some(open_info) = self.new_substreams.pop_front() { - return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: TOutboundSubstreamHandler::upgrade(open_info), - }); - } - - match poll_substreams(&mut self.inbound_substreams, cx) { - Poll::Ready(Ok((id, message))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - OutEvent::InboundEvent { id, message }, - )) - } - Poll::Ready(Err((id, error))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - OutEvent::InboundError { id, error }, - )) - } - Poll::Pending => {} - } - - match poll_substreams(&mut self.outbound_substreams, cx) { - Poll::Ready(Ok((id, message))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - OutEvent::OutboundEvent { id, message }, - )) - } - Poll::Ready(Err((id, error))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - OutEvent::OutboundError { id, error }, - )) - } - Poll::Pending => {} - } - - Poll::Pending - } -} - -/// A helper struct for substream handlers that can be implemented as async functions. -/// -/// This only works for substreams without an `InEvent` because - once constructed - the state of an inner future is opaque. -pub(crate) struct FutureSubstream { - future: Fuse>>, -} - -impl FutureSubstream { - pub(crate) fn new( - future: impl Future> + Send + 'static, - ) -> Self { - Self { - future: future.boxed().fuse(), - } - } - - pub(crate) fn advance(mut self, cx: &mut Context<'_>) -> Result, TError> { - if self.future.is_terminated() { - return Ok(Next::Done); - } - - match self.future.poll_unpin(cx) { - Poll::Ready(Ok(event)) => Ok(Next::EmitEvent { - event, - next_state: self, - }), - Poll::Ready(Err(error)) => Err(error), - Poll::Pending => Ok(Next::Pending { next_state: self }), - } - } -} - -impl SubstreamHandler for void::Void { - type InEvent = void::Void; - type OutEvent = void::Void; - type Error = void::Void; - type OpenInfo = (); - - fn new(_: Stream, _: Self::OpenInfo) -> Self { - unreachable!("we should never yield a substream") - } - - fn on_event(self, event: Self::InEvent) -> Self { - void::unreachable(event) - } - - fn advance(self, _: &mut Context<'_>) -> Result, Self::Error> { - void::unreachable(self) - } - - fn upgrade( - open_info: Self::OpenInfo, - ) -> SubstreamProtocol { - SubstreamProtocol::new(PassthroughProtocol { ident: None }, open_info) - } -} From 2a794e0668538a2e3514b2770c5543d7f935ed4e Mon Sep 17 00:00:00 2001 From: dgarus Date: Fri, 9 Jun 2023 15:36:59 +0300 Subject: [PATCH 03/26] Fix test failure --- Cargo.lock | 109 +++++++++++++++++++++++++---- protocols/rendezvous/src/client.rs | 2 +- protocols/rendezvous/src/codec.rs | 10 +-- protocols/rendezvous/src/server.rs | 8 +-- 4 files changed, 101 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea343a9b79f..ba12c169215 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -668,6 +668,12 @@ dependencies = [ "serde", ] +[[package]] +name = "cache-padded" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" + [[package]] name = "cast" version = "0.3.0" @@ -810,7 +816,7 @@ dependencies = [ "anstyle", "bitflags", "clap_lex", - "strsim", + "strsim 0.10.0", ] [[package]] @@ -1120,14 +1126,38 @@ dependencies = [ "zeroize", ] +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core 0.10.2", + "darling_macro 0.10.2", +] + [[package]] name = "darling" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn 1.0.109", ] [[package]] @@ -1140,7 +1170,18 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core 0.10.2", + "quote", "syn 1.0.109", ] @@ -1150,7 +1191,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core", + "darling_core 0.14.4", "quote", "syn 1.0.109", ] @@ -1258,7 +1299,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" dependencies = [ - "darling", + "darling 0.14.4", "proc-macro2", "quote", "syn 1.0.109", @@ -1703,6 +1744,19 @@ dependencies = [ "slab", ] +[[package]] +name = "futures_ringbuf" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b905098b5519bd63b2a1f9f4615198b0e38a473ce201ffdbd4dea6eb63087ddc" +dependencies = [ + "futures", + "log", + "log-derive", + "ringbuf 0.2.8", + "rustc_version", +] + [[package]] name = "futures_ringbuf" version = "0.4.0" @@ -1711,7 +1765,7 @@ checksum = "6628abb6eb1fc74beaeb20cd0670c43d158b0150f7689b38c3eaf663f99bdec7" dependencies = [ "futures", "log", - "ringbuf", + "ringbuf 0.3.3", "rustc_version", ] @@ -2451,7 +2505,7 @@ dependencies = [ "async-std", "flate2", "futures", - "futures_ringbuf", + "futures_ringbuf 0.4.0", "libp2p-core", "libp2p-tcp", "quickcheck-ext", @@ -2691,7 +2745,7 @@ version = "0.1.0" dependencies = [ "futures", "futures-timer", - "futures_ringbuf", + "futures_ringbuf 0.4.0", "libp2p-core", "log", ] @@ -2704,7 +2758,7 @@ dependencies = [ "curve25519-dalek 3.2.0", "env_logger 0.10.0", "futures", - "futures_ringbuf", + "futures_ringbuf 0.4.0", "libp2p-core", "libp2p-identity", "log", @@ -2777,7 +2831,7 @@ dependencies = [ "bytes", "env_logger 0.10.0", "futures", - "futures_ringbuf", + "futures_ringbuf 0.4.0", "libp2p-core", "libp2p-identity", "log", @@ -2873,7 +2927,7 @@ dependencies = [ "env_logger 0.10.0", "futures", "futures-timer", - "futures_ringbuf", + "futures_ringbuf 0.3.1", "instant", "libp2p-core", "libp2p-identify", @@ -2902,7 +2956,7 @@ dependencies = [ "async-trait", "env_logger 0.10.0", "futures", - "futures_ringbuf", + "futures_ringbuf 0.4.0", "instant", "libp2p-core", "libp2p-identity", @@ -3197,6 +3251,18 @@ dependencies = [ "value-bag", ] +[[package]] +name = "log-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a42526bb432bcd1b43571d5f163984effa25409a29f1a3242a54d0577d55bcf" +dependencies = [ + "darling 0.10.2", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "lru" version = "0.10.0" @@ -3351,7 +3417,7 @@ dependencies = [ "bytes", "env_logger 0.10.0", "futures", - "futures_ringbuf", + "futures_ringbuf 0.4.0", "libp2p-core", "libp2p-identity", "libp2p-plaintext", @@ -4182,6 +4248,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "ringbuf" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65af18d50f789e74aaf23bbb3f65dcd22a3cb6e029b5bced149f6bd57c5c2a2" +dependencies = [ + "cache-padded", +] + [[package]] name = "ringbuf" version = "0.3.3" @@ -4714,6 +4789,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + [[package]] name = "strsim" version = "0.10.0" diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 3d9f89bb620..ba5e67e0c9a 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -104,7 +104,7 @@ impl Behaviour { Self { inner: libp2p_request_response::Behaviour::with_codec( crate::codec::Codec::default(), - iter::once((crate::PROTOCOL_IDENT, ProtocolSupport::Full)), + iter::once((crate::PROTOCOL_IDENT, ProtocolSupport::Outbound)), libp2p_request_response::Config::default(), ), error_events: Default::default(), diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 4037bc0f5a0..14e6390fb3d 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -702,10 +702,7 @@ mod tests { // Write/read request - let (mut a, mut b) = Endpoint::pair( - MAX_MESSAGE_LEN_BYTES, - MAX_MESSAGE_LEN_BYTES, - ); + let (mut a, mut b) = Endpoint::pair(MAX_MESSAGE_LEN_BYTES, MAX_MESSAGE_LEN_BYTES); codec .write_request(protocol, &mut a, exp_msg.clone()) @@ -723,10 +720,7 @@ mod tests { // Write/read response - let (mut a, mut b) = Endpoint::pair( - MAX_MESSAGE_LEN_BYTES, - MAX_MESSAGE_LEN_BYTES, - ); + let (mut a, mut b) = Endpoint::pair(MAX_MESSAGE_LEN_BYTES, MAX_MESSAGE_LEN_BYTES); codec .write_response(protocol, &mut a, exp_msg.clone()) diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 07a25b6e3f4..77f4f8f45f6 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -77,7 +77,7 @@ impl Behaviour { Self { inner: libp2p_request_response::Behaviour::with_codec( crate::codec::Codec::default(), - iter::once((crate::PROTOCOL_IDENT, ProtocolSupport::Full)), + iter::once((crate::PROTOCOL_IDENT, ProtocolSupport::Inbound)), libp2p_request_response::Config::default(), ), @@ -383,7 +383,6 @@ impl Registrations { let namespace = new_registration.namespace; let registration_id = RegistrationId::new(); - // todo check can we save a `Registration` as the right part? if let Some(old_registration) = self .registrations_for_peer .get_by_left(&(new_registration.record.peer_id(), namespace.clone())) @@ -477,11 +476,10 @@ impl Registrations { self.cookies .insert(new_cookie.clone(), reggos_of_last_discover); - // todo if have it done like I want, this will be an excess block of code - let reggos = &self.registrations; + let regs = &self.registrations; let registrations = ids .into_iter() - .map(move |id| reggos.get(&id).expect("bad internal data structure")); + .map(move |id| regs.get(&id).expect("bad internal data structure")); Ok((registrations, new_cookie)) } From 386b5030b50948d5bb9a975d114c0ff5d3ab2f4a Mon Sep 17 00:00:00 2001 From: dgarus Date: Fri, 9 Jun 2023 23:18:58 +0300 Subject: [PATCH 04/26] Fixing clippy fail --- protocols/rendezvous/src/client.rs | 56 ++++++++++----------- protocols/rendezvous/src/codec.rs | 12 ++--- protocols/rendezvous/src/server.rs | 80 +++++++++++++++--------------- 3 files changed, 68 insertions(+), 80 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index ba5e67e0c9a..5039bf4220c 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -176,7 +176,7 @@ impl Behaviour { &rendezvous_node, Discover { namespace: namespace.clone(), - cookie: cookie.clone(), + cookie, limit, }, ); @@ -195,13 +195,13 @@ impl NetworkBehaviour for Behaviour { fn handle_established_inbound_connection( &mut self, - _connection_id: ConnectionId, + connection_id: ConnectionId, peer: PeerId, local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { self.inner.handle_established_inbound_connection( - _connection_id, + connection_id, peer, local_addr, remote_addr, @@ -221,12 +221,12 @@ impl NetworkBehaviour for Behaviour { fn on_connection_handler_event( &mut self, - _peer_id: PeerId, - _connection_id: ConnectionId, - _event: THandlerOutEvent, + peer_id: PeerId, + connection_id: ConnectionId, + event: THandlerOutEvent, ) { self.inner - .on_connection_handler_event(_peer_id, _connection_id, _event); + .on_connection_handler_event(peer_id, connection_id, event); } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -246,19 +246,17 @@ impl NetworkBehaviour for Behaviour { let poll_res = self.inner.poll(cx, params); - if let Poll::Ready(to_swarm) = &poll_res { - if let ToSwarm::GenerateEvent(event) = to_swarm { - if let libp2p_request_response::Event::Message { - peer: _, - message: libp2p_request_response::Message::Request { .. }, - } = event - { - return Poll::Pending; - } + if let Poll::Ready(ToSwarm::GenerateEvent(event)) = &poll_res { + if let libp2p_request_response::Event::Message { + peer: _, + message: libp2p_request_response::Message::Request { .. }, + } = event + { + return Poll::Pending; + } - if let libp2p_request_response::Event::InboundFailure { .. } = event { - return Poll::Pending; - } + if let libp2p_request_response::Event::InboundFailure { .. } = event { + return Poll::Pending; }; } @@ -280,7 +278,7 @@ impl NetworkBehaviour for Behaviour { let (rendezvous_node, namespace) = self .waiting_for_register .remove(&request_id) - .expect(format!("unknown request_id: {request_id}").as_str()); + .unwrap_or_else(|| panic!("unknown request_id: {request_id}")); Event::RegisterFailed(RegisterError::Remote { rendezvous_node, namespace, @@ -302,7 +300,7 @@ impl NetworkBehaviour for Behaviour { }) }); - if let Poll::Pending = &result { + if result.is_pending() { if let Some(expired_registration) = futures::ready!(self.expiring_registrations.poll_next_unpin(cx)) { @@ -346,9 +344,9 @@ impl Behaviour { RegisterResponse(result) => { let (rendezvous_node, namespace) = self .waiting_for_register - .remove(&request_id) - .expect(format!("unknown request_id: {request_id}").as_str()); - let res = match result { + .remove(request_id) + .unwrap_or_else(|| panic!("unknown request_id: {request_id}")); + match result { Ok(ttl) => Event::Registered { rendezvous_node, ttl, @@ -357,17 +355,15 @@ impl Behaviour { Err(error_code) => Event::RegisterFailed(RegisterError::Remote { rendezvous_node, namespace, - error: error_code.clone(), + error: error_code, }), - }; - - res + } } DiscoverResponse(response) => { let (rendezvous_node, ns) = self .waiting_for_discovery - .remove(&request_id) - .expect(format!("unknown request_id: {request_id}").as_str()); + .remove(request_id) + .unwrap_or_else(|| panic!("unknown request_id: {request_id}")); let res = match response { Ok((registrations, cookie)) => { self.discovered_peers diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 14e6390fb3d..3ca055d1e11 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -30,7 +30,7 @@ use std::convert::{TryFrom, TryInto}; use std::{fmt, io}; pub type Ttl = u64; -pub type Limit = u64; +pub(crate) type Limit = u64; const MAX_MESSAGE_LEN_BYTES: usize = 1024 * 1024; @@ -206,7 +206,7 @@ pub enum ErrorCode { Unavailable, } -pub struct RendezvousCodec { +pub(crate) struct RendezvousCodec { inner: quick_protobuf_codec::Codec, } @@ -243,15 +243,9 @@ impl Decoder for RendezvousCodec { } } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct Codec {} -impl Default for Codec { - fn default() -> Self { - Codec {} - } -} - #[async_trait] impl libp2p_request_response::Codec for Codec { type Protocol = StreamProtocol; diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 77f4f8f45f6..37799f652ab 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -122,13 +122,13 @@ impl NetworkBehaviour for Behaviour { fn handle_established_inbound_connection( &mut self, - _connection_id: ConnectionId, + connection_id: ConnectionId, peer: PeerId, local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { self.inner.handle_established_inbound_connection( - _connection_id, + connection_id, peer, local_addr, remote_addr, @@ -137,13 +137,13 @@ impl NetworkBehaviour for Behaviour { fn handle_established_outbound_connection( &mut self, - _connection_id: ConnectionId, + connection_id: ConnectionId, peer: PeerId, addr: &Multiaddr, role_override: Endpoint, ) -> Result, ConnectionDenied> { self.inner - .handle_established_outbound_connection(_connection_id, peer, addr, role_override) + .handle_established_outbound_connection(connection_id, peer, addr, role_override) } fn on_connection_handler_event( @@ -168,46 +168,44 @@ impl NetworkBehaviour for Behaviour { } let poll_res = self.inner.poll(cx, params); - if let Poll::Ready(to_swarm) = poll_res { - if let ToSwarm::GenerateEvent(event) = to_swarm { - let opt_event = match event { - libp2p_request_response::Event::Message { - peer: peer_id, - message: - libp2p_request_response::Message::Request { - request, channel, .. - }, - } => { - let (event, response) = - handle_request(peer_id, request, &mut self.registrations); - if let Some(resp) = response { - self.inner - .send_response(channel, resp) - .expect("Send response"); - } - - Some(event) + if let Poll::Ready(ToSwarm::GenerateEvent(event)) = poll_res { + let opt_event = match event { + libp2p_request_response::Event::Message { + peer: peer_id, + message: + libp2p_request_response::Message::Request { + request, channel, .. + }, + } => { + let (event, response) = + handle_request(peer_id, request, &mut self.registrations); + if let Some(resp) = response { + self.inner + .send_response(channel, resp) + .expect("Send response"); } - libp2p_request_response::Event::ResponseSent { .. } => None, - libp2p_request_response::Event::InboundFailure { - peer, - request_id, - error, - } => { - log::warn!("Inbound request {request_id} with peer {peer} failed: {error}"); - None - } - libp2p_request_response::Event::Message { - peer: _, - message: libp2p_request_response::Message::Response { .. }, - } => None, - libp2p_request_response::Event::OutboundFailure { .. } => None, - }; + Some(event) + } + libp2p_request_response::Event::ResponseSent { .. } => None, + libp2p_request_response::Event::InboundFailure { + peer, + request_id, + error, + } => { + log::warn!("Inbound request {request_id} with peer {peer} failed: {error}"); - if let Some(out_event) = opt_event { - return Poll::Ready(ToSwarm::GenerateEvent(out_event)); + None } + libp2p_request_response::Event::Message { + peer: _, + message: libp2p_request_response::Message::Response { .. }, + } => None, + libp2p_request_response::Event::OutboundFailure { .. } => None, + }; + + if let Some(out_event) = opt_event { + return Poll::Ready(ToSwarm::GenerateEvent(out_event)); } } @@ -284,7 +282,7 @@ fn handle_request( Ok((registrations, cookie)) => { let discovered = registrations.cloned().collect::>(); - let response = Message::DiscoverResponse(Ok((discovered.clone(), cookie.clone()))); + let response = Message::DiscoverResponse(Ok((discovered.clone(), cookie))); let event = Event::DiscoverServed { enquirer: peer_id, From 59663effa146537b4822c08c1272c1b56f4e5490 Mon Sep 17 00:00:00 2001 From: dgarus Date: Fri, 9 Jun 2023 23:25:01 +0300 Subject: [PATCH 05/26] Fixing clippy fail --- protocols/rendezvous/src/client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 5039bf4220c..980c5a791c3 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -210,13 +210,13 @@ impl NetworkBehaviour for Behaviour { fn handle_established_outbound_connection( &mut self, - _connection_id: ConnectionId, + connection_id: ConnectionId, peer: PeerId, addr: &Multiaddr, role_override: Endpoint, ) -> Result, ConnectionDenied> { self.inner - .handle_established_outbound_connection(_connection_id, peer, addr, role_override) + .handle_established_outbound_connection(connection_id, peer, addr, role_override) } fn on_connection_handler_event( From 1e738f878b8938f8eb893300c32f5a037046ae3c Mon Sep 17 00:00:00 2001 From: dgarus Date: Fri, 9 Jun 2023 23:28:48 +0300 Subject: [PATCH 06/26] Fixing clippy fail --- protocols/rendezvous/src/codec.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 3ca055d1e11..e91f487ecb9 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -654,11 +654,9 @@ mod tests { use super::*; use crate::codec::{Message, NewRegistration}; use crate::Namespace; - use async_std; use futures::AsyncWriteExt; use futures_ringbuf::Endpoint; use libp2p_core::PeerRecord; - use libp2p_identity; use libp2p_request_response::Codec; #[test] From c0751f9c5dfd8d683bac774cc1acb8e19563b4d8 Mon Sep 17 00:00:00 2001 From: Denis Garus Date: Tue, 13 Jun 2023 09:48:12 +0300 Subject: [PATCH 07/26] Update protocols/rendezvous/Cargo.toml Co-authored-by: Thomas Eizinger --- protocols/rendezvous/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 487020646c6..65caabf060d 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -39,7 +39,7 @@ libp2p-tcp = { workspace = true, features = ["tokio"] } rand = "0.8" tokio = { version = "1.28", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } libp2p-swarm-test = { workspace = true } -futures_ringbuf = "0.3.1" +futures_ringbuf = "0.4.0" async-std = { version = "1.6.2", features = ["attributes"] } # Passing arguments to the docsrs builder in order to properly document cfg's. From 4ce78c55da2ad863afb48d40c078c932c3be6000 Mon Sep 17 00:00:00 2001 From: Denis Garus Date: Tue, 13 Jun 2023 09:49:03 +0300 Subject: [PATCH 08/26] Update protocols/rendezvous/src/codec.rs Co-authored-by: Thomas Eizinger --- protocols/rendezvous/src/codec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index e91f487ecb9..6527e8c277a 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -213,7 +213,7 @@ pub(crate) struct RendezvousCodec { impl Default for RendezvousCodec { fn default() -> Self { Self { - inner: quick_protobuf_codec::Codec::new(MAX_MESSAGE_LEN_BYTES), // 1MB + inner: quick_protobuf_codec::Codec::new(MAX_MESSAGE_LEN_BYTES), } } } From 111b6409a1bb1bc5107e1e16f843555b68dc943f Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 13 Jun 2023 12:51:34 +0300 Subject: [PATCH 09/26] Fixed review comments --- Cargo.lock | 109 ++++-------------------------- protocols/rendezvous/Cargo.toml | 2 - protocols/rendezvous/src/codec.rs | 69 ++----------------- 3 files changed, 20 insertions(+), 160 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a72d79c12c..5e4a30c0f81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -668,12 +668,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cache-padded" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" - [[package]] name = "cast" version = "0.3.0" @@ -816,7 +810,7 @@ dependencies = [ "anstyle", "bitflags", "clap_lex", - "strsim 0.10.0", + "strsim", ] [[package]] @@ -1126,38 +1120,14 @@ dependencies = [ "zeroize", ] -[[package]] -name = "darling" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -dependencies = [ - "darling_core 0.10.2", - "darling_macro 0.10.2", -] - [[package]] name = "darling" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.9.3", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -1170,18 +1140,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -dependencies = [ - "darling_core 0.10.2", - "quote", + "strsim", "syn 1.0.109", ] @@ -1191,7 +1150,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core 0.14.4", + "darling_core", "quote", "syn 1.0.109", ] @@ -1299,7 +1258,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" dependencies = [ - "darling 0.14.4", + "darling", "proc-macro2", "quote", "syn 1.0.109", @@ -1744,19 +1703,6 @@ dependencies = [ "slab", ] -[[package]] -name = "futures_ringbuf" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b905098b5519bd63b2a1f9f4615198b0e38a473ce201ffdbd4dea6eb63087ddc" -dependencies = [ - "futures", - "log", - "log-derive", - "ringbuf 0.2.8", - "rustc_version", -] - [[package]] name = "futures_ringbuf" version = "0.4.0" @@ -1765,7 +1711,7 @@ checksum = "6628abb6eb1fc74beaeb20cd0670c43d158b0150f7689b38c3eaf663f99bdec7" dependencies = [ "futures", "log", - "ringbuf 0.3.3", + "ringbuf", "rustc_version", ] @@ -2505,7 +2451,7 @@ dependencies = [ "async-std", "flate2", "futures", - "futures_ringbuf 0.4.0", + "futures_ringbuf", "libp2p-core", "libp2p-tcp", "quickcheck-ext", @@ -2745,7 +2691,7 @@ version = "0.1.0" dependencies = [ "futures", "futures-timer", - "futures_ringbuf 0.4.0", + "futures_ringbuf", "libp2p-core", "log", ] @@ -2758,7 +2704,7 @@ dependencies = [ "curve25519-dalek 3.2.0", "env_logger 0.10.0", "futures", - "futures_ringbuf 0.4.0", + "futures_ringbuf", "libp2p-core", "libp2p-identity", "log", @@ -2831,7 +2777,7 @@ dependencies = [ "bytes", "env_logger 0.10.0", "futures", - "futures_ringbuf 0.4.0", + "futures_ringbuf", "libp2p-core", "libp2p-identity", "log", @@ -2920,14 +2866,12 @@ dependencies = [ name = "libp2p-rendezvous" version = "0.13.0" dependencies = [ - "async-std", "async-trait", "asynchronous-codec", "bimap", "env_logger 0.10.0", "futures", "futures-timer", - "futures_ringbuf 0.3.1", "instant", "libp2p-core", "libp2p-identify", @@ -2956,7 +2900,7 @@ dependencies = [ "async-trait", "env_logger 0.10.0", "futures", - "futures_ringbuf 0.4.0", + "futures_ringbuf", "instant", "libp2p-core", "libp2p-identity", @@ -3251,18 +3195,6 @@ dependencies = [ "value-bag", ] -[[package]] -name = "log-derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a42526bb432bcd1b43571d5f163984effa25409a29f1a3242a54d0577d55bcf" -dependencies = [ - "darling 0.10.2", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "lru" version = "0.10.0" @@ -3417,7 +3349,7 @@ dependencies = [ "bytes", "env_logger 0.10.0", "futures", - "futures_ringbuf 0.4.0", + "futures_ringbuf", "libp2p-core", "libp2p-identity", "libp2p-plaintext", @@ -4242,15 +4174,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "ringbuf" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65af18d50f789e74aaf23bbb3f65dcd22a3cb6e029b5bced149f6bd57c5c2a2" -dependencies = [ - "cache-padded", -] - [[package]] name = "ringbuf" version = "0.3.3" @@ -4789,12 +4712,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" - [[package]] name = "strsim" version = "0.10.0" diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 65caabf060d..b1cf7db044d 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -39,8 +39,6 @@ libp2p-tcp = { workspace = true, features = ["tokio"] } rand = "0.8" tokio = { version = "1.28", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } libp2p-swarm-test = { workspace = true } -futures_ringbuf = "0.4.0" -async-std = { version = "1.6.2", features = ["attributes"] } # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 6527e8c277a..d6bd2d5c395 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -256,8 +256,7 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncRead + Unpin + Send, { - let mut frame = FramedRead::new(io, RendezvousCodec::default()); - if let Some(result) = frame.next().await { + if let Some(result) = FramedRead::new(io, RendezvousCodec::default()).next().await { return Ok(result?); } @@ -289,8 +288,9 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncWrite + Unpin + Send, { - let mut framer = FramedWrite::new(io, RendezvousCodec::default()); - framer.send(req).await?; + FramedWrite::new(io, RendezvousCodec::default()) + .send(req) + .await?; Ok(()) } @@ -304,8 +304,9 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncWrite + Unpin + Send, { - let mut framer = FramedWrite::new(io, RendezvousCodec::default()); - framer.send(res).await?; + FramedWrite::new(io, RendezvousCodec::default()) + .send(res) + .await?; Ok(()) } @@ -652,12 +653,7 @@ mod proto { #[cfg(test)] mod tests { use super::*; - use crate::codec::{Message, NewRegistration}; use crate::Namespace; - use futures::AsyncWriteExt; - use futures_ringbuf::Endpoint; - use libp2p_core::PeerRecord; - use libp2p_request_response::Codec; #[test] fn cookie_wire_encoding_roundtrip() { @@ -677,55 +673,4 @@ mod tests { assert_eq!(bytes.len(), 8 + 3) } - - #[async_std::test] - async fn codec_test() { - let identity = libp2p_identity::Keypair::generate_ed25519(); - - let exp_msg = Message::Register(NewRegistration { - namespace: Namespace::from_static("wonderland"), - record: PeerRecord::new(&identity, vec!["/ip4/127.0.0.1/tcp/1234".parse().unwrap()]) - .unwrap(), - ttl: None, - }); - - let mut codec = super::Codec {}; - let protocol = &crate::PROTOCOL_IDENT; - - // Write/read request - - let (mut a, mut b) = Endpoint::pair(MAX_MESSAGE_LEN_BYTES, MAX_MESSAGE_LEN_BYTES); - - codec - .write_request(protocol, &mut a, exp_msg.clone()) - .await - .expect("Should write"); - a.close().await.unwrap(); - - let act_msg = codec - .read_request(protocol, &mut b) - .await - .expect("Should read"); - b.close().await.unwrap(); - - assert_eq!(exp_msg, act_msg); - - // Write/read response - - let (mut a, mut b) = Endpoint::pair(MAX_MESSAGE_LEN_BYTES, MAX_MESSAGE_LEN_BYTES); - - codec - .write_response(protocol, &mut a, exp_msg.clone()) - .await - .expect("Should write"); - a.close().await.unwrap(); - - let act_msg = codec - .read_response(protocol, &mut b) - .await - .expect("Should read"); - b.close().await.unwrap(); - - assert_eq!(exp_msg, act_msg); - } } From 29906357c0fe2492e56b601d97314661120fb95d Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 13 Jun 2023 12:57:50 +0300 Subject: [PATCH 10/26] Fixed review comments --- protocols/rendezvous/src/codec.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index d6bd2d5c395..2218872c0f5 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -256,11 +256,12 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncRead + Unpin + Send, { - if let Some(result) = FramedRead::new(io, RendezvousCodec::default()).next().await { - return Ok(result?); - } + let res = FramedRead::new(io, RendezvousCodec::default()) + .next() + .await + .ok_or(Err(io::ErrorKind::InvalidInput.into()))?; - Err(io::ErrorKind::InvalidInput.into()) + Ok(res?) } async fn read_response( From 3d7c5ee39769962a8545b402268cd554c2f36bdc Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 13 Jun 2023 13:25:07 +0300 Subject: [PATCH 11/26] Fixed review comments --- protocols/rendezvous/src/client.rs | 103 ++++++++++++++--------------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 980c5a791c3..30783bdc9f7 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -36,47 +36,6 @@ use std::iter; use std::task::{Context, Poll}; use std::time::Duration; -#[derive(Debug, thiserror::Error)] -pub enum RegisterError { - #[error("We don't know about any externally reachable addresses of ours")] - NoExternalAddresses, - #[error("Failed to make a new PeerRecord")] - FailedToMakeRecord(#[from] SigningError), - #[error("Failed to register with Rendezvous node")] - Remote { - rendezvous_node: PeerId, - namespace: Namespace, - error: ErrorCode, - }, -} - -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -pub enum Event { - /// We successfully discovered other nodes with using the contained rendezvous node. - Discovered { - rendezvous_node: PeerId, - registrations: Vec, - cookie: Cookie, - }, - /// We failed to discover other nodes on the contained rendezvous node. - DiscoverFailed { - rendezvous_node: PeerId, - namespace: Option, - error: ErrorCode, - }, - /// We successfully registered with the contained rendezvous node. - Registered { - rendezvous_node: PeerId, - ttl: Ttl, - namespace: Namespace, - }, - /// We failed to register with the contained rendezvous node. - RegisterFailed(RegisterError), - /// The connection details we learned from this node expired. - Expired { peer: PeerId }, -} - pub struct Behaviour { inner: libp2p_request_response::Behaviour, @@ -132,24 +91,21 @@ impl Behaviour { return; } - let opt_req_id = match PeerRecord::new(&self.keypair, external_addresses) { - Ok(peer_record) => Some(self.inner.send_request( - &rendezvous_node, - Register(NewRegistration::new(namespace.clone(), peer_record, ttl)), - )), + match PeerRecord::new(&self.keypair, external_addresses) { + Ok(peer_record) => { + self.inner.send_request( + &rendezvous_node, + Register(NewRegistration::new(namespace.clone(), peer_record, ttl)), + ); + self.waiting_for_register + .insert(req_id, (rendezvous_node, namespace)) + } Err(signing_error) => { self.error_events.push_back(Event::RegisterFailed( RegisterError::FailedToMakeRecord(signing_error), )); - - None } }; - - if let Some(req_id) = opt_req_id { - self.waiting_for_register - .insert(req_id, (rendezvous_node, namespace)); - } } /// Unregister ourselves from the given namespace with the given rendezvous peer. @@ -186,6 +142,47 @@ impl Behaviour { } } +#[derive(Debug, thiserror::Error)] +pub enum RegisterError { + #[error("We don't know about any externally reachable addresses of ours")] + NoExternalAddresses, + #[error("Failed to make a new PeerRecord")] + FailedToMakeRecord(#[from] SigningError), + #[error("Failed to register with Rendezvous node")] + Remote { + rendezvous_node: PeerId, + namespace: Namespace, + error: ErrorCode, + }, +} + +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +pub enum Event { + /// We successfully discovered other nodes with using the contained rendezvous node. + Discovered { + rendezvous_node: PeerId, + registrations: Vec, + cookie: Cookie, + }, + /// We failed to discover other nodes on the contained rendezvous node. + DiscoverFailed { + rendezvous_node: PeerId, + namespace: Option, + error: ErrorCode, + }, + /// We successfully registered with the contained rendezvous node. + Registered { + rendezvous_node: PeerId, + ttl: Ttl, + namespace: Namespace, + }, + /// We failed to register with the contained rendezvous node. + RegisterFailed(RegisterError), + /// The connection details we learned from this node expired. + Expired { peer: PeerId }, +} + impl NetworkBehaviour for Behaviour { type ConnectionHandler = Date: Tue, 13 Jun 2023 13:41:53 +0300 Subject: [PATCH 12/26] Fixed review comments --- protocols/rendezvous/src/client.rs | 22 +++------------------- protocols/rendezvous/src/codec.rs | 9 ++++----- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 30783bdc9f7..4f719b084b1 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -93,12 +93,12 @@ impl Behaviour { match PeerRecord::new(&self.keypair, external_addresses) { Ok(peer_record) => { - self.inner.send_request( + let req_id = self.inner.send_request( &rendezvous_node, Register(NewRegistration::new(namespace.clone(), peer_record, ttl)), ); self.waiting_for_register - .insert(req_id, (rendezvous_node, namespace)) + .insert(req_id, (rendezvous_node, namespace)); } Err(signing_error) => { self.error_events.push_back(Event::RegisterFailed( @@ -241,23 +241,7 @@ impl NetworkBehaviour for Behaviour { return Poll::Ready(ToSwarm::GenerateEvent(event)); } - let poll_res = self.inner.poll(cx, params); - - if let Poll::Ready(ToSwarm::GenerateEvent(event)) = &poll_res { - if let libp2p_request_response::Event::Message { - peer: _, - message: libp2p_request_response::Message::Request { .. }, - } = event - { - return Poll::Pending; - } - - if let libp2p_request_response::Event::InboundFailure { .. } = event { - return Poll::Pending; - }; - } - - let result = poll_res.map(|to_swarm| { + let result = self.inner.poll(cx, params).map(|to_swarm| { to_swarm.map_out(|event| match event { libp2p_request_response::Event::Message { peer: _, diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 2218872c0f5..d6bd2d5c395 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -256,12 +256,11 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncRead + Unpin + Send, { - let res = FramedRead::new(io, RendezvousCodec::default()) - .next() - .await - .ok_or(Err(io::ErrorKind::InvalidInput.into()))?; + if let Some(result) = FramedRead::new(io, RendezvousCodec::default()).next().await { + return Ok(result?); + } - Ok(res?) + Err(io::ErrorKind::InvalidInput.into()) } async fn read_response( From 20dd3a3a357b71d2eccd40c117948368d5e1e5f2 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 13 Jun 2023 14:17:03 +0300 Subject: [PATCH 13/26] Fixed review comments --- protocols/rendezvous/src/client.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 4f719b084b1..fccc40ffb5a 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -266,6 +266,9 @@ impl NetworkBehaviour for Behaviour { error: ErrorCode::Unavailable, }) } + // By specification, a client able only send a request (get a response) + // and can't receive any inbound request. + // That is why the next cases should be considered as unreachable. libp2p_request_response::Event::InboundFailure { .. } => { unreachable!() } From 5d0cfdcfa7f324f9d1f629eb3eac22d544bcc295 Mon Sep 17 00:00:00 2001 From: Denis Garus Date: Tue, 13 Jun 2023 17:03:16 +0300 Subject: [PATCH 14/26] Update protocols/rendezvous/src/client.rs Co-authored-by: Thomas Eizinger --- protocols/rendezvous/src/client.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index fccc40ffb5a..6206cd41d0f 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -266,20 +266,11 @@ impl NetworkBehaviour for Behaviour { error: ErrorCode::Unavailable, }) } - // By specification, a client able only send a request (get a response) - // and can't receive any inbound request. - // That is why the next cases should be considered as unreachable. - libp2p_request_response::Event::InboundFailure { .. } => { - unreachable!() - } - libp2p_request_response::Event::ResponseSent { .. } => { - unreachable!() - } - libp2p_request_response::Event::Message { + libp2p_request_response::Event::InboundFailure { .. } | libp2p_request_response::Event::ResponseSent { .. } | libp2p_request_response::Event::Message { peer: _, message: libp2p_request_response::Message::Request { .. }, } => { - unreachable!() + unreachable!("rendezvous clients never receive requests") } }) }); From 1d2716b28a2c562442ef99dcc7e009829a4d21fc Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 13 Jun 2023 16:54:31 +0200 Subject: [PATCH 15/26] Use combinator for handling EOF --- protocols/rendezvous/src/codec.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index d6bd2d5c395..610e64ba910 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -256,11 +256,12 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncRead + Unpin + Send, { - if let Some(result) = FramedRead::new(io, RendezvousCodec::default()).next().await { - return Ok(result?); - } + let message = FramedRead::new(io, RendezvousCodec::default()) + .next() + .await + .ok_or(io::ErrorKind::UnexpectedEof)??; - Err(io::ErrorKind::InvalidInput.into()) + Ok(message) } async fn read_response( @@ -271,12 +272,12 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncRead + Unpin + Send, { - let mut frame = FramedRead::new(io, RendezvousCodec::default()); - if let Some(result) = frame.next().await { - return Ok(result?); - } + let message = FramedRead::new(io, RendezvousCodec::default()) + .next() + .await + .ok_or(io::ErrorKind::UnexpectedEof)??; - Err(io::ErrorKind::InvalidInput.into()) + Ok(message) } async fn write_request( From 75f9e0fd376825b4daa3062e5dc0299e96722bee Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 13 Jun 2023 20:54:42 +0200 Subject: [PATCH 16/26] Handle OutboundFailure for register and discover requests --- protocols/rendezvous/src/client.rs | 107 ++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 34 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 6206cd41d0f..57c55c3e82b 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -19,7 +19,9 @@ // DEALINGS IN THE SOFTWARE. use crate::codec::Message::*; -use crate::codec::{Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl}; +use crate::codec::{ + Codec, Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl, +}; use futures::future::BoxFuture; use futures::future::FutureExt; use futures::stream::FuturesUnordered; @@ -237,56 +239,93 @@ impl NetworkBehaviour for Behaviour { cx: &mut Context<'_>, params: &mut impl PollParameters, ) -> Poll>> { - if let Some(event) = self.error_events.pop_front() { - return Poll::Ready(ToSwarm::GenerateEvent(event)); - } + use libp2p_request_response as req_res; + + loop { + if let Some(event) = self.error_events.pop_front() { + return Poll::Ready(ToSwarm::GenerateEvent(event)); + } - let result = self.inner.poll(cx, params).map(|to_swarm| { - to_swarm.map_out(|event| match event { - libp2p_request_response::Event::Message { - peer: _, + match self.inner.poll(cx, params) { + Poll::Ready(ToSwarm::GenerateEvent(req_res::Event::Message { message: - libp2p_request_response::Message::Response { + req_res::Message::Response { request_id, response, }, - } => self.handle_response(&request_id, response), - libp2p_request_response::Event::OutboundFailure { - peer: _, + .. + })) => { + let event = self.handle_response(&request_id, response); + + return Poll::Ready(ToSwarm::GenerateEvent(event)); + } + Poll::Ready(ToSwarm::GenerateEvent(req_res::Event::OutboundFailure { request_id, - error: _, - } => { - let (rendezvous_node, namespace) = self - .waiting_for_register - .remove(&request_id) - .unwrap_or_else(|| panic!("unknown request_id: {request_id}")); - Event::RegisterFailed(RegisterError::Remote { - rendezvous_node, - namespace, - error: ErrorCode::Unavailable, - }) + .. + })) => { + if let Some((rendezvous_node, namespace)) = + self.waiting_for_register.remove(&request_id) + { + return Poll::Ready(ToSwarm::GenerateEvent(Event::RegisterFailed( + RegisterError::Remote { + rendezvous_node, + namespace, + error: ErrorCode::Unavailable, + }, + ))); + }; + + if let Some((rendezvous_node, namespace)) = + self.waiting_for_discovery.remove(&request_id) + { + return Poll::Ready(ToSwarm::GenerateEvent(Event::DiscoverFailed { + rendezvous_node, + namespace, + error: ErrorCode::Unavailable, + })); + }; + + continue; // not a request we care about } - libp2p_request_response::Event::InboundFailure { .. } | libp2p_request_response::Event::ResponseSent { .. } | libp2p_request_response::Event::Message { - peer: _, - message: libp2p_request_response::Message::Request { .. }, - } => { + Poll::Ready(ToSwarm::GenerateEvent( + req_res::Event::InboundFailure { .. } + | req_res::Event::ResponseSent { .. } + | req_res::Event::Message { + message: req_res::Message::Request { .. }, + .. + }, + )) => { unreachable!("rendezvous clients never receive requests") } - }) - }); + Poll::Ready( + other @ (ToSwarm::ExternalAddrConfirmed(_) + | ToSwarm::ExternalAddrExpired(_) + | ToSwarm::NewExternalAddrCandidate(_) + | ToSwarm::NotifyHandler { .. } + | ToSwarm::Dial { .. } + | ToSwarm::CloseConnection { .. } + | ToSwarm::ListenOn { .. } + | ToSwarm::RemoveListener { .. }), + ) => { + let new_to_swarm = + other.map_out(|_| unreachable!("we manually map `GenerateEvent` variants")); + + return Poll::Ready(new_to_swarm); + } + Poll::Pending => {} + } - if result.is_pending() { - if let Some(expired_registration) = - futures::ready!(self.expiring_registrations.poll_next_unpin(cx)) + if let Poll::Ready(Some(expired_registration)) = + self.expiring_registrations.poll_next_unpin(cx) { self.discovered_peers.remove(&expired_registration); return Poll::Ready(ToSwarm::GenerateEvent(Event::Expired { peer: expired_registration.0, })); } - } - result + return Poll::Pending; + } } fn handle_pending_outbound_connection( From 1a615c484b4c2f1cfd0ef9c83183111dd904e0a8 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 11:46:12 +0300 Subject: [PATCH 17/26] Fixed review comments --- protocols/rendezvous/src/client.rs | 183 +++++++++++++++-------------- 1 file changed, 94 insertions(+), 89 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 57c55c3e82b..da32d94fc99 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -19,9 +19,7 @@ // DEALINGS IN THE SOFTWARE. use crate::codec::Message::*; -use crate::codec::{ - Codec, Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl, -}; +use crate::codec::{Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl}; use futures::future::BoxFuture; use futures::future::FutureExt; use futures::stream::FuturesUnordered; @@ -241,11 +239,11 @@ impl NetworkBehaviour for Behaviour { ) -> Poll>> { use libp2p_request_response as req_res; - loop { - if let Some(event) = self.error_events.pop_front() { - return Poll::Ready(ToSwarm::GenerateEvent(event)); - } + if let Some(event) = self.error_events.pop_front() { + return Poll::Ready(ToSwarm::GenerateEvent(event)); + } + loop { match self.inner.poll(cx, params) { Poll::Ready(ToSwarm::GenerateEvent(req_res::Event::Message { message: @@ -255,35 +253,19 @@ impl NetworkBehaviour for Behaviour { }, .. })) => { - let event = self.handle_response(&request_id, response); + if let Some(event) = self.handle_response(&request_id, response) { + return Poll::Ready(ToSwarm::GenerateEvent(event)); + } - return Poll::Ready(ToSwarm::GenerateEvent(event)); + continue; // not a request we care about } Poll::Ready(ToSwarm::GenerateEvent(req_res::Event::OutboundFailure { request_id, .. })) => { - if let Some((rendezvous_node, namespace)) = - self.waiting_for_register.remove(&request_id) - { - return Poll::Ready(ToSwarm::GenerateEvent(Event::RegisterFailed( - RegisterError::Remote { - rendezvous_node, - namespace, - error: ErrorCode::Unavailable, - }, - ))); - }; - - if let Some((rendezvous_node, namespace)) = - self.waiting_for_discovery.remove(&request_id) - { - return Poll::Ready(ToSwarm::GenerateEvent(Event::DiscoverFailed { - rendezvous_node, - namespace, - error: ErrorCode::Unavailable, - })); - }; + if let Some(event) = self.event_for_outbound_failure(&request_id) { + return Poll::Ready(ToSwarm::GenerateEvent(event)); + } continue; // not a request we care about } @@ -353,73 +335,96 @@ impl NetworkBehaviour for Behaviour { } impl Behaviour { - fn handle_response(&mut self, request_id: &RequestId, response: Message) -> Event { + fn event_for_outbound_failure(&mut self, req_id: &RequestId) -> Option { + if let Some((rendezvous_node, namespace)) = self.waiting_for_register.remove(req_id) { + return Some(Event::RegisterFailed(RegisterError::Remote { + rendezvous_node, + namespace, + error: ErrorCode::Unavailable, + })); + }; + + if let Some((rendezvous_node, namespace)) = self.waiting_for_discovery.remove(req_id) { + return Some(Event::DiscoverFailed { + rendezvous_node, + namespace, + error: ErrorCode::Unavailable, + }); + }; + + None + } + + fn handle_response(&mut self, request_id: &RequestId, response: Message) -> Option { match response { RegisterResponse(result) => { - let (rendezvous_node, namespace) = self - .waiting_for_register - .remove(request_id) - .unwrap_or_else(|| panic!("unknown request_id: {request_id}")); - match result { - Ok(ttl) => Event::Registered { - rendezvous_node, - ttl, - namespace, - }, - Err(error_code) => Event::RegisterFailed(RegisterError::Remote { - rendezvous_node, - namespace, - error: error_code, - }), + if let Some((rendezvous_node, namespace)) = + self.waiting_for_register.remove(request_id) + { + return Some(match result { + Ok(ttl) => Event::Registered { + rendezvous_node, + ttl, + namespace, + }, + Err(error_code) => Event::RegisterFailed(RegisterError::Remote { + rendezvous_node, + namespace, + error: error_code, + }), + }); } + + None } DiscoverResponse(response) => { - let (rendezvous_node, ns) = self - .waiting_for_discovery - .remove(request_id) - .unwrap_or_else(|| panic!("unknown request_id: {request_id}")); - let res = match response { - Ok((registrations, cookie)) => { - self.discovered_peers - .extend(registrations.iter().map(|registration| { - let peer_id = registration.record.peer_id(); - let namespace = registration.namespace.clone(); - - let addresses = registration.record.addresses().to_vec(); - - ((peer_id, namespace), addresses) - })); - - self.expiring_registrations - .extend(registrations.iter().cloned().map(|registration| { - async move { - // if the timer errors we consider it expired - futures_timer::Delay::new(Duration::from_secs( - registration.ttl, - )) - .await; - - (registration.record.peer_id(), registration.namespace) - } - .boxed() - })); - - Event::Discovered { - rendezvous_node, - registrations, - cookie, + if let Some((rendezvous_node, ns)) = self.waiting_for_discovery.remove(request_id) { + let res = match response { + Ok((registrations, cookie)) => { + self.discovered_peers.extend(registrations.iter().map( + |registration| { + let peer_id = registration.record.peer_id(); + let namespace = registration.namespace.clone(); + + let addresses = registration.record.addresses().to_vec(); + + ((peer_id, namespace), addresses) + }, + )); + + self.expiring_registrations + .extend(registrations.iter().cloned().map(|registration| { + async move { + // if the timer errors we consider it expired + futures_timer::Delay::new(Duration::from_secs( + registration.ttl, + )) + .await; + + (registration.record.peer_id(), registration.namespace) + } + .boxed() + })); + + Event::Discovered { + rendezvous_node, + registrations, + cookie, + } } - } - Err(error_code) => Event::DiscoverFailed { - rendezvous_node, - namespace: ns, - error: error_code, - }, - }; + Err(error_code) => Event::DiscoverFailed { + rendezvous_node, + namespace: ns, + error: error_code, + }, + }; + + return Some(res); + } - res + None } - _ => unreachable!(), + _ => unreachable!("rendezvous clients never receive requests"), } } } From 9a6e0dd152ce68b11859f121c4938df5666b6613 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 12:32:12 +0300 Subject: [PATCH 18/26] Fixed the server's behaviour fn poll() --- protocols/rendezvous/src/server.rs | 118 +++++++++++++++++++---------- 1 file changed, 79 insertions(+), 39 deletions(-) diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 37799f652ab..132daeddec2 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -18,11 +18,12 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::codec::{Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl}; +use crate::codec::{ + Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl, +}; use crate::{MAX_TTL, MIN_TTL}; use bimap::BiMap; use futures::future::BoxFuture; -use futures::ready; use futures::stream::FuturesUnordered; use futures::{FutureExt, StreamExt}; use libp2p_core::{Endpoint, Multiaddr}; @@ -168,44 +169,63 @@ impl NetworkBehaviour for Behaviour { } let poll_res = self.inner.poll(cx, params); - if let Poll::Ready(ToSwarm::GenerateEvent(event)) = poll_res { - let opt_event = match event { - libp2p_request_response::Event::Message { - peer: peer_id, - message: - libp2p_request_response::Message::Request { - request, channel, .. - }, - } => { - let (event, response) = - handle_request(peer_id, request, &mut self.registrations); - if let Some(resp) = response { - self.inner - .send_response(channel, resp) - .expect("Send response"); - } + if let Poll::Ready(to_swarm) = poll_res { + match to_swarm { + ToSwarm::GenerateEvent(event) => { + let opt_event = match event { + libp2p_request_response::Event::Message { + peer: peer_id, + message: + libp2p_request_response::Message::Request { + request, channel, .. + }, + } => { + let (event, response) = + handle_request(peer_id, request, &mut self.registrations); + if let Some(resp) = response { + self.inner + .send_response(channel, resp) + .expect("Send response"); + } + + Some(event) + } + libp2p_request_response::Event::ResponseSent { .. } => None, + libp2p_request_response::Event::InboundFailure { + peer, + request_id, + error, + } => { + log::warn!( + "Inbound request {request_id} with peer {peer} failed: {error}" + ); + + None + } + libp2p_request_response::Event::Message { + peer: _, + message: libp2p_request_response::Message::Response { .. }, + } => None, + libp2p_request_response::Event::OutboundFailure { .. } => None, + }; - Some(event) + if let Some(out_event) = opt_event { + return Poll::Ready(ToSwarm::GenerateEvent(out_event)); + } } - libp2p_request_response::Event::ResponseSent { .. } => None, - libp2p_request_response::Event::InboundFailure { - peer, - request_id, - error, - } => { - log::warn!("Inbound request {request_id} with peer {peer} failed: {error}"); - - None + ToSwarm::Dial { .. } + | ToSwarm::ListenOn { .. } + | ToSwarm::RemoveListener { .. } + | ToSwarm::NotifyHandler { .. } + | ToSwarm::NewExternalAddrCandidate(_) + | ToSwarm::ExternalAddrConfirmed(_) + | ToSwarm::ExternalAddrExpired(_) + | ToSwarm::CloseConnection { .. } => { + let new_to_swarm = to_swarm + .map_out(|_| unreachable!("we manually map `GenerateEvent` variants")); + + return Poll::Ready(new_to_swarm); } - libp2p_request_response::Event::Message { - peer: _, - message: libp2p_request_response::Message::Response { .. }, - } => None, - libp2p_request_response::Event::OutboundFailure { .. } => None, - }; - - if let Some(out_event) = opt_event { - return Poll::Ready(ToSwarm::GenerateEvent(out_event)); } } @@ -483,7 +503,27 @@ impl Registrations { } fn poll(&mut self, cx: &mut Context<'_>) -> Poll { - let expired_registration = ready!(self.next_expiry.poll_next_unpin(cx)).expect( + if let Poll::Ready(Some(expired_registration)) = self.next_expiry.poll_next_unpin(cx) { + // clean up our cookies + self.cookies.retain(|_, registrations| { + registrations.remove(&expired_registration); + + // retain all cookies where there are still registrations left + !registrations.is_empty() + }); + + self.registrations_for_peer + .remove_by_right(&expired_registration); + + return match self.registrations.remove(&expired_registration) { + None => self.poll(cx), + Some(registration) => Poll::Ready(ExpiredRegistration(registration)), + }; + } + + Poll::Pending + + /*let expired_registration = ready!(self.next_expiry.poll_next_unpin(cx)).expect( "This stream should never finish because it is initialised with a pending future", ); @@ -500,7 +540,7 @@ impl Registrations { match self.registrations.remove(&expired_registration) { None => self.poll(cx), Some(registration) => Poll::Ready(ExpiredRegistration(registration)), - } + }*/ } } From 15921f67a828d7d3e566c97b39fb8959934f456c Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 12:36:02 +0300 Subject: [PATCH 19/26] Fixed fmt fail --- protocols/rendezvous/src/server.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 132daeddec2..dde2fefcd6c 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -18,9 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::codec::{ - Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl, -}; +use crate::codec::{Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, Ttl}; use crate::{MAX_TTL, MIN_TTL}; use bimap::BiMap; use futures::future::BoxFuture; From eb9976484d1f809737bcc004beee9161930edb57 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 14:32:29 +0300 Subject: [PATCH 20/26] Refactoring --- protocols/rendezvous/src/client.rs | 112 +++++++++++++++-------------- 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index da32d94fc99..b4ddf02846a 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -357,69 +357,75 @@ impl Behaviour { fn handle_response(&mut self, request_id: &RequestId, response: Message) -> Option { match response { - RegisterResponse(result) => { + RegisterResponse(Ok(ttl)) => { if let Some((rendezvous_node, namespace)) = self.waiting_for_register.remove(request_id) { - return Some(match result { - Ok(ttl) => Event::Registered { - rendezvous_node, - ttl, - namespace, - }, - Err(error_code) => Event::RegisterFailed(RegisterError::Remote { - rendezvous_node, - namespace, - error: error_code, - }), + return Some(Event::Registered { + rendezvous_node, + ttl, + namespace, }); } None } - DiscoverResponse(response) => { - if let Some((rendezvous_node, ns)) = self.waiting_for_discovery.remove(request_id) { - let res = match response { - Ok((registrations, cookie)) => { - self.discovered_peers.extend(registrations.iter().map( - |registration| { - let peer_id = registration.record.peer_id(); - let namespace = registration.namespace.clone(); - - let addresses = registration.record.addresses().to_vec(); - - ((peer_id, namespace), addresses) - }, - )); - - self.expiring_registrations - .extend(registrations.iter().cloned().map(|registration| { - async move { - // if the timer errors we consider it expired - futures_timer::Delay::new(Duration::from_secs( - registration.ttl, - )) - .await; - - (registration.record.peer_id(), registration.namespace) - } - .boxed() - })); - - Event::Discovered { - rendezvous_node, - registrations, - cookie, - } - } - Err(error_code) => Event::DiscoverFailed { - rendezvous_node, - namespace: ns, - error: error_code, + RegisterResponse(Err(error_code)) => { + if let Some((rendezvous_node, namespace)) = + self.waiting_for_register.remove(request_id) + { + return Some(Event::RegisterFailed(RegisterError::Remote { + rendezvous_node, + namespace, + error: error_code, + })); + } + + None + } + DiscoverResponse(Ok((registrations, cookie))) => { + if let Some((rendezvous_node, _ns)) = self.waiting_for_discovery.remove(request_id) { + self.discovered_peers.extend(registrations.iter().map( + |registration| { + let peer_id = registration.record.peer_id(); + let namespace = registration.namespace.clone(); + + let addresses = registration.record.addresses().to_vec(); + + ((peer_id, namespace), addresses) }, - }; + )); + + self.expiring_registrations + .extend(registrations.iter().cloned().map(|registration| { + async move { + // if the timer errors we consider it expired + futures_timer::Delay::new(Duration::from_secs( + registration.ttl, + )) + .await; + + (registration.record.peer_id(), registration.namespace) + } + .boxed() + })); + + return Some(Event::Discovered { + rendezvous_node, + registrations, + cookie, + }); + } - return Some(res); + None + } + DiscoverResponse(Err(error_code)) => { + if let Some((rendezvous_node, ns)) = self.waiting_for_discovery.remove(request_id) { + return Some(Event::DiscoverFailed { + rendezvous_node, + namespace: ns, + error: error_code, + }); } None From b71b961642acd75c37b6c5c3be10b78fa435a669 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 14:45:35 +0300 Subject: [PATCH 21/26] Refactoring server --- protocols/rendezvous/src/server.rs | 81 +++++++++++++----------------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index dde2fefcd6c..d51a0be5ee2 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -166,51 +166,42 @@ impl NetworkBehaviour for Behaviour { ))); } - let poll_res = self.inner.poll(cx, params); - if let Poll::Ready(to_swarm) = poll_res { - match to_swarm { - ToSwarm::GenerateEvent(event) => { - let opt_event = match event { - libp2p_request_response::Event::Message { - peer: peer_id, - message: - libp2p_request_response::Message::Request { - request, channel, .. - }, - } => { - let (event, response) = - handle_request(peer_id, request, &mut self.registrations); - if let Some(resp) = response { - self.inner - .send_response(channel, resp) - .expect("Send response"); - } - - Some(event) - } - libp2p_request_response::Event::ResponseSent { .. } => None, - libp2p_request_response::Event::InboundFailure { - peer, - request_id, - error, - } => { - log::warn!( - "Inbound request {request_id} with peer {peer} failed: {error}" - ); - - None - } - libp2p_request_response::Event::Message { - peer: _, - message: libp2p_request_response::Message::Response { .. }, - } => None, - libp2p_request_response::Event::OutboundFailure { .. } => None, - }; - - if let Some(out_event) = opt_event { - return Poll::Ready(ToSwarm::GenerateEvent(out_event)); + if let Poll::Ready(to_swarm) = self.inner.poll(cx, params) { + return match to_swarm { + ToSwarm::GenerateEvent(libp2p_request_response::Event::Message { + peer: peer_id, + message: + libp2p_request_response::Message::Request { + request, channel, .. + }, + }) => { + let (event, response) = + handle_request(peer_id, request, &mut self.registrations); + if let Some(resp) = response { + self.inner + .send_response(channel, resp) + .expect("Send response"); } + + Poll::Ready(ToSwarm::GenerateEvent(event)) } + ToSwarm::GenerateEvent(libp2p_request_response::Event::InboundFailure { + peer, + request_id, + error, + }) => { + log::warn!("Inbound request {request_id} with peer {peer} failed: {error}"); + + Poll::Pending + } + ToSwarm::GenerateEvent(libp2p_request_response::Event::ResponseSent { .. }) + | ToSwarm::GenerateEvent(libp2p_request_response::Event::Message { + peer: _, + message: libp2p_request_response::Message::Response { .. }, + }) + | ToSwarm::GenerateEvent(libp2p_request_response::Event::OutboundFailure { + .. + }) => Poll::Pending, ToSwarm::Dial { .. } | ToSwarm::ListenOn { .. } | ToSwarm::RemoveListener { .. } @@ -222,9 +213,9 @@ impl NetworkBehaviour for Behaviour { let new_to_swarm = to_swarm .map_out(|_| unreachable!("we manually map `GenerateEvent` variants")); - return Poll::Ready(new_to_swarm); + Poll::Ready(new_to_swarm) } - } + }; } Poll::Pending From dfeb11e8bc591ad156ea5d30ff39ec1bb413c79d Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 14:51:35 +0300 Subject: [PATCH 22/26] Removed the recursion --- protocols/rendezvous/src/server.rs | 42 ++++++++++-------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index d51a0be5ee2..ae428083d6e 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -35,7 +35,7 @@ use libp2p_swarm::{ use std::collections::{HashMap, HashSet}; use std::iter; use std::iter::FromIterator; -use std::task::{Context, Poll}; +use std::task::{ready, Context, Poll}; use std::time::Duration; pub struct Behaviour { @@ -492,7 +492,11 @@ impl Registrations { } fn poll(&mut self, cx: &mut Context<'_>) -> Poll { - if let Poll::Ready(Some(expired_registration)) = self.next_expiry.poll_next_unpin(cx) { + loop { + let expired_registration = ready!(self.next_expiry.poll_next_unpin(cx)).expect( + "This stream should never finish because it is initialised with a pending future", + ); + // clean up our cookies self.cookies.retain(|_, registrations| { registrations.remove(&expired_registration); @@ -503,33 +507,15 @@ impl Registrations { self.registrations_for_peer .remove_by_right(&expired_registration); - - return match self.registrations.remove(&expired_registration) { - None => self.poll(cx), - Some(registration) => Poll::Ready(ExpiredRegistration(registration)), - }; + match self.registrations.remove(&expired_registration) { + None => { + continue; + } + Some(registration) => { + return Poll::Ready(ExpiredRegistration(registration)); + } + } } - - Poll::Pending - - /*let expired_registration = ready!(self.next_expiry.poll_next_unpin(cx)).expect( - "This stream should never finish because it is initialised with a pending future", - ); - - // clean up our cookies - self.cookies.retain(|_, registrations| { - registrations.remove(&expired_registration); - - // retain all cookies where there are still registrations left - !registrations.is_empty() - }); - - self.registrations_for_peer - .remove_by_right(&expired_registration); - match self.registrations.remove(&expired_registration) { - None => self.poll(cx), - Some(registration) => Poll::Ready(ExpiredRegistration(registration)), - }*/ } } From 0a1e48a7a01af0133f8ae5da31a38a05ff29f319 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 14:57:02 +0300 Subject: [PATCH 23/26] fn poll is wrapped into the loop --- protocols/rendezvous/src/server.rs | 104 +++++++++++++++-------------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index ae428083d6e..4ae464e642f 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -166,59 +166,65 @@ impl NetworkBehaviour for Behaviour { ))); } - if let Poll::Ready(to_swarm) = self.inner.poll(cx, params) { - return match to_swarm { - ToSwarm::GenerateEvent(libp2p_request_response::Event::Message { - peer: peer_id, - message: - libp2p_request_response::Message::Request { - request, channel, .. - }, - }) => { - let (event, response) = - handle_request(peer_id, request, &mut self.registrations); - if let Some(resp) = response { - self.inner - .send_response(channel, resp) - .expect("Send response"); + loop { + if let Poll::Ready(to_swarm) = self.inner.poll(cx, params) { + match to_swarm { + ToSwarm::GenerateEvent(libp2p_request_response::Event::Message { + peer: peer_id, + message: + libp2p_request_response::Message::Request { + request, channel, .. + }, + }) => { + let (event, response) = + handle_request(peer_id, request, &mut self.registrations); + if let Some(resp) = response { + self.inner + .send_response(channel, resp) + .expect("Send response"); + } + + return Poll::Ready(ToSwarm::GenerateEvent(event)); } + ToSwarm::GenerateEvent(libp2p_request_response::Event::InboundFailure { + peer, + request_id, + error, + }) => { + log::warn!("Inbound request {request_id} with peer {peer} failed: {error}"); - Poll::Ready(ToSwarm::GenerateEvent(event)) - } - ToSwarm::GenerateEvent(libp2p_request_response::Event::InboundFailure { - peer, - request_id, - error, - }) => { - log::warn!("Inbound request {request_id} with peer {peer} failed: {error}"); + continue; + } + ToSwarm::GenerateEvent(libp2p_request_response::Event::ResponseSent { + .. + }) + | ToSwarm::GenerateEvent(libp2p_request_response::Event::Message { + peer: _, + message: libp2p_request_response::Message::Response { .. }, + }) + | ToSwarm::GenerateEvent(libp2p_request_response::Event::OutboundFailure { + .. + }) => { + continue; + } + ToSwarm::Dial { .. } + | ToSwarm::ListenOn { .. } + | ToSwarm::RemoveListener { .. } + | ToSwarm::NotifyHandler { .. } + | ToSwarm::NewExternalAddrCandidate(_) + | ToSwarm::ExternalAddrConfirmed(_) + | ToSwarm::ExternalAddrExpired(_) + | ToSwarm::CloseConnection { .. } => { + let new_to_swarm = to_swarm + .map_out(|_| unreachable!("we manually map `GenerateEvent` variants")); + + return Poll::Ready(new_to_swarm); + } + }; + } - Poll::Pending - } - ToSwarm::GenerateEvent(libp2p_request_response::Event::ResponseSent { .. }) - | ToSwarm::GenerateEvent(libp2p_request_response::Event::Message { - peer: _, - message: libp2p_request_response::Message::Response { .. }, - }) - | ToSwarm::GenerateEvent(libp2p_request_response::Event::OutboundFailure { - .. - }) => Poll::Pending, - ToSwarm::Dial { .. } - | ToSwarm::ListenOn { .. } - | ToSwarm::RemoveListener { .. } - | ToSwarm::NotifyHandler { .. } - | ToSwarm::NewExternalAddrCandidate(_) - | ToSwarm::ExternalAddrConfirmed(_) - | ToSwarm::ExternalAddrExpired(_) - | ToSwarm::CloseConnection { .. } => { - let new_to_swarm = to_swarm - .map_out(|_| unreachable!("we manually map `GenerateEvent` variants")); - - Poll::Ready(new_to_swarm) - } - }; + return Poll::Pending; } - - Poll::Pending } fn on_swarm_event(&mut self, event: FromSwarm) { From 58e75f6a391910a64851667570ec0484ecf08033 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 16:15:33 +0300 Subject: [PATCH 24/26] Got rid of RendezvousCodec --- protocols/rendezvous/src/codec.rs | 37 +++++++++++-------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 610e64ba910..bfc3cf275fc 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -25,6 +25,7 @@ use asynchronous_codec::{FramedRead, FramedWrite}; use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; use libp2p_swarm::StreamProtocol; +use quick_protobuf_codec::Codec as ProtobufCodec; use rand::RngCore; use std::convert::{TryFrom, TryInto}; use std::{fmt, io}; @@ -206,35 +207,27 @@ pub enum ErrorCode { Unavailable, } -pub(crate) struct RendezvousCodec { - inner: quick_protobuf_codec::Codec, -} - -impl Default for RendezvousCodec { - fn default() -> Self { - Self { - inner: quick_protobuf_codec::Codec::new(MAX_MESSAGE_LEN_BYTES), - } - } -} - -impl Encoder for RendezvousCodec { +impl Encoder for Codec { type Item = Message; type Error = Error; fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { - self.inner.encode(proto::Message::from(item), dst)?; + let mut pb: ProtobufCodec = ProtobufCodec::new(MAX_MESSAGE_LEN_BYTES); + + pb.encode(proto::Message::from(item), dst)?; Ok(()) } } -impl Decoder for RendezvousCodec { +impl Decoder for Codec { type Item = Message; type Error = Error; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - let message = match self.inner.decode(src)? { + let mut pb: ProtobufCodec = ProtobufCodec::new(MAX_MESSAGE_LEN_BYTES); + + let message = match pb.decode(src)? { Some(p) => p, None => return Ok(None), }; @@ -256,7 +249,7 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncRead + Unpin + Send, { - let message = FramedRead::new(io, RendezvousCodec::default()) + let message = FramedRead::new(io, self.clone()) .next() .await .ok_or(io::ErrorKind::UnexpectedEof)??; @@ -272,7 +265,7 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncRead + Unpin + Send, { - let message = FramedRead::new(io, RendezvousCodec::default()) + let message = FramedRead::new(io, self.clone()) .next() .await .ok_or(io::ErrorKind::UnexpectedEof)??; @@ -289,9 +282,7 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncWrite + Unpin + Send, { - FramedWrite::new(io, RendezvousCodec::default()) - .send(req) - .await?; + FramedWrite::new(io, self.clone()).send(req).await?; Ok(()) } @@ -305,9 +296,7 @@ impl libp2p_request_response::Codec for Codec { where T: AsyncWrite + Unpin + Send, { - FramedWrite::new(io, RendezvousCodec::default()) - .send(res) - .await?; + FramedWrite::new(io, self.clone()).send(res).await?; Ok(()) } From ba058d589fbcdf054c2eb8a2747d9884fc679e57 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 16:16:38 +0300 Subject: [PATCH 25/26] fixed fmt --- protocols/rendezvous/src/client.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index b4ddf02846a..363e31965eb 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -384,30 +384,28 @@ impl Behaviour { None } DiscoverResponse(Ok((registrations, cookie))) => { - if let Some((rendezvous_node, _ns)) = self.waiting_for_discovery.remove(request_id) { - self.discovered_peers.extend(registrations.iter().map( - |registration| { + if let Some((rendezvous_node, _ns)) = self.waiting_for_discovery.remove(request_id) + { + self.discovered_peers + .extend(registrations.iter().map(|registration| { let peer_id = registration.record.peer_id(); let namespace = registration.namespace.clone(); let addresses = registration.record.addresses().to_vec(); ((peer_id, namespace), addresses) - }, - )); + })); self.expiring_registrations .extend(registrations.iter().cloned().map(|registration| { async move { // if the timer errors we consider it expired - futures_timer::Delay::new(Duration::from_secs( - registration.ttl, - )) + futures_timer::Delay::new(Duration::from_secs(registration.ttl)) .await; (registration.record.peer_id(), registration.namespace) } - .boxed() + .boxed() })); return Some(Event::Discovered { From d4c65d41fd2103278491dab8355df3ac8a5b6292 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 14 Jun 2023 20:25:31 +0300 Subject: [PATCH 26/26] Fixed review comment --- protocols/rendezvous/src/server.rs | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 4ae464e642f..44f2f97a6a0 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -176,15 +176,19 @@ impl NetworkBehaviour for Behaviour { request, channel, .. }, }) => { - let (event, response) = - handle_request(peer_id, request, &mut self.registrations); - if let Some(resp) = response { - self.inner - .send_response(channel, resp) - .expect("Send response"); + if let Some((event, response)) = + handle_request(peer_id, request, &mut self.registrations) + { + if let Some(resp) = response { + self.inner + .send_response(channel, resp) + .expect("Send response"); + } + + return Poll::Ready(ToSwarm::GenerateEvent(event)); } - return Poll::Ready(ToSwarm::GenerateEvent(event)); + continue; } ToSwarm::GenerateEvent(libp2p_request_response::Event::InboundFailure { peer, @@ -236,7 +240,7 @@ fn handle_request( peer_id: PeerId, message: Message, registrations: &mut Registrations, -) -> (Event, Option) { +) -> Option<(Event, Option)> { match message { Message::Register(registration) => { if registration.record.peer_id() != peer_id { @@ -248,7 +252,7 @@ fn handle_request( error, }; - return (event, Some(Message::RegisterResponse(Err(error)))); + return Some((event, Some(Message::RegisterResponse(Err(error))))); } let namespace = registration.namespace.clone(); @@ -262,7 +266,7 @@ fn handle_request( registration, }; - (event, Some(response)) + Some((event, Some(response))) } Err(TtlOutOfRange::TooLong { .. }) | Err(TtlOutOfRange::TooShort { .. }) => { let error = ErrorCode::InvalidTtl; @@ -275,7 +279,7 @@ fn handle_request( error, }; - (event, Some(response)) + Some((event, Some(response))) } } } @@ -287,7 +291,7 @@ fn handle_request( namespace, }; - (event, None) + Some((event, None)) } Message::Discover { namespace, @@ -304,7 +308,7 @@ fn handle_request( registrations: discovered, }; - (event, Some(response)) + Some((event, Some(response))) } Err(_) => { let error = ErrorCode::InvalidCookie; @@ -316,15 +320,11 @@ fn handle_request( error, }; - (event, Some(response)) + Some((event, Some(response))) } }, - Message::RegisterResponse(_) => { - unreachable!() - } - Message::DiscoverResponse(_) => { - unreachable!() - } + Message::RegisterResponse(_) => None, + Message::DiscoverResponse(_) => None, } }