From 047840d546ff54d547c63236e78d3edbe1cb8828 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 25 May 2021 00:16:25 +1000 Subject: [PATCH 001/242] Define protobuf messages and add build script to generate rust bindings --- Cargo.toml | 3 ++ protocols/rendezvous/Cargo.toml | 19 ++++++++++ protocols/rendezvous/build.rs | 24 ++++++++++++ protocols/rendezvous/src/lib.rs | 3 ++ protocols/rendezvous/src/rpc.proto | 61 ++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+) create mode 100644 protocols/rendezvous/Cargo.toml create mode 100644 protocols/rendezvous/build.rs create mode 100644 protocols/rendezvous/src/lib.rs create mode 100644 protocols/rendezvous/src/rpc.proto diff --git a/Cargo.toml b/Cargo.toml index 487249ff400..6875a7b4a9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ default = [ "pnet", "relay", "request-response", + "rendezvous", "secp256k1", "tcp-async-io", "uds", @@ -47,6 +48,7 @@ plaintext = ["libp2p-plaintext"] pnet = ["libp2p-pnet"] relay = ["libp2p-relay"] request-response = ["libp2p-request-response"] +rendezvous = ["libp2p-rendezvous"] tcp-async-io = ["libp2p-tcp", "libp2p-tcp/async-io"] tcp-tokio = ["libp2p-tcp", "libp2p-tcp/tokio"] uds = ["libp2p-uds"] @@ -76,6 +78,7 @@ libp2p-plaintext = { version = "0.28.0", path = "transports/plaintext", optional libp2p-pnet = { version = "0.21.0", path = "transports/pnet", optional = true } libp2p-relay = { version = "0.2.0", path = "protocols/relay", optional = true } libp2p-request-response = { version = "0.11.0", path = "protocols/request-response", optional = true } +libp2p-rendezvous = { version = "0.1.0", path = "protocols/rendezvous", optional = true } libp2p-swarm = { version = "0.29.0", path = "swarm" } libp2p-swarm-derive = { version = "0.23.0", path = "swarm-derive" } libp2p-uds = { version = "0.28.0", path = "transports/uds", optional = true } diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml new file mode 100644 index 00000000000..113fb268ad9 --- /dev/null +++ b/protocols/rendezvous/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "libp2p-rendezvous" +edition = "2018" +description = "Rendezvous protocol for libp2p" +version = "0.1.0" +authors = ["Comit Guys "] +license = "MIT" +repository = "https://github.com/libp2p/rust-libp2p" +keywords = ["peer-to-peer", "libp2p", "networking"] +categories = ["network-programming", "asynchronous"] + +[dependencies] +libp2p-core = { version = "0.28.0", path = "../../core" } +libp2p-swarm = { version = "0.29.0", path = "../../swarm" } +prost = "0.7" + + +[build-dependencies] +prost-build = "0.7" diff --git a/protocols/rendezvous/build.rs b/protocols/rendezvous/build.rs new file mode 100644 index 00000000000..3de5b750ca2 --- /dev/null +++ b/protocols/rendezvous/build.rs @@ -0,0 +1,24 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// 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. + +fn main() { + prost_build::compile_protos(&["src/rpc.proto"], &["src"]).unwrap(); +} + diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs new file mode 100644 index 00000000000..c94fc52852e --- /dev/null +++ b/protocols/rendezvous/src/lib.rs @@ -0,0 +1,3 @@ +mod rpc_proto { + include!(concat!(env!("OUT_DIR"), "/rendezvous.pb.rs")); +} diff --git a/protocols/rendezvous/src/rpc.proto b/protocols/rendezvous/src/rpc.proto new file mode 100644 index 00000000000..1123d8a42b6 --- /dev/null +++ b/protocols/rendezvous/src/rpc.proto @@ -0,0 +1,61 @@ +syntax = "proto2"; + +package rendezvous.pb; + +message Message { + enum MessageType { + REGISTER = 0; + REGISTER_RESPONSE = 1; + UNREGISTER = 2; + DISCOVER = 3; + DISCOVER_RESPONSE = 4; + } + + enum ResponseStatus { + OK = 0; + E_INVALID_NAMESPACE = 100; + E_INVALID_SIGNED_PEER_RECORD = 101; + E_INVALID_TTL = 102; + E_INVALID_COOKIE = 103; + E_NOT_AUTHORIZED = 200; + E_INTERNAL_ERROR = 300; + E_UNAVAILABLE = 400; + } + + message Register { + optional string ns = 1; + optional bytes signedPeerRecord = 2; + optional int64 ttl = 3; // in seconds + } + + message RegisterResponse { + optional ResponseStatus status = 1; + optional string statusText = 2; + optional int64 ttl = 3; // in seconds + } + + message Unregister { + optional string ns = 1; + optional bytes id = 2; + } + + message Discover { + optional string ns = 1; + optional int64 limit = 2; + optional bytes cookie = 3; + } + + message DiscoverResponse { + repeated Register registrations = 1; + optional bytes cookie = 2; + optional ResponseStatus status = 3; + optional string statusText = 4; + } + + optional MessageType type = 1; + optional Register register = 2; + optional RegisterResponse registerResponse = 3; + optional Unregister unregister = 4; + optional Discover discover = 5; + optional DiscoverResponse discoverResponse = 6; +} \ No newline at end of file From 76f57bdf08f7ec0d9f13abacd6f7012082a0d364 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 25 May 2021 11:55:39 +1000 Subject: [PATCH 002/242] WIP: --- protocols/rendezvous/Cargo.toml | 2 + protocols/rendezvous/src/handler.rs | 145 +++++++++++++++++++++++ protocols/rendezvous/src/lib.rs | 72 ++++++++++- protocols/rendezvous/src/protocol.rs | 74 ++++++++++++ protocols/rendezvous/tests/rendezvous.rs | 2 + 5 files changed, 292 insertions(+), 3 deletions(-) create mode 100644 protocols/rendezvous/src/handler.rs create mode 100644 protocols/rendezvous/src/protocol.rs create mode 100644 protocols/rendezvous/tests/rendezvous.rs diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 113fb268ad9..0e9a34659e5 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -10,8 +10,10 @@ keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] [dependencies] +asynchronous-codec = "0.6" libp2p-core = { version = "0.28.0", path = "../../core" } libp2p-swarm = { version = "0.29.0", path = "../../swarm" } +libp2p-request-response = { path = "../../protocols/request-response" } prost = "0.7" diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs new file mode 100644 index 00000000000..169c6ffc980 --- /dev/null +++ b/protocols/rendezvous/src/handler.rs @@ -0,0 +1,145 @@ +use crate::protocol; +use libp2p_core::{InboundUpgrade, OutboundUpgrade}; +use libp2p_swarm::{ + KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, + ProtocolsHandlerUpgrErr, SubstreamProtocol, +}; +use std::error::Error; +use std::fmt; +use std::task::{Context, Poll}; + +#[derive(Debug)] +pub struct HandlerEvent(protocol::Message); + +pub struct RendezvousHandler; + +impl RendezvousHandler { + pub fn new() -> Self { + Self + } +} + +/// The result of an inbound or outbound ping. +pub type RendezvousResult = Result; + +/// The successful result of processing an inbound or outbound ping. +#[derive(Debug)] +pub enum RendezvousSuccess {} + +/// An outbound ping failure. +#[derive(Debug)] +pub enum RendezvousFailure { + Other { + error: Box, + }, +} + +impl fmt::Display for RendezvousFailure { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RendezvousFailureFailure::Other { error } => write!(f, "Rendezvous error: {}", error), + } + } +} + +impl Error for RendezvousFailure { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + RendezvousFailure::Other { error } => Some(&**error), + } + } +} + +impl ProtocolsHandler for RendezvousHandler { + type InEvent = (); + type OutEvent = RendezvousResult; + type Error = RendezvousFailure; + type InboundProtocol = protocol::Rendezvous; + type OutboundProtocol = protocol::Rendezvous; + type OutboundOpenInfo = (); + type InboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + todo!() + } + + fn inject_fully_negotiated_inbound( + &mut self, + substream: >::Output, + _info: Self::InboundOpenInfo, + ) { + // If the peer doesn't support the protocol, reject all substreams + if self.protocol_unsupported { + return; + } + + self.inbound_substreams_created += 1; + + // update the known kind of peer + if self.peer_kind.is_none() { + self.peer_kind = Some(peer_kind); + } + + // new inbound substream. Replace the current one, if it exists. + trace!("New inbound substream request"); + self.inbound_substream = Some(InboundSubstreamState::WaitingInput(substream)); + } + + fn inject_fully_negotiated_outbound( + &mut self, + substream: >::Output, + message: Self::OutboundOpenInfo, + ) { + if self.outbound_substream.is_some() { + warn!("Established an outbound substream with one already available"); + // Add the message back to the send queue + self.send_queue.push(message); + } else { + self.outbound_substream = Some(OutboundSubstreamState::PendingSend(substream, message)); + } + } + + fn inject_event(&mut self, message: GossipsubHandlerIn) { + if !self.protocol_unsupported { + match message { + GossipsubHandlerIn::Message(m) => self.send_queue.push(m), + // If we have joined the mesh, keep the connection alive. + GossipsubHandlerIn::JoinedMesh => { + self.in_mesh = true; + self.keep_alive = KeepAlive::Yes; + } + // If we have left the mesh, start the idle timer. + GossipsubHandlerIn::LeftMesh => { + self.in_mesh = false; + self.keep_alive = KeepAlive::Until(Instant::now() + self.idle_timeout); + } + } + } + } + + fn inject_dial_upgrade_error( + &mut self, + info: Self::OutboundOpenInfo, + error: ProtocolsHandlerUpgrErr<()>, + ) { + todo!() + } + + fn connection_keep_alive(&self) -> KeepAlive { + todo!() + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ProtocolsHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + Self::OutEvent, + Self::Error, + >, + > { + todo!() + } +} diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index c94fc52852e..a0ea2cfb8ce 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,3 +1,69 @@ -mod rpc_proto { - include!(concat!(env!("OUT_DIR"), "/rendezvous.pb.rs")); -} +mod handler; +mod protocol; + +use libp2p_core::connection::ConnectionId; +use libp2p_core::{Multiaddr, PeerId}; +use libp2p_swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}; +use std::collections::VecDeque; +use std::task::{Context, Poll}; + +// +// pub struct Rendezvous { +// #[behaviour(ignore)] +// events: VecDeque, +// } +// +// /// Event generated by the `Ping` network behaviour. +// #[derive(Debug)] +// pub struct RendezvousEvent { +// /// The peer ID of the remote. +// pub peer: PeerId, +// } +// +// impl Rendezvous { +// /// Creates a new `Ping` network behaviour with the given configuration. +// pub fn new() -> Self { +// Self { +// events: VecDeque::new(), +// } +// } +// } +// +// impl Default for Rendezvous { +// fn default() -> Self { +// Rendezvous::new() +// } +// } +// +// impl NetworkBehaviour for Rendezvous { +// type ProtocolsHandler = RendezvousHandler; +// type OutEvent = RendezvousEvent; +// +// fn new_handler(&mut self) -> Self::ProtocolsHandler { +// RendezvousHandler::new() +// } +// +// fn addresses_of_peer(&mut self, _peer_id: &PeerId) -> Vec { +// Vec::new() +// } +// +// fn inject_connected(&mut self, _: &PeerId) {} +// +// fn inject_disconnected(&mut self, _: &PeerId) {} +// +// fn inject_event(&mut self, peer: PeerId, _: ConnectionId, result: PingResult) { +// self.events.push_front(RendezvousEvent { peer }) +// } +// +// fn poll( +// &mut self, +// _: &mut Context<'_>, +// _: &mut impl PollParameters, +// ) -> Poll> { +// if let Some(e) = self.events.pop_back() { +// Poll::Ready(NetworkBehaviourAction::GenerateEvent(e)) +// } else { +// Poll::Pending +// } +// } +// } diff --git a/protocols/rendezvous/src/protocol.rs b/protocols/rendezvous/src/protocol.rs new file mode 100644 index 00000000000..51fd99525b1 --- /dev/null +++ b/protocols/rendezvous/src/protocol.rs @@ -0,0 +1,74 @@ +use asynchronous_codec::{BytesMut, Decoder, Encoder, Framed}; +use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; +use libp2p_swarm::NegotiatedSubstream; +use std::io::Error; +use std::{future, iter}; +use void::Void; + +#[derive(Default, Debug, Copy, Clone)] +pub struct Rendezvous; + +impl Rendezvous { + pub fn new() -> Rendezvous { + Rendezvous + } +} + +impl UpgradeInfo for Rendezvous { + type Info = &'static [u8]; + type InfoIter = iter::Once; + + fn protocol_info(&self) -> Self::InfoIter { + iter::once(b"/rendezvous/1.0.0") + } +} + +impl InboundUpgrade for Rendezvous { + type Output = Framed; + type Error = Void; + type Future = future::Ready>; + + fn upgrade_inbound(self, socket: TSocket, protocol_id: Self::Info) -> Self::Future { + future::ok(Framed::new(socket, RendezvousCodec)) + } +} + +impl OutboundUpgrade for Rendezvous { + type Output = Framed; + type Error = Void; + type Future = future::Ready>; + + fn upgrade_outbound(self, socket: TSocket, protocol_id: Self::Info) -> Self::Future { + future::ok(Framed::new(socket, RendezvousCodec)) + } +} + +/// The event emitted by the Handler. This informs the behaviour of various events created +/// by the handler. +#[derive(Debug)] +pub enum Message { + RegisterReq, + DiscoverReq, + RegisterResp, + DiscoverResp, +} + +struct RendezvousCodec; + +impl Encoder for RendezvousCodec { + type Item = Message; + type Error = std::io::Error; +} + +impl Decoder for RendezvousCodec { + type Item = Message; + type Error = std::io::Error; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + todo!("decode src and return it") + } +} + +mod wire { + include!(concat!(env!("OUT_DIR"), "/rendezvous.pb.rs")); +} diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs new file mode 100644 index 00000000000..8490513213d --- /dev/null +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -0,0 +1,2 @@ +#[test] +fn rendesvous() {} From aed2f955beb5210fe946e439b3c1e09d145c656a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 25 May 2021 12:28:35 +1000 Subject: [PATCH 003/242] Add crate to workspace members --- Cargo.toml | 1 + protocols/rendezvous/Cargo.toml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6875a7b4a9e..74d32f74018 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,6 +113,7 @@ members = [ "muxers/yamux", "protocols/floodsub", "protocols/gossipsub", + "protocols/rendezvous", "protocols/identify", "protocols/kad", "protocols/mdns", diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 0e9a34659e5..2c755c1fa64 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -16,6 +16,5 @@ libp2p-swarm = { version = "0.29.0", path = "../../swarm" } libp2p-request-response = { path = "../../protocols/request-response" } prost = "0.7" - [build-dependencies] prost-build = "0.7" From 61b73430c719852eeab8c98f4248a3e5fe2fbe8f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 25 May 2021 16:10:25 +1000 Subject: [PATCH 004/242] WIP --- core/src/network/event.rs | 2 +- protocols/rendezvous/Cargo.toml | 6 +- protocols/rendezvous/build.rs | 20 ----- protocols/rendezvous/src/codec.rs | 114 ++++++++++++++++++++++++ protocols/rendezvous/src/handler.rs | 125 +++++++++++---------------- protocols/rendezvous/src/lib.rs | 1 + protocols/rendezvous/src/protocol.rs | 73 +++++++++------- 7 files changed, 213 insertions(+), 128 deletions(-) create mode 100644 protocols/rendezvous/src/codec.rs diff --git a/core/src/network/event.rs b/core/src/network/event.rs index 8154bd2087d..285a7e33828 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -116,7 +116,7 @@ where /// A connection may close if /// /// * it encounters an error, which includes the connection being - /// closed by the remote. In this case `error` is `Some`. + /// closed by the remote. In this case `error` is `ome`. /// * it was actively closed by [`EstablishedConnection::start_close`], /// i.e. a successful, orderly close. In this case `error` is `None`. /// * it was actively closed by [`super::peer::ConnectedPeer::disconnect`] or diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 2c755c1fa64..dfcf3640cf4 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -13,8 +13,12 @@ categories = ["network-programming", "asynchronous"] asynchronous-codec = "0.6" libp2p-core = { version = "0.28.0", path = "../../core" } libp2p-swarm = { version = "0.29.0", path = "../../swarm" } -libp2p-request-response = { path = "../../protocols/request-response" } prost = "0.7" +void = "1" +log = "0.4" +futures = { version = "0.3", default-features = false, features = ["std"] } +thiserror = "1" # TODO: REMOVE FOR PRODUCTION USE +unsigned-varint = "0.7" [build-dependencies] prost-build = "0.7" diff --git a/protocols/rendezvous/build.rs b/protocols/rendezvous/build.rs index 3de5b750ca2..2c596bd8249 100644 --- a/protocols/rendezvous/build.rs +++ b/protocols/rendezvous/build.rs @@ -1,23 +1,3 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// -// 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. - fn main() { prost_build::compile_protos(&["src/rpc.proto"], &["src"]).unwrap(); } diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs new file mode 100644 index 00000000000..91acdb8dba6 --- /dev/null +++ b/protocols/rendezvous/src/codec.rs @@ -0,0 +1,114 @@ +use asynchronous_codec::{Encoder, BytesMut, Decoder, Bytes}; +use unsigned_varint::codec::UviBytes; + +/// The event emitted by the Handler. This informs the behaviour of various events created +/// by the handler. +#[derive(Debug)] +pub enum Message { + Register { + namespace: String + }, + Discover { + namespace: Option + }, + SuccessfullyRegistered { + ttl: i64 + }, + FailedtoRegister { + error: ErrorCode, + }, + DiscoverResp, +} + +#[derive(Debug)] +pub enum ErrorCode { + InvalidNamespace, + InvalidSignedPeerRecord, + InvalidTtl, + InvalidCookie, + NotAuthorized, + InternalError, + Unavailable, +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Failed to encode message as bytes")] + Encode(#[from] prost::EncodeError), + #[error("Failed to decode message from bytes")] + Decode(#[from] prost::DecodeError), + #[error("Failed to read/write")] // TODO: Better message + Io(#[from] std::io::Error) +} + +impl From for wire::Message { + fn from(_: Message) -> Self { + todo!() + } +} + +impl From for Message { + fn from(_: wire::Message) -> Self { + todo!() + } +} + +pub struct RendezvousCodec { + /// Codec to encode/decode the Unsigned varint length prefix of the frames. + length_codec: UviBytes, +} + +impl Default for RendezvousCodec { + fn default() -> Self { + let mut length_codec = UviBytes::default(); + length_codec.set_max_len(1024 * 1024); // 1MB TODO clarify with spec what the default should be + + Self { + length_codec + } + } +} + +impl Encoder for RendezvousCodec { + type Item = Message; + type Error = Error; + + fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { + use prost::Message; + + let message = wire::Message::from(item); + + let mut buf = Vec::with_capacity(message.encoded_len()); + + message.encode(&mut buf) + .expect("Buffer has sufficient capacity"); + + // length prefix the protobuf message, ensuring the max limit is not hit + self.length_codec + .encode(Bytes::from(buf), dst)?; + + Ok(()) + } +} + +impl Decoder for RendezvousCodec { + type Item = Message; + type Error = Error; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + use prost::Message; + + let message = match self.length_codec.decode(src)? { + Some(p) => p, + None => return Ok(None), + }; + + let message = wire::Message::decode(message)?; + + Ok(Some(message.into())) + } +} + +mod wire { + include!(concat!(env!("OUT_DIR"), "/rendezvous.pb.rs")); +} diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 169c6ffc980..7a4ba104dd9 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,5 +1,5 @@ use crate::protocol; -use libp2p_core::{InboundUpgrade, OutboundUpgrade}; +use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, @@ -7,60 +7,61 @@ use libp2p_swarm::{ use std::error::Error; use std::fmt; use std::task::{Context, Poll}; +use std::time::Instant; +use asynchronous_codec::Framed; +use crate::codec::RendezvousCodec; +use crate::codec::Message; +use crate::protocol::{InboundStream, DiscoverResponse}; +use std::collections::VecDeque; -#[derive(Debug)] -pub struct HandlerEvent(protocol::Message); +pub struct RendezvousHandler{ + /// Upgrade configuration for the rendezvous protocol. + listen_protocol: SubstreamProtocol, -pub struct RendezvousHandler; -impl RendezvousHandler { - pub fn new() -> Self { - Self - } -} -/// The result of an inbound or outbound ping. -pub type RendezvousResult = Result; + in_events: VecDeque, -/// The successful result of processing an inbound or outbound ping. -#[derive(Debug)] -pub enum RendezvousSuccess {} - -/// An outbound ping failure. -#[derive(Debug)] -pub enum RendezvousFailure { - Other { - error: Box, - }, + /// Queue of values that we want to send to the remote. + out_events: VecDeque, } -impl fmt::Display for RendezvousFailure { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RendezvousFailureFailure::Other { error } => write!(f, "Rendezvous error: {}", error), +impl RendezvousHandler { + pub fn new() -> Self { + Self { + listen_protocol: SubstreamProtocol::new(Default::default(), ()), + in_events: VecDeque::new(), + out_events: VecDeque::new() } } } -impl Error for RendezvousFailure { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - RendezvousFailure::Other { error } => Some(&**error), - } - } +#[derive(Debug)] +pub enum RendezvousHandlerOut { + DiscoverResponse(DiscoverResponse), + RegisterResponse, + RegisterRequest, + DiscoverRequest, +} + +#[derive(Debug, Clone)] +pub enum RendezvousHandlerIn { + DiscoverRequest, + RegisterRequest, } -impl ProtocolsHandler for RendezvousHandler { - type InEvent = (); - type OutEvent = RendezvousResult; - type Error = RendezvousFailure; +impl ProtocolsHandler for RendezvousHandler { + type InEvent = RendezvousHandlerIn; + type OutEvent = RendezvousHandlerOut; + type Error = (); + type InboundOpenInfo = (); type InboundProtocol = protocol::Rendezvous; - type OutboundProtocol = protocol::Rendezvous; type OutboundOpenInfo = (); - type InboundOpenInfo = (); + type OutboundProtocol = protocol::Rendezvous; fn listen_protocol(&self) -> SubstreamProtocol { - todo!() + let rendezvous_protocol = crate::protocol::Rendezvous::new(); + SubstreamProtocol::new(rendezvous_protocol, ()) } fn inject_fully_negotiated_inbound( @@ -68,53 +69,25 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, _info: Self::InboundOpenInfo, ) { - // If the peer doesn't support the protocol, reject all substreams - if self.protocol_unsupported { - return; - } - - self.inbound_substreams_created += 1; - - // update the known kind of peer - if self.peer_kind.is_none() { - self.peer_kind = Some(peer_kind); + match substream { + InboundStream::Discover => self.out_events.push_back(RendezvousHandlerOut::DiscoverRequest), + InboundStream::Register(a) => self.out_events.push_back(RendezvousHandlerOut::RegisterRequest) } - - // new inbound substream. Replace the current one, if it exists. - trace!("New inbound substream request"); - self.inbound_substream = Some(InboundSubstreamState::WaitingInput(substream)); } fn inject_fully_negotiated_outbound( &mut self, substream: >::Output, - message: Self::OutboundOpenInfo, + _info: Self::OutboundOpenInfo, ) { - if self.outbound_substream.is_some() { - warn!("Established an outbound substream with one already available"); - // Add the message back to the send queue - self.send_queue.push(message); - } else { - self.outbound_substream = Some(OutboundSubstreamState::PendingSend(substream, message)); + match substream { + } + self.out_events.push_back(RendezvousHandlerOut::DiscoverResponse(substream)); } - fn inject_event(&mut self, message: GossipsubHandlerIn) { - if !self.protocol_unsupported { - match message { - GossipsubHandlerIn::Message(m) => self.send_queue.push(m), - // If we have joined the mesh, keep the connection alive. - GossipsubHandlerIn::JoinedMesh => { - self.in_mesh = true; - self.keep_alive = KeepAlive::Yes; - } - // If we have left the mesh, start the idle timer. - GossipsubHandlerIn::LeftMesh => { - self.in_mesh = false; - self.keep_alive = KeepAlive::Until(Instant::now() + self.idle_timeout); - } - } - } + fn inject_event(&mut self, message: RendezvousHandlerIn) { + RendezvousHandlerIn::Message(m) => self.out_events.push(m) } fn inject_dial_upgrade_error( @@ -140,6 +113,6 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { - todo!() + Poll::Ready(ProtocolsHandlerEvent::Custom(RendezvousResult::Ok(()))) } } diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index a0ea2cfb8ce..417a49f0c62 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,5 +1,6 @@ mod handler; mod protocol; +mod codec; use libp2p_core::connection::ConnectionId; use libp2p_core::{Multiaddr, PeerId}; diff --git a/protocols/rendezvous/src/protocol.rs b/protocols/rendezvous/src/protocol.rs index 51fd99525b1..229fdd1c318 100644 --- a/protocols/rendezvous/src/protocol.rs +++ b/protocols/rendezvous/src/protocol.rs @@ -1,9 +1,11 @@ use asynchronous_codec::{BytesMut, Decoder, Encoder, Framed}; use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; -use libp2p_swarm::NegotiatedSubstream; -use std::io::Error; use std::{future, iter}; use void::Void; +use futures::AsyncRead; +use futures::AsyncWrite; +use crate::codec::RendezvousCodec; +use crate::codec; #[derive(Default, Debug, Copy, Clone)] pub struct Rendezvous; @@ -23,52 +25,63 @@ impl UpgradeInfo for Rendezvous { } } -impl InboundUpgrade for Rendezvous { - type Output = Framed; +impl InboundUpgrade for Rendezvous { + type Output = InboundStream; type Error = Void; type Future = future::Ready>; - fn upgrade_inbound(self, socket: TSocket, protocol_id: Self::Info) -> Self::Future { - future::ok(Framed::new(socket, RendezvousCodec)) + fn upgrade_inbound(self, socket: TSocket, _: Self::Info) -> Self::Future { + future::ready(todo!()) } } -impl OutboundUpgrade for Rendezvous { - type Output = Framed; +impl OutboundUpgrade for Rendezvous { + //type Output = Framed; + type Output = DiscoverResponse; type Error = Void; type Future = future::Ready>; - fn upgrade_outbound(self, socket: TSocket, protocol_id: Self::Info) -> Self::Future { - future::ok(Framed::new(socket, RendezvousCodec)) + fn upgrade_outbound(self, socket: TSocket, _: Self::Info) -> Self::Future { + //future::ready(Ok(Framed::new(socket, RendezvousCodec))) + future::ready(todo!()) } } -/// The event emitted by the Handler. This informs the behaviour of various events created -/// by the handler. -#[derive(Debug)] -pub enum Message { - RegisterReq, - DiscoverReq, - RegisterResp, - DiscoverResp, +pub enum InboundStream { + Register(RegisterRequest), + Discover } -struct RendezvousCodec; - -impl Encoder for RendezvousCodec { - type Item = Message; - type Error = std::io::Error; +pub enum Outbound { + Register(RegisterRequest), + Discover } -impl Decoder for RendezvousCodec { - type Item = Message; - type Error = std::io::Error; +pub struct RegisterRequest { + +} - fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - todo!("decode src and return it") +impl RegisterRequest { + pub fn respond_with(self, response: RegisterResponse) -> RegisterResponseFuture { + todo!() } } -mod wire { - include!(concat!(env!("OUT_DIR"), "/rendezvous.pb.rs")); +pub struct DiscoverRequest { + +} + +pub enum DiscoverResponse { + Registrations, + Error, +} + +pub enum RegisterResponse { + Ok, + Error +} + +pub struct RegisterResponseFuture { + stream: Framed, + message: codec::Message } From 88d6fcadd1f24fbf846a6661d68dcf2f35d57cb9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 25 May 2021 17:57:58 +1000 Subject: [PATCH 005/242] Add mapping between protobuf and application types --- protocols/rendezvous/src/codec.rs | 258 ++++++++++++++++++++++++++++-- 1 file changed, 243 insertions(+), 15 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 91acdb8dba6..fbe19fe3b06 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -1,23 +1,43 @@ use asynchronous_codec::{Encoder, BytesMut, Decoder, Bytes}; use unsigned_varint::codec::UviBytes; +use std::convert::{TryFrom, TryInto}; -/// The event emitted by the Handler. This informs the behaviour of various events created -/// by the handler. #[derive(Debug)] pub enum Message { Register { - namespace: String - }, - Discover { - namespace: Option + namespace: String, + ttl: Option, + // TODO: Signed peer record field }, SuccessfullyRegistered { ttl: i64 }, - FailedtoRegister { + FailedToRegister { error: ErrorCode, }, - DiscoverResp, + Unregister { + namespace: String, + // TODO: what is the `id` field here in the PB message + }, + Discover { + namespace: Option, + // TODO limit: Option + // TODO cookie: Option + }, + DiscoverResponse { + registrations: Vec, + // TODO cookie: Option + }, + FailedToDiscover { + error: ErrorCode + }, +} + +#[derive(Debug)] +pub struct Registration { + namespace: String, + // ttl: i64, TODO: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds + // TODO: Signed peer record } #[derive(Debug)] @@ -38,21 +58,229 @@ pub enum Error { #[error("Failed to decode message from bytes")] Decode(#[from] prost::DecodeError), #[error("Failed to read/write")] // TODO: Better message - Io(#[from] std::io::Error) + Io(#[from] std::io::Error), + #[error("Failed to convert wire message to internal data model")] + ConversionError(#[from] ConversionError) } impl From for wire::Message { - fn from(_: Message) -> Self { - todo!() + fn from(message: Message) -> Self { + use wire::message::*; + + match message { + Message::Register { namespace, ttl } => wire::Message { + r#type: Some(MessageType::Register.into()), + register: Some(Register { + ns: Some(namespace), + ttl, + signed_peer_record: None + }), + register_response: None, + unregister: None, + discover: None, + discover_response: None + }, + Message::SuccessfullyRegistered { ttl } => wire::Message { + r#type: Some(MessageType::RegisterResponse.into()), + register_response: Some(RegisterResponse { + status: Some(ResponseStatus::Ok.into()), + status_text: None, + ttl: Some(ttl) + }), + register: None, + discover: None, + unregister: None, + discover_response: None + }, + Message::FailedToRegister { error } => wire::Message { + r#type: Some(MessageType::RegisterResponse.into()), + register_response: Some(RegisterResponse { + status: Some(ResponseStatus::from(error).into()), + status_text: None, + ttl: None + }), + register: None, + discover: None, + unregister: None, + discover_response: None + }, + Message::Unregister { namespace } => wire::Message { + r#type: Some(MessageType::Unregister.into()), + unregister: Some(Unregister { + ns: Some(namespace), + id: None + }), + register: None, + register_response: None, + discover: None, + discover_response: None + }, + Message::Discover { namespace } => wire::Message { + r#type: Some(MessageType::Discover.into()), + discover: Some(Discover { + ns: namespace, + cookie: None, + limit: None + }), + register: None, + register_response: None, + unregister: None, + discover_response: None + }, + Message::DiscoverResponse { registrations } => wire::Message { + r#type: Some(MessageType::DiscoverResponse.into()), + discover_response: Some(DiscoverResponse { + registrations: registrations.into_iter().map(|reggo| Register { + ns: Some(reggo.namespace), + ttl: None, + signed_peer_record: None + }).collect(), + status: Some(ResponseStatus::Ok.into()), + status_text: None, + cookie: None + }), + register: None, + discover: None, + unregister: None, + register_response: None + }, + Message::FailedToDiscover { error } => wire::Message { + r#type: Some(MessageType::DiscoverResponse.into()), + discover_response: Some(DiscoverResponse { + registrations: Vec::new(), + status: Some(ResponseStatus::from(error).into()), + status_text: None, + cookie: None + }), + register: None, + discover: None, + unregister: None, + register_response: None + }, + } } } -impl From for Message { - fn from(_: wire::Message) -> Self { - todo!() +impl TryFrom for Message { + type Error = ConversionError; + + fn try_from(message: wire::Message) -> Result { + use wire::message::*; + + let message = match message { + wire::Message { r#type: Some(0), register: Some(Register { ns, ttl, .. }), .. } => { + Message::Register { + namespace: ns.ok_or(ConversionError::MissingNamespace)?, + ttl + } + }, + wire::Message { r#type: Some(1), register_response: Some(RegisterResponse { status: Some(0), ttl, .. }), .. } => { + Message::SuccessfullyRegistered { + ttl: ttl.ok_or(ConversionError::MissingTtl)? + } + }, + wire::Message { r#type: Some(1), register_response: Some(RegisterResponse { status: Some(0), ttl, .. }), .. } => { + Message::SuccessfullyRegistered { + ttl: ttl.ok_or(ConversionError::MissingTtl)? + } + }, + wire::Message { r#type: Some(1), register_response: Some(RegisterResponse { status: Some(error_code), .. }), .. } => { + Message::FailedToRegister { + error: wire::message::ResponseStatus::from_i32(error_code).ok_or(ConversionError::BadStatusCode)?.try_into()? + } + }, + wire::Message { r#type: Some(2), unregister: Some(Unregister { ns, .. }), .. } => { + Message::Unregister { + namespace: ns.ok_or(ConversionError::MissingNamespace)? + } + }, + wire::Message { r#type: Some(3), discover: Some(Discover { ns, .. }), .. } => { + Message::Discover { + namespace: ns + } + }, + wire::Message { r#type: Some(4), discover_response: Some(DiscoverResponse { registrations, status: Some(0), .. }), .. } => { + Message::DiscoverResponse { + registrations: registrations.into_iter().map(|reggo| Ok(Registration { + namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)? + })).collect::, ConversionError>>()?, + } + }, + wire::Message { r#type: Some(4), discover_response: Some(DiscoverResponse { status: Some(error_code), .. }), .. } => { + let response_status = unsafe { + std::mem::transmute::<_, wire::message::ResponseStatus>(error_code) + }; + + Message::FailedToDiscover { + error: response_status.try_into()? + } + }, + _ => return Err(ConversionError::InconsistentWireMessage) + }; + + Ok(message) } } +#[derive(Debug, thiserror::Error)] +pub enum ConversionError { + #[error("The wire message is consistent")] + InconsistentWireMessage, + #[error("Missing namespace field")] + MissingNamespace, + #[error("Missing TTL field")] + MissingTtl, + #[error("Bad status code")] + BadStatusCode, +} + +impl TryFrom for ErrorCode { + type Error = NotAnError; + + fn try_from(value: wire::message::ResponseStatus) -> Result { + use wire::message::ResponseStatus::*; + + let code = match value { + Ok => return Err(NotAnError), + EInvalidNamespace => ErrorCode::InvalidNamespace, + EInvalidSignedPeerRecord => ErrorCode::InvalidSignedPeerRecord, + EInvalidTtl => ErrorCode::InvalidTtl, + EInvalidCookie => ErrorCode::InvalidCookie, + ENotAuthorized => ErrorCode::NotAuthorized, + EInternalError => ErrorCode::InternalError, + EUnavailable => ErrorCode::Unavailable, + }; + + Result::Ok(code) + } +} + +impl From for wire::message::ResponseStatus { + fn from(error_code: ErrorCode) -> Self { + use wire::message::ResponseStatus::*; + + match error_code { + ErrorCode::InvalidNamespace => EInvalidNamespace, + ErrorCode::InvalidSignedPeerRecord => EInvalidSignedPeerRecord, + ErrorCode::InvalidTtl => EInvalidTtl, + ErrorCode::InvalidCookie => EInvalidCookie, + ErrorCode::NotAuthorized => ENotAuthorized, + ErrorCode::InternalError => EInternalError, + ErrorCode::Unavailable => EUnavailable, + } + } +} + +impl From for ConversionError { + fn from(_: NotAnError) -> Self { + ConversionError::InconsistentWireMessage + } +} + +#[derive(Debug, thiserror::Error)] +#[error("The provided response code is not an error code")] +pub struct NotAnError; + pub struct RendezvousCodec { /// Codec to encode/decode the Unsigned varint length prefix of the frames. length_codec: UviBytes, @@ -105,7 +333,7 @@ impl Decoder for RendezvousCodec { let message = wire::Message::decode(message)?; - Ok(Some(message.into())) + Ok(Some(message.try_into()?)) } } From e191a3ddaa884fd8ff069436fc1db23c19da4934 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 25 May 2021 17:59:34 +1000 Subject: [PATCH 006/242] Return Framed stream from protocol --- protocols/rendezvous/src/protocol.rs | 49 +++------------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/protocols/rendezvous/src/protocol.rs b/protocols/rendezvous/src/protocol.rs index 229fdd1c318..ebce4bb742c 100644 --- a/protocols/rendezvous/src/protocol.rs +++ b/protocols/rendezvous/src/protocol.rs @@ -26,62 +26,21 @@ impl UpgradeInfo for Rendezvous { } impl InboundUpgrade for Rendezvous { - type Output = InboundStream; + type Output = Framed; type Error = Void; type Future = future::Ready>; fn upgrade_inbound(self, socket: TSocket, _: Self::Info) -> Self::Future { - future::ready(todo!()) + future::ready(Ok(Framed::new(socket, RendezvousCodec::default()))) } } impl OutboundUpgrade for Rendezvous { - //type Output = Framed; - type Output = DiscoverResponse; + type Output = Framed; type Error = Void; type Future = future::Ready>; fn upgrade_outbound(self, socket: TSocket, _: Self::Info) -> Self::Future { - //future::ready(Ok(Framed::new(socket, RendezvousCodec))) - future::ready(todo!()) + future::ready(Ok(Framed::new(socket, RendezvousCodec::default()))) } } - -pub enum InboundStream { - Register(RegisterRequest), - Discover -} - -pub enum Outbound { - Register(RegisterRequest), - Discover -} - -pub struct RegisterRequest { - -} - -impl RegisterRequest { - pub fn respond_with(self, response: RegisterResponse) -> RegisterResponseFuture { - todo!() - } -} - -pub struct DiscoverRequest { - -} - -pub enum DiscoverResponse { - Registrations, - Error, -} - -pub enum RegisterResponse { - Ok, - Error -} - -pub struct RegisterResponseFuture { - stream: Framed, - message: codec::Message -} From f7288215caf7471e71c3b52d3ff14ec7b878ea23 Mon Sep 17 00:00:00 2001 From: rishflab Date: Wed, 26 May 2021 11:59:21 +1000 Subject: [PATCH 007/242] Define behaviour API --- protocols/rendezvous/src/behaviour.rs | 86 +++++++++++++++++++++++++++ protocols/rendezvous/src/handler.rs | 33 ++++------ 2 files changed, 96 insertions(+), 23 deletions(-) create mode 100644 protocols/rendezvous/src/behaviour.rs diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs new file mode 100644 index 00000000000..e3eba49c1bd --- /dev/null +++ b/protocols/rendezvous/src/behaviour.rs @@ -0,0 +1,86 @@ +use crate::codec::{ErrorCode, Registration}; +use crate::handler::RendezvousHandler; +use libp2p_core::connection::ConnectionId; +use libp2p_core::{Multiaddr, PeerId}; +use libp2p_swarm::{ + IntoProtocolsHandler, NetworkBehaviour, NetworkBehaviourAction, PollParameters, +}; +use std::collections::HashMap; +use std::task::{Context, Poll}; + +struct Rendezvous; + +impl Rendezvous { + pub fn register(&mut self, ns: String, rendezvous_node: PeerId) {} + pub fn unregister(&mut self, ns: String, rendezvous_node: PeerId) {} + pub fn discover(&mut self, ns: Option, rendezvous_node: PeerId) {} +} + +enum OutEvent { + Discovered { + rendezvous_node: PeerId, + ns: Vec, + }, + FailedToDiscover { + rendezvous_node: PeerId, + err_code: ErrorCode, + }, + RegisteredWithRendezvousNode { + rendezvous_node: PeerId, + ns: String, + }, + FailedToRegisterWithRendezvousNode { + rendezvous_node: PeerId, + ns: String, + err_code: ErrorCode, + }, + DeclinedRegisterRequest { + peer: PeerId, + ns: String, + }, + PeerRegistered { + peer_id: PeerId, + ns: Registration, + }, + PeerUnregistered { + peer_id: PeerId, + ns: Registration, + }, +} + +impl NetworkBehaviour for Rendezvous { + type ProtocolsHandler = RendezvousHandler; + type OutEvent = OutEvent; + + fn new_handler(&mut self) -> Self::ProtocolsHandler { + RendezvousHandler::new() + } + + fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { + todo!() + } + + fn inject_connected(&mut self, peer_id: &PeerId) { + todo!() + } + + fn inject_disconnected(&mut self, peer_id: &PeerId) { + todo!() + } + + fn inject_event(&mut self, peer_id: PeerId, connection: ConnectionId, event: _) { + todo!() + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + params: &mut impl PollParameters< + SupportedProtocolsIter = _, + ListenedAddressesIter = _, + ExternalAddressesIter = _, + >, + ) -> Poll> { + todo!() + } +} diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 7a4ba104dd9..7f8e1a410f1 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,37 +1,34 @@ +use crate::codec::Message; +use crate::codec::RendezvousCodec; use crate::protocol; +use asynchronous_codec::Framed; use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; +use std::collections::VecDeque; use std::error::Error; use std::fmt; use std::task::{Context, Poll}; use std::time::Instant; -use asynchronous_codec::Framed; -use crate::codec::RendezvousCodec; -use crate::codec::Message; -use crate::protocol::{InboundStream, DiscoverResponse}; -use std::collections::VecDeque; -pub struct RendezvousHandler{ +pub struct RendezvousHandler { /// Upgrade configuration for the rendezvous protocol. listen_protocol: SubstreamProtocol, - - in_events: VecDeque, /// Queue of values that we want to send to the remote. out_events: VecDeque, } -impl RendezvousHandler { +impl RendezvousHandler { pub fn new() -> Self { Self { listen_protocol: SubstreamProtocol::new(Default::default(), ()), in_events: VecDeque::new(), - out_events: VecDeque::new() + out_events: VecDeque::new(), } } } @@ -50,7 +47,7 @@ pub enum RendezvousHandlerIn { RegisterRequest, } -impl ProtocolsHandler for RendezvousHandler { +impl ProtocolsHandler for RendezvousHandler { type InEvent = RendezvousHandlerIn; type OutEvent = RendezvousHandlerOut; type Error = (); @@ -69,10 +66,6 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, _info: Self::InboundOpenInfo, ) { - match substream { - InboundStream::Discover => self.out_events.push_back(RendezvousHandlerOut::DiscoverRequest), - InboundStream::Register(a) => self.out_events.push_back(RendezvousHandlerOut::RegisterRequest) - } } fn inject_fully_negotiated_outbound( @@ -80,15 +73,9 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, _info: Self::OutboundOpenInfo, ) { - match substream { - - } - self.out_events.push_back(RendezvousHandlerOut::DiscoverResponse(substream)); } - fn inject_event(&mut self, message: RendezvousHandlerIn) { - RendezvousHandlerIn::Message(m) => self.out_events.push(m) - } + fn inject_event(&mut self, message: RendezvousHandlerIn) {} fn inject_dial_upgrade_error( &mut self, @@ -113,6 +100,6 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { - Poll::Ready(ProtocolsHandlerEvent::Custom(RendezvousResult::Ok(()))) + todo!() } } From 87df759d30eb731bff8c29cd612ebe9ff7d1f6b1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 May 2021 11:19:39 +1000 Subject: [PATCH 008/242] Initial PoC for signed peer records --- core/build.rs | 2 +- core/src/envelope.proto | 12 +++ core/src/identity.rs | 1 + core/src/lib.rs | 12 +++ core/src/peer_record.proto | 22 +++++ core/src/peer_record.rs | 170 ++++++++++++++++++++++++++++++++++++ core/src/signed_envelope.rs | 120 +++++++++++++++++++++++++ 7 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 core/src/envelope.proto create mode 100644 core/src/peer_record.proto create mode 100644 core/src/peer_record.rs create mode 100644 core/src/signed_envelope.rs diff --git a/core/build.rs b/core/build.rs index c08517dee58..ea8a6af2c37 100644 --- a/core/build.rs +++ b/core/build.rs @@ -19,5 +19,5 @@ // DEALINGS IN THE SOFTWARE. fn main() { - prost_build::compile_protos(&["src/keys.proto"], &["src"]).unwrap(); + prost_build::compile_protos(&["src/keys.proto", "src/envelope.proto", "src/peer_record.proto"], &["src"]).unwrap(); } diff --git a/core/src/envelope.proto b/core/src/envelope.proto new file mode 100644 index 00000000000..509899a899c --- /dev/null +++ b/core/src/envelope.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +package envelope_proto; + +import "keys.proto"; + +message Envelope { + required keys_proto.PublicKey public_key = 1; + required bytes payload_type = 2; + required bytes payload = 3; + required bytes signature = 5; +} diff --git a/core/src/identity.rs b/core/src/identity.rs index 2ea92a6793c..697ad1e8c13 100644 --- a/core/src/identity.rs +++ b/core/src/identity.rs @@ -135,6 +135,7 @@ impl PublicKey { /// that the signature has been produced by the corresponding /// private key (authenticity), and that the message has not been /// tampered with (integrity). + #[must_use] pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool { use PublicKey::*; match self { diff --git a/core/src/lib.rs b/core/src/lib.rs index 844fd2a23bc..d782f1ec6eb 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -39,6 +39,14 @@ mod keys_proto { include!(concat!(env!("OUT_DIR"), "/keys_proto.rs")); } +mod envelope_proto { + include!(concat!(env!("OUT_DIR"), "/envelope_proto.rs")); +} + +mod peer_record_proto { + include!(concat!(env!("OUT_DIR"), "/peer_record_proto.rs")); +} + /// Multi-address re-export. pub use multiaddr; pub type Negotiated = multistream_select::Negotiated; @@ -53,6 +61,8 @@ pub mod muxing; pub mod network; pub mod transport; pub mod upgrade; +mod signed_envelope; +mod peer_record; pub use multiaddr::Multiaddr; pub use multihash; @@ -64,6 +74,8 @@ pub use translation::address_translation; pub use upgrade::{InboundUpgrade, OutboundUpgrade, UpgradeInfo, UpgradeError, ProtocolName}; pub use connection::{Connected, Endpoint, ConnectedPoint}; pub use network::Network; +pub use signed_envelope::SignedEnvelope; +pub use peer_record::{AuthenticatedPeerRecord, PeerRecord}; use std::{future::Future, pin::Pin}; diff --git a/core/src/peer_record.proto b/core/src/peer_record.proto new file mode 100644 index 00000000000..34a8c9abc1b --- /dev/null +++ b/core/src/peer_record.proto @@ -0,0 +1,22 @@ +syntax = "proto2"; + +package peer_record_proto; + +// PeerRecord contains the listen addresses for a peer at a particular point in time. +message PeerRecord { + // AddressInfo wraps a multiaddr. In the future, it may be extended to + // contain additional metadata, such as "routability" (whether an address is + // local or global, etc). + message AddressInfo { + required bytes multiaddr = 1; + } + + // the peer id of the subject of the record (who these addresses belong to). + required bytes peer_id = 1; + + // A monotonically increasing sequence number, used for record ordering. + required uint64 seq = 2; + + // All current listen addresses + repeated AddressInfo addresses = 3; +} diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs new file mode 100644 index 00000000000..c6ca1fb3e95 --- /dev/null +++ b/core/src/peer_record.rs @@ -0,0 +1,170 @@ +use crate::identity::Keypair; +use crate::signed_envelope::SignedEnvelope; +use crate::{peer_record_proto, Multiaddr, PeerId}; +use std::convert::TryInto; + +const PAYLOAD_TYPE: &str = "/libp2p/routing-state-record"; +const DOMAIN_SEP: &str = "libp2p-routing-state"; + +// TODO: docs +#[derive(Debug, PartialEq, Clone)] +pub struct PeerRecord { + pub peer_id: PeerId, + pub seq: u64, + pub addresses: Vec, +} + +impl PeerRecord { + // TODO: docs + pub fn into_protobuf_encoding(self) -> Vec { + use prost::Message; + + let record = peer_record_proto::PeerRecord { + peer_id: self.peer_id.to_bytes(), + seq: self.seq, + addresses: self + .addresses + .into_iter() + .map(|m| peer_record_proto::peer_record::AddressInfo { + multiaddr: m.to_vec(), + }) + .collect(), + }; + + let mut buf = Vec::with_capacity(record.encoded_len()); + record + .encode(&mut buf) + .expect("Vec provides capacity as needed"); + buf + } + + // TODO: wrap error to not expose `prost` as a dependency + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + use prost::Message; + + let record = peer_record_proto::PeerRecord::decode(bytes)?; + + Ok(Self { + peer_id: PeerId::from_bytes(&record.peer_id).unwrap(), // TODO: Error handling + seq: record.seq, + addresses: record + .addresses + .into_iter() + .map(|a| a.multiaddr.try_into()) + .collect::, _>>() + .unwrap(), // TODO: error handlign + }) + } + + // TODO: docs + pub fn warp_in_envelope(self, key: Keypair) -> SignedEnvelope { + if key.public().into_peer_id() != self.peer_id { + panic!("bad key") + } + + let payload = self.into_protobuf_encoding(); + + SignedEnvelope::new( + key, + String::from(DOMAIN_SEP), + PAYLOAD_TYPE.as_bytes().to_vec(), + payload, + ) + .unwrap() // TODO: Error handling + } + + pub fn authenticate(self, key: Keypair) -> AuthenticatedPeerRecord { + AuthenticatedPeerRecord::from_record(key, self) + } +} + +// TODO: docs +#[derive(Debug)] +pub struct AuthenticatedPeerRecord { + inner: PeerRecord, + + /// A signed envelope containing the above inner [`PeerRecord`]. + /// + /// If this [`AuthenticatedPeerRecord`] was constructed from a [`SignedEnvelope`], this is the original instance. + /// If this [`AuthenticatedPeerRecord`] was created by [`authenticating`](PeerRecord::authenticate) an existing [`PeerRecord`], then this is a pre-computed [`SignedEnvelope`] to make it easier to send an [`AuthenticatedPeerRecord`] across the wire. + envelope: SignedEnvelope, +} + +impl AuthenticatedPeerRecord { + // TODO: docs + pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result { + let payload = envelope + .payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes()) + .unwrap(); // TODO: Error handling + + let record = PeerRecord::from_protobuf_encoding(payload).unwrap(); // TODO: Error handling + + Ok(Self { + inner: record, + envelope, + }) + } + + pub fn from_record(key: Keypair, record: PeerRecord) -> Self { + let envelope = record.clone().warp_in_envelope(key); + + Self { + inner: record, + envelope, + } + } + + pub fn to_signed_envelope(&self) -> SignedEnvelope { + self.envelope.clone() + } + + pub fn into_signed_envelope(self) -> SignedEnvelope { + self.envelope + } + + pub fn peer_id(&self) -> PeerId { + self.inner.peer_id + } + + pub fn seq(&self) -> u64 { + self.inner.seq + } + + pub fn addresses(&self) -> &[Multiaddr] { + self.inner.addresses.as_slice() + } +} + +impl PartialEq for AuthenticatedPeerRecord { + fn eq(&self, other: &PeerRecord) -> bool { + self.inner.eq(other) + } +} + +impl PartialEq for PeerRecord { + fn eq(&self, other: &AuthenticatedPeerRecord) -> bool { + other.inner.eq(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const HOME: &str = "/ip4/127.0.0.1/tcp/1337"; + + #[test] + fn roundtrip_envelope() { + let identity = Keypair::generate_ed25519(); + let record = PeerRecord { + peer_id: identity.public().into_peer_id(), + seq: 0, + addresses: vec![HOME.parse().unwrap()], + }; + + let envelope = record.clone().warp_in_envelope(identity); + let authenticated = AuthenticatedPeerRecord::from_signed_envelope(envelope).unwrap(); + + assert_eq!(authenticated, record) + } +} diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs new file mode 100644 index 00000000000..f22e6a9f951 --- /dev/null +++ b/core/src/signed_envelope.rs @@ -0,0 +1,120 @@ +use crate::identity::error::SigningError; +use crate::identity::Keypair; +use crate::PublicKey; +use prost::bytes::BufMut; +use unsigned_varint::encode::usize_buffer; + +// TODO: docs +#[derive(Debug, Clone)] +pub struct SignedEnvelope { + key: PublicKey, + payload_type: Vec, + payload: Vec, + signature: Vec, +} + +impl SignedEnvelope { + // TODO: docs + pub fn new( + key: Keypair, + domain_separation: String, + payload_type: Vec, + payload: Vec, + ) -> Result { + // TODO: fix duplication + + let mut domain_sep_length_buffer = usize_buffer(); + let domain_sep_length = + unsigned_varint::encode::usize(domain_separation.len(), &mut domain_sep_length_buffer); + + let mut payload_type_length_buffer = usize_buffer(); + let payload_type_length = + unsigned_varint::encode::usize(payload_type.len(), &mut payload_type_length_buffer); + + let mut payload_length_buffer = usize_buffer(); + let payload_length = + unsigned_varint::encode::usize(payload.len(), &mut payload_length_buffer); + + let mut buffer = Vec::with_capacity( + domain_sep_length.len() + + domain_separation.len() + + payload_type_length.len() + + payload_type.len() + + payload_length.len() + + payload.len(), + ); + + buffer.put(domain_sep_length); + buffer.put(domain_separation.as_bytes()); + buffer.put(payload_type_length); + buffer.put(payload_type.as_slice()); + buffer.put(payload_length); + buffer.put(payload.as_slice()); + + let signature = key.sign(&buffer)?; + + Ok(Self { + key: key.public(), + payload_type, + payload, + signature, + }) + } + + #[must_use] + pub fn verify(&self, domain_separation: String) -> bool { + let mut domain_sep_length_buffer = usize_buffer(); + let domain_sep_length = + unsigned_varint::encode::usize(domain_separation.len(), &mut domain_sep_length_buffer); + + let mut payload_type_length_buffer = usize_buffer(); + let payload_type_length = unsigned_varint::encode::usize( + self.payload_type.len(), + &mut payload_type_length_buffer, + ); + + let mut payload_length_buffer = usize_buffer(); + let payload_length = + unsigned_varint::encode::usize(self.payload.len(), &mut payload_length_buffer); + + let mut buffer = Vec::with_capacity( + domain_sep_length.len() + + domain_separation.len() + + payload_type_length.len() + + self.payload_type.len() + + payload_length.len() + + self.payload.len(), + ); + + buffer.put(domain_sep_length); + buffer.put(domain_separation.as_bytes()); + buffer.put(payload_type_length); + buffer.put(self.payload_type.as_slice()); + buffer.put(payload_length); + buffer.put(self.payload.as_slice()); + + self.key.verify(&buffer, &self.signature) + } + + // TODO: docs + pub fn payload( + &self, + domain_separation: String, + expected_payload_type: &[u8], + ) -> Result<&[u8], ()> { + if &self.payload_type != expected_payload_type { + panic!("bad payload type") // TODO: error handling + } + + if !self.verify(domain_separation) { + panic!("bad signature") // TODO: error handling + } + + Ok(&self.payload) + } + + // TODO: Do we need this? + // pub fn payload_unchecked(&self) -> Vec { + // + // } +} From 5c61db04caa4a544c7ad1de78785132379538e90 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 May 2021 11:19:39 +1000 Subject: [PATCH 009/242] Initial PoC for signed peer records --- core/build.rs | 2 +- core/src/envelope.proto | 12 +++ core/src/identity.rs | 1 + core/src/lib.rs | 12 +++ core/src/peer_record.proto | 22 +++++ core/src/peer_record.rs | 170 ++++++++++++++++++++++++++++++++++++ core/src/signed_envelope.rs | 120 +++++++++++++++++++++++++ 7 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 core/src/envelope.proto create mode 100644 core/src/peer_record.proto create mode 100644 core/src/peer_record.rs create mode 100644 core/src/signed_envelope.rs diff --git a/core/build.rs b/core/build.rs index c08517dee58..ea8a6af2c37 100644 --- a/core/build.rs +++ b/core/build.rs @@ -19,5 +19,5 @@ // DEALINGS IN THE SOFTWARE. fn main() { - prost_build::compile_protos(&["src/keys.proto"], &["src"]).unwrap(); + prost_build::compile_protos(&["src/keys.proto", "src/envelope.proto", "src/peer_record.proto"], &["src"]).unwrap(); } diff --git a/core/src/envelope.proto b/core/src/envelope.proto new file mode 100644 index 00000000000..509899a899c --- /dev/null +++ b/core/src/envelope.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +package envelope_proto; + +import "keys.proto"; + +message Envelope { + required keys_proto.PublicKey public_key = 1; + required bytes payload_type = 2; + required bytes payload = 3; + required bytes signature = 5; +} diff --git a/core/src/identity.rs b/core/src/identity.rs index 2ea92a6793c..697ad1e8c13 100644 --- a/core/src/identity.rs +++ b/core/src/identity.rs @@ -135,6 +135,7 @@ impl PublicKey { /// that the signature has been produced by the corresponding /// private key (authenticity), and that the message has not been /// tampered with (integrity). + #[must_use] pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool { use PublicKey::*; match self { diff --git a/core/src/lib.rs b/core/src/lib.rs index 844fd2a23bc..d782f1ec6eb 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -39,6 +39,14 @@ mod keys_proto { include!(concat!(env!("OUT_DIR"), "/keys_proto.rs")); } +mod envelope_proto { + include!(concat!(env!("OUT_DIR"), "/envelope_proto.rs")); +} + +mod peer_record_proto { + include!(concat!(env!("OUT_DIR"), "/peer_record_proto.rs")); +} + /// Multi-address re-export. pub use multiaddr; pub type Negotiated = multistream_select::Negotiated; @@ -53,6 +61,8 @@ pub mod muxing; pub mod network; pub mod transport; pub mod upgrade; +mod signed_envelope; +mod peer_record; pub use multiaddr::Multiaddr; pub use multihash; @@ -64,6 +74,8 @@ pub use translation::address_translation; pub use upgrade::{InboundUpgrade, OutboundUpgrade, UpgradeInfo, UpgradeError, ProtocolName}; pub use connection::{Connected, Endpoint, ConnectedPoint}; pub use network::Network; +pub use signed_envelope::SignedEnvelope; +pub use peer_record::{AuthenticatedPeerRecord, PeerRecord}; use std::{future::Future, pin::Pin}; diff --git a/core/src/peer_record.proto b/core/src/peer_record.proto new file mode 100644 index 00000000000..34a8c9abc1b --- /dev/null +++ b/core/src/peer_record.proto @@ -0,0 +1,22 @@ +syntax = "proto2"; + +package peer_record_proto; + +// PeerRecord contains the listen addresses for a peer at a particular point in time. +message PeerRecord { + // AddressInfo wraps a multiaddr. In the future, it may be extended to + // contain additional metadata, such as "routability" (whether an address is + // local or global, etc). + message AddressInfo { + required bytes multiaddr = 1; + } + + // the peer id of the subject of the record (who these addresses belong to). + required bytes peer_id = 1; + + // A monotonically increasing sequence number, used for record ordering. + required uint64 seq = 2; + + // All current listen addresses + repeated AddressInfo addresses = 3; +} diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs new file mode 100644 index 00000000000..c6ca1fb3e95 --- /dev/null +++ b/core/src/peer_record.rs @@ -0,0 +1,170 @@ +use crate::identity::Keypair; +use crate::signed_envelope::SignedEnvelope; +use crate::{peer_record_proto, Multiaddr, PeerId}; +use std::convert::TryInto; + +const PAYLOAD_TYPE: &str = "/libp2p/routing-state-record"; +const DOMAIN_SEP: &str = "libp2p-routing-state"; + +// TODO: docs +#[derive(Debug, PartialEq, Clone)] +pub struct PeerRecord { + pub peer_id: PeerId, + pub seq: u64, + pub addresses: Vec, +} + +impl PeerRecord { + // TODO: docs + pub fn into_protobuf_encoding(self) -> Vec { + use prost::Message; + + let record = peer_record_proto::PeerRecord { + peer_id: self.peer_id.to_bytes(), + seq: self.seq, + addresses: self + .addresses + .into_iter() + .map(|m| peer_record_proto::peer_record::AddressInfo { + multiaddr: m.to_vec(), + }) + .collect(), + }; + + let mut buf = Vec::with_capacity(record.encoded_len()); + record + .encode(&mut buf) + .expect("Vec provides capacity as needed"); + buf + } + + // TODO: wrap error to not expose `prost` as a dependency + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + use prost::Message; + + let record = peer_record_proto::PeerRecord::decode(bytes)?; + + Ok(Self { + peer_id: PeerId::from_bytes(&record.peer_id).unwrap(), // TODO: Error handling + seq: record.seq, + addresses: record + .addresses + .into_iter() + .map(|a| a.multiaddr.try_into()) + .collect::, _>>() + .unwrap(), // TODO: error handlign + }) + } + + // TODO: docs + pub fn warp_in_envelope(self, key: Keypair) -> SignedEnvelope { + if key.public().into_peer_id() != self.peer_id { + panic!("bad key") + } + + let payload = self.into_protobuf_encoding(); + + SignedEnvelope::new( + key, + String::from(DOMAIN_SEP), + PAYLOAD_TYPE.as_bytes().to_vec(), + payload, + ) + .unwrap() // TODO: Error handling + } + + pub fn authenticate(self, key: Keypair) -> AuthenticatedPeerRecord { + AuthenticatedPeerRecord::from_record(key, self) + } +} + +// TODO: docs +#[derive(Debug)] +pub struct AuthenticatedPeerRecord { + inner: PeerRecord, + + /// A signed envelope containing the above inner [`PeerRecord`]. + /// + /// If this [`AuthenticatedPeerRecord`] was constructed from a [`SignedEnvelope`], this is the original instance. + /// If this [`AuthenticatedPeerRecord`] was created by [`authenticating`](PeerRecord::authenticate) an existing [`PeerRecord`], then this is a pre-computed [`SignedEnvelope`] to make it easier to send an [`AuthenticatedPeerRecord`] across the wire. + envelope: SignedEnvelope, +} + +impl AuthenticatedPeerRecord { + // TODO: docs + pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result { + let payload = envelope + .payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes()) + .unwrap(); // TODO: Error handling + + let record = PeerRecord::from_protobuf_encoding(payload).unwrap(); // TODO: Error handling + + Ok(Self { + inner: record, + envelope, + }) + } + + pub fn from_record(key: Keypair, record: PeerRecord) -> Self { + let envelope = record.clone().warp_in_envelope(key); + + Self { + inner: record, + envelope, + } + } + + pub fn to_signed_envelope(&self) -> SignedEnvelope { + self.envelope.clone() + } + + pub fn into_signed_envelope(self) -> SignedEnvelope { + self.envelope + } + + pub fn peer_id(&self) -> PeerId { + self.inner.peer_id + } + + pub fn seq(&self) -> u64 { + self.inner.seq + } + + pub fn addresses(&self) -> &[Multiaddr] { + self.inner.addresses.as_slice() + } +} + +impl PartialEq for AuthenticatedPeerRecord { + fn eq(&self, other: &PeerRecord) -> bool { + self.inner.eq(other) + } +} + +impl PartialEq for PeerRecord { + fn eq(&self, other: &AuthenticatedPeerRecord) -> bool { + other.inner.eq(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const HOME: &str = "/ip4/127.0.0.1/tcp/1337"; + + #[test] + fn roundtrip_envelope() { + let identity = Keypair::generate_ed25519(); + let record = PeerRecord { + peer_id: identity.public().into_peer_id(), + seq: 0, + addresses: vec![HOME.parse().unwrap()], + }; + + let envelope = record.clone().warp_in_envelope(identity); + let authenticated = AuthenticatedPeerRecord::from_signed_envelope(envelope).unwrap(); + + assert_eq!(authenticated, record) + } +} diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs new file mode 100644 index 00000000000..f22e6a9f951 --- /dev/null +++ b/core/src/signed_envelope.rs @@ -0,0 +1,120 @@ +use crate::identity::error::SigningError; +use crate::identity::Keypair; +use crate::PublicKey; +use prost::bytes::BufMut; +use unsigned_varint::encode::usize_buffer; + +// TODO: docs +#[derive(Debug, Clone)] +pub struct SignedEnvelope { + key: PublicKey, + payload_type: Vec, + payload: Vec, + signature: Vec, +} + +impl SignedEnvelope { + // TODO: docs + pub fn new( + key: Keypair, + domain_separation: String, + payload_type: Vec, + payload: Vec, + ) -> Result { + // TODO: fix duplication + + let mut domain_sep_length_buffer = usize_buffer(); + let domain_sep_length = + unsigned_varint::encode::usize(domain_separation.len(), &mut domain_sep_length_buffer); + + let mut payload_type_length_buffer = usize_buffer(); + let payload_type_length = + unsigned_varint::encode::usize(payload_type.len(), &mut payload_type_length_buffer); + + let mut payload_length_buffer = usize_buffer(); + let payload_length = + unsigned_varint::encode::usize(payload.len(), &mut payload_length_buffer); + + let mut buffer = Vec::with_capacity( + domain_sep_length.len() + + domain_separation.len() + + payload_type_length.len() + + payload_type.len() + + payload_length.len() + + payload.len(), + ); + + buffer.put(domain_sep_length); + buffer.put(domain_separation.as_bytes()); + buffer.put(payload_type_length); + buffer.put(payload_type.as_slice()); + buffer.put(payload_length); + buffer.put(payload.as_slice()); + + let signature = key.sign(&buffer)?; + + Ok(Self { + key: key.public(), + payload_type, + payload, + signature, + }) + } + + #[must_use] + pub fn verify(&self, domain_separation: String) -> bool { + let mut domain_sep_length_buffer = usize_buffer(); + let domain_sep_length = + unsigned_varint::encode::usize(domain_separation.len(), &mut domain_sep_length_buffer); + + let mut payload_type_length_buffer = usize_buffer(); + let payload_type_length = unsigned_varint::encode::usize( + self.payload_type.len(), + &mut payload_type_length_buffer, + ); + + let mut payload_length_buffer = usize_buffer(); + let payload_length = + unsigned_varint::encode::usize(self.payload.len(), &mut payload_length_buffer); + + let mut buffer = Vec::with_capacity( + domain_sep_length.len() + + domain_separation.len() + + payload_type_length.len() + + self.payload_type.len() + + payload_length.len() + + self.payload.len(), + ); + + buffer.put(domain_sep_length); + buffer.put(domain_separation.as_bytes()); + buffer.put(payload_type_length); + buffer.put(self.payload_type.as_slice()); + buffer.put(payload_length); + buffer.put(self.payload.as_slice()); + + self.key.verify(&buffer, &self.signature) + } + + // TODO: docs + pub fn payload( + &self, + domain_separation: String, + expected_payload_type: &[u8], + ) -> Result<&[u8], ()> { + if &self.payload_type != expected_payload_type { + panic!("bad payload type") // TODO: error handling + } + + if !self.verify(domain_separation) { + panic!("bad signature") // TODO: error handling + } + + Ok(&self.payload) + } + + // TODO: Do we need this? + // pub fn payload_unchecked(&self) -> Vec { + // + // } +} From 5428d7f035273443cc19f8d73f8c74f669da4b29 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 May 2021 14:22:56 +1000 Subject: [PATCH 010/242] Add {into,from}_protobuf_encoding for SignedEnvelope --- core/src/identity.rs | 53 +++++++++++++++++++++++-------------- core/src/signed_envelope.rs | 32 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/core/src/identity.rs b/core/src/identity.rs index 697ad1e8c13..9fa6a83a28f 100644 --- a/core/src/identity.rs +++ b/core/src/identity.rs @@ -30,6 +30,7 @@ pub mod error; use self::error::*; use crate::{PeerId, keys_proto}; +use std::convert::{TryFrom, TryInto}; /// Identity keypair of a node. /// @@ -152,7 +153,33 @@ impl PublicKey { pub fn into_protobuf_encoding(self) -> Vec { use prost::Message; - let public_key = match self { + let public_key = keys_proto::PublicKey::from(self); + + let mut buf = Vec::with_capacity(public_key.encoded_len()); + public_key.encode(&mut buf).expect("Vec provides capacity as needed"); + buf + } + + /// Decode a public key from a protobuf structure, e.g. read from storage + /// or received from another node. + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + use prost::Message; + + let pubkey = keys_proto::PublicKey::decode(bytes) + .map_err(|e| DecodingError::new("Protobuf").source(e))?; + + pubkey.try_into() + } + + /// Convert the `PublicKey` into the corresponding `PeerId`. + pub fn into_peer_id(self) -> PeerId { + self.into() + } +} + +impl From for keys_proto::PublicKey { + fn from(key: PublicKey) -> Self { + match key { PublicKey::Ed25519(key) => keys_proto::PublicKey { r#type: keys_proto::KeyType::Ed25519 as i32, @@ -170,22 +197,14 @@ impl PublicKey { r#type: keys_proto::KeyType::Secp256k1 as i32, data: key.encode().to_vec() } - }; - - let mut buf = Vec::with_capacity(public_key.encoded_len()); - public_key.encode(&mut buf).expect("Vec provides capacity as needed"); - buf + } } +} - /// Decode a public key from a protobuf structure, e.g. read from storage - /// or received from another node. - pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { - use prost::Message; - - #[allow(unused_mut)] // Due to conditional compilation. - let mut pubkey = keys_proto::PublicKey::decode(bytes) - .map_err(|e| DecodingError::new("Protobuf").source(e))?; +impl TryFrom for PublicKey { + type Error = DecodingError; + fn try_from(pubkey: keys_proto::PublicKey) -> Result { let key_type = keys_proto::KeyType::from_i32(pubkey.r#type) .ok_or_else(|| DecodingError::new(format!("unknown key type: {}", pubkey.r#type)))?; @@ -213,10 +232,4 @@ impl PublicKey { } } } - - /// Convert the `PublicKey` into the corresponding `PeerId`. - pub fn into_peer_id(self) -> PeerId { - self.into() - } } - diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index f22e6a9f951..490905d518b 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -2,6 +2,7 @@ use crate::identity::error::SigningError; use crate::identity::Keypair; use crate::PublicKey; use prost::bytes::BufMut; +use std::convert::TryInto; use unsigned_varint::encode::usize_buffer; // TODO: docs @@ -117,4 +118,35 @@ impl SignedEnvelope { // pub fn payload_unchecked(&self) -> Vec { // // } + + pub fn into_protobuf_encoding(self) -> Vec { + use prost::Message; + + let envelope = crate::envelope_proto::Envelope { + public_key: self.key.into(), + payload_type: self.payload_type, + payload: self.payload, + signature: self.signature, + }; + + let mut buf = Vec::with_capacity(envelope.encoded_len()); + envelope + .encode(&mut buf) + .expect("Vec provides capacity as needed"); + buf + } + + // TODO: wrap error to not expose `prost` as a dependency + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + use prost::Message; + + let envelope = crate::envelope_proto::Envelope::decode(bytes)?; + + Ok(Self { + key: envelope.public_key.try_into().unwrap(), // TODO: error handling + payload_type: envelope.payload_type, + payload: envelope.payload, + signature: envelope.signature, + }) + } } From 7a12627238adcc138714ecc1024eee30a186d78f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 May 2021 15:03:48 +1000 Subject: [PATCH 011/242] Do proper error handling for decoding peer records --- core/src/lib.rs | 4 +- core/src/peer_record.rs | 108 ++++++++++++++++++++++++++++++++---- core/src/signed_envelope.rs | 78 +++++++++++++++++++++++--- 3 files changed, 169 insertions(+), 21 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index d782f1ec6eb..9041f02ef30 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -61,8 +61,8 @@ pub mod muxing; pub mod network; pub mod transport; pub mod upgrade; -mod signed_envelope; -mod peer_record; +pub mod signed_envelope; +pub mod peer_record; pub use multiaddr::Multiaddr; pub use multihash; diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index c6ca1fb3e95..a7cdde0a6d0 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -1,7 +1,8 @@ use crate::identity::Keypair; use crate::signed_envelope::SignedEnvelope; -use crate::{peer_record_proto, Multiaddr, PeerId}; +use crate::{peer_record_proto, signed_envelope, Multiaddr, PeerId}; use std::convert::TryInto; +use std::fmt; const PAYLOAD_TYPE: &str = "/libp2p/routing-state-record"; const DOMAIN_SEP: &str = "libp2p-routing-state"; @@ -38,21 +39,19 @@ impl PeerRecord { buf } - // TODO: wrap error to not expose `prost` as a dependency - pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { use prost::Message; let record = peer_record_proto::PeerRecord::decode(bytes)?; Ok(Self { - peer_id: PeerId::from_bytes(&record.peer_id).unwrap(), // TODO: Error handling + peer_id: PeerId::from_bytes(&record.peer_id)?, seq: record.seq, addresses: record .addresses .into_iter() .map(|a| a.multiaddr.try_into()) - .collect::, _>>() - .unwrap(), // TODO: error handlign + .collect::, _>>()?, }) } @@ -78,6 +77,58 @@ impl PeerRecord { } } +#[derive(Debug)] +pub enum DecodingError { + /// Failed to decode the provided bytes as a [`PeerRecord`]. + InvalidPeerRecord(prost::DecodeError), + /// Failed to decode the peer ID. + InvalidPeerId(multihash::Error), + /// Failed to decode a multi-address. + InvalidMultiaddr(multiaddr::Error), +} + +impl From for DecodingError { + fn from(e: prost::DecodeError) -> Self { + Self::InvalidPeerRecord(e) + } +} + +impl From for DecodingError { + fn from(e: multihash::Error) -> Self { + Self::InvalidPeerId(e) + } +} + +impl From for DecodingError { + fn from(e: multiaddr::Error) -> Self { + Self::InvalidMultiaddr(e) + } +} + +impl fmt::Display for DecodingError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DecodingError::InvalidPeerRecord(_) => { + write!(f, "Failed to decode bytes as PeerRecord") + } + DecodingError::InvalidPeerId(_) => write!(f, "Failed to decode bytes as PeerId"), + DecodingError::InvalidMultiaddr(_) => { + write!(f, "Failed to decode bytes as MultiAddress") + } + } + } +} + +impl std::error::Error for DecodingError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + DecodingError::InvalidPeerRecord(inner) => Some(inner), + DecodingError::InvalidPeerId(inner) => Some(inner), + DecodingError::InvalidMultiaddr(inner) => Some(inner), + } + } +} + // TODO: docs #[derive(Debug)] pub struct AuthenticatedPeerRecord { @@ -92,12 +143,9 @@ pub struct AuthenticatedPeerRecord { impl AuthenticatedPeerRecord { // TODO: docs - pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result { - let payload = envelope - .payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes()) - .unwrap(); // TODO: Error handling - - let record = PeerRecord::from_protobuf_encoding(payload).unwrap(); // TODO: Error handling + pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result { + let payload = envelope.payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes())?; + let record = PeerRecord::from_protobuf_encoding(payload)?; Ok(Self { inner: record, @@ -147,6 +195,42 @@ impl PartialEq for PeerRecord { } } +#[derive(Debug)] +pub enum FromEnvelopeError { + BadPayload(signed_envelope::ReadPayloadError), + InvalidPeerRecord(DecodingError), +} + +impl From for FromEnvelopeError { + fn from(e: signed_envelope::ReadPayloadError) -> Self { + Self::BadPayload(e) + } +} + +impl From for FromEnvelopeError { + fn from(e: DecodingError) -> Self { + Self::InvalidPeerRecord(e) + } +} + +impl fmt::Display for FromEnvelopeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BadPayload(_) => write!(f, "Failed to extract payload from envelope"), + Self::InvalidPeerRecord(_) => write!(f, "Failed to decode payload as PeerRecord"), + } + } +} + +impl std::error::Error for FromEnvelopeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InvalidPeerRecord(inner) => Some(inner), + Self::BadPayload(inner) => Some(inner), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index 490905d518b..8f6a883972e 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -1,8 +1,9 @@ use crate::identity::error::SigningError; use crate::identity::Keypair; -use crate::PublicKey; +use crate::{identity, PublicKey}; use prost::bytes::BufMut; use std::convert::TryInto; +use std::fmt; use unsigned_varint::encode::usize_buffer; // TODO: docs @@ -102,13 +103,16 @@ impl SignedEnvelope { &self, domain_separation: String, expected_payload_type: &[u8], - ) -> Result<&[u8], ()> { + ) -> Result<&[u8], ReadPayloadError> { if &self.payload_type != expected_payload_type { - panic!("bad payload type") // TODO: error handling + return Err(ReadPayloadError::UnexpectedPayloadType { + expected: expected_payload_type.to_vec(), + got: self.payload_type.clone(), + }); } if !self.verify(domain_separation) { - panic!("bad signature") // TODO: error handling + return Err(ReadPayloadError::InvalidSignature); } Ok(&self.payload) @@ -136,17 +140,77 @@ impl SignedEnvelope { buf } - // TODO: wrap error to not expose `prost` as a dependency - pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { use prost::Message; let envelope = crate::envelope_proto::Envelope::decode(bytes)?; Ok(Self { - key: envelope.public_key.try_into().unwrap(), // TODO: error handling + key: envelope.public_key.try_into()?, payload_type: envelope.payload_type, payload: envelope.payload, signature: envelope.signature, }) } } + +#[derive(Debug)] +pub enum DecodingError { + /// Decoding the provided bytes as a signed envelope failed. + InvalidEnvelope(prost::DecodeError), + /// The public key in the envelope could not be converted to our internal public key type. + InvalidPublicKey(identity::error::DecodingError), +} + +impl From for DecodingError { + fn from(e: prost::DecodeError) -> Self { + Self::InvalidEnvelope(e) + } +} + +impl From for DecodingError { + fn from(e: identity::error::DecodingError) -> Self { + Self::InvalidPublicKey(e) + } +} + +impl fmt::Display for DecodingError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidEnvelope(_) => write!(f, "Failed to decode envelope"), + Self::InvalidPublicKey(_) => write!(f, "Failed to convert public key"), + } + } +} + +impl std::error::Error for DecodingError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InvalidEnvelope(inner) => Some(inner), + Self::InvalidPublicKey(inner) => Some(inner), + } + } +} + +#[derive(Debug)] +pub enum ReadPayloadError { + /// The signature on the signed envelope does not verify with the provided domain separation string. + InvalidSignature, + /// The payload contained in the envelope is not of the expected type. + UnexpectedPayloadType { expected: Vec, got: Vec }, +} + +impl fmt::Display for ReadPayloadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidSignature => write!(f, "Invalid signature"), + Self::UnexpectedPayloadType { expected, got } => write!( + f, + "Unexpected payload type, expected {:?} but got {:?}", + expected, got + ), + } + } +} + +impl std::error::Error for ReadPayloadError {} From c94e21571ae7730ba455d115b5d70933a14df2b6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 May 2021 15:13:35 +1000 Subject: [PATCH 012/242] Integrate signed peer records into rendezvous --- protocols/rendezvous/Cargo.toml | 2 +- protocols/rendezvous/src/codec.rs | 23 ++++++++++++++++------- protocols/rendezvous/src/protocol.rs | 3 +-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index dfcf3640cf4..51ff424f8cf 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -18,7 +18,7 @@ void = "1" log = "0.4" futures = { version = "0.3", default-features = false, features = ["std"] } thiserror = "1" # TODO: REMOVE FOR PRODUCTION USE -unsigned-varint = "0.7" +unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } [build-dependencies] prost-build = "0.7" diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index fbe19fe3b06..459989ffdbc 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -1,13 +1,14 @@ use asynchronous_codec::{Encoder, BytesMut, Decoder, Bytes}; use unsigned_varint::codec::UviBytes; use std::convert::{TryFrom, TryInto}; +use libp2p_core::{AuthenticatedPeerRecord, SignedEnvelope, signed_envelope, peer_record}; #[derive(Debug)] pub enum Message { Register { namespace: String, ttl: Option, - // TODO: Signed peer record field + record: AuthenticatedPeerRecord }, SuccessfullyRegistered { ttl: i64 @@ -36,8 +37,8 @@ pub enum Message { #[derive(Debug)] pub struct Registration { namespace: String, + record: AuthenticatedPeerRecord // ttl: i64, TODO: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds - // TODO: Signed peer record } #[derive(Debug)] @@ -68,12 +69,12 @@ impl From for wire::Message { use wire::message::*; match message { - Message::Register { namespace, ttl } => wire::Message { + Message::Register { namespace, ttl, record } => wire::Message { r#type: Some(MessageType::Register.into()), register: Some(Register { ns: Some(namespace), ttl, - signed_peer_record: None + signed_peer_record: Some(record.into_signed_envelope().into_protobuf_encoding()) }), register_response: None, unregister: None, @@ -168,10 +169,11 @@ impl TryFrom for Message { use wire::message::*; let message = match message { - wire::Message { r#type: Some(0), register: Some(Register { ns, ttl, .. }), .. } => { + wire::Message { r#type: Some(0), register: Some(Register { ns, ttl, signed_peer_record: Some(signed_peer_record) }), .. } => { Message::Register { namespace: ns.ok_or(ConversionError::MissingNamespace)?, - ttl + ttl, + record: AuthenticatedPeerRecord::from_signed_envelope(SignedEnvelope::from_protobuf_encoding(&signed_peer_record)?)? } }, wire::Message { r#type: Some(1), register_response: Some(RegisterResponse { status: Some(0), ttl, .. }), .. } => { @@ -202,7 +204,8 @@ impl TryFrom for Message { wire::Message { r#type: Some(4), discover_response: Some(DiscoverResponse { registrations, status: Some(0), .. }), .. } => { Message::DiscoverResponse { registrations: registrations.into_iter().map(|reggo| Ok(Registration { - namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)? + namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)?, + record: AuthenticatedPeerRecord::from_signed_envelope(SignedEnvelope::from_protobuf_encoding(®go.signed_peer_record.ok_or(ConversionError::MissingSignedPeerRecord)?)?)? })).collect::, ConversionError>>()?, } }, @@ -228,10 +231,16 @@ pub enum ConversionError { InconsistentWireMessage, #[error("Missing namespace field")] MissingNamespace, + #[error("Missing signed peer record field")] + MissingSignedPeerRecord, #[error("Missing TTL field")] MissingTtl, #[error("Bad status code")] BadStatusCode, + #[error("Failed to decode signed envelope")] + BadSignedEnvelope(#[from] signed_envelope::DecodingError), + #[error("Failed to decode envelope as signed peer record")] + BadSignedPeerRecord(#[from] peer_record::FromEnvelopeError), } impl TryFrom for ErrorCode { diff --git a/protocols/rendezvous/src/protocol.rs b/protocols/rendezvous/src/protocol.rs index ebce4bb742c..661340e4123 100644 --- a/protocols/rendezvous/src/protocol.rs +++ b/protocols/rendezvous/src/protocol.rs @@ -1,11 +1,10 @@ -use asynchronous_codec::{BytesMut, Decoder, Encoder, Framed}; +use asynchronous_codec::{Framed}; use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; use std::{future, iter}; use void::Void; use futures::AsyncRead; use futures::AsyncWrite; use crate::codec::RendezvousCodec; -use crate::codec; #[derive(Default, Debug, Copy, Clone)] pub struct Rendezvous; From 193c01645308e5da913a0ca15bd6d9806c298cfa Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 May 2021 14:22:56 +1000 Subject: [PATCH 013/242] Add {into,from}_protobuf_encoding for SignedEnvelope --- core/src/identity.rs | 53 +++++++++++++++++++++++-------------- core/src/signed_envelope.rs | 32 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/core/src/identity.rs b/core/src/identity.rs index 697ad1e8c13..9fa6a83a28f 100644 --- a/core/src/identity.rs +++ b/core/src/identity.rs @@ -30,6 +30,7 @@ pub mod error; use self::error::*; use crate::{PeerId, keys_proto}; +use std::convert::{TryFrom, TryInto}; /// Identity keypair of a node. /// @@ -152,7 +153,33 @@ impl PublicKey { pub fn into_protobuf_encoding(self) -> Vec { use prost::Message; - let public_key = match self { + let public_key = keys_proto::PublicKey::from(self); + + let mut buf = Vec::with_capacity(public_key.encoded_len()); + public_key.encode(&mut buf).expect("Vec provides capacity as needed"); + buf + } + + /// Decode a public key from a protobuf structure, e.g. read from storage + /// or received from another node. + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + use prost::Message; + + let pubkey = keys_proto::PublicKey::decode(bytes) + .map_err(|e| DecodingError::new("Protobuf").source(e))?; + + pubkey.try_into() + } + + /// Convert the `PublicKey` into the corresponding `PeerId`. + pub fn into_peer_id(self) -> PeerId { + self.into() + } +} + +impl From for keys_proto::PublicKey { + fn from(key: PublicKey) -> Self { + match key { PublicKey::Ed25519(key) => keys_proto::PublicKey { r#type: keys_proto::KeyType::Ed25519 as i32, @@ -170,22 +197,14 @@ impl PublicKey { r#type: keys_proto::KeyType::Secp256k1 as i32, data: key.encode().to_vec() } - }; - - let mut buf = Vec::with_capacity(public_key.encoded_len()); - public_key.encode(&mut buf).expect("Vec provides capacity as needed"); - buf + } } +} - /// Decode a public key from a protobuf structure, e.g. read from storage - /// or received from another node. - pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { - use prost::Message; - - #[allow(unused_mut)] // Due to conditional compilation. - let mut pubkey = keys_proto::PublicKey::decode(bytes) - .map_err(|e| DecodingError::new("Protobuf").source(e))?; +impl TryFrom for PublicKey { + type Error = DecodingError; + fn try_from(pubkey: keys_proto::PublicKey) -> Result { let key_type = keys_proto::KeyType::from_i32(pubkey.r#type) .ok_or_else(|| DecodingError::new(format!("unknown key type: {}", pubkey.r#type)))?; @@ -213,10 +232,4 @@ impl PublicKey { } } } - - /// Convert the `PublicKey` into the corresponding `PeerId`. - pub fn into_peer_id(self) -> PeerId { - self.into() - } } - diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index f22e6a9f951..490905d518b 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -2,6 +2,7 @@ use crate::identity::error::SigningError; use crate::identity::Keypair; use crate::PublicKey; use prost::bytes::BufMut; +use std::convert::TryInto; use unsigned_varint::encode::usize_buffer; // TODO: docs @@ -117,4 +118,35 @@ impl SignedEnvelope { // pub fn payload_unchecked(&self) -> Vec { // // } + + pub fn into_protobuf_encoding(self) -> Vec { + use prost::Message; + + let envelope = crate::envelope_proto::Envelope { + public_key: self.key.into(), + payload_type: self.payload_type, + payload: self.payload, + signature: self.signature, + }; + + let mut buf = Vec::with_capacity(envelope.encoded_len()); + envelope + .encode(&mut buf) + .expect("Vec provides capacity as needed"); + buf + } + + // TODO: wrap error to not expose `prost` as a dependency + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + use prost::Message; + + let envelope = crate::envelope_proto::Envelope::decode(bytes)?; + + Ok(Self { + key: envelope.public_key.try_into().unwrap(), // TODO: error handling + payload_type: envelope.payload_type, + payload: envelope.payload, + signature: envelope.signature, + }) + } } From 5a9aae3a89c603c84cb097beda74797d26b9acab Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 May 2021 15:03:48 +1000 Subject: [PATCH 014/242] Do proper error handling for decoding peer records --- core/src/lib.rs | 4 +- core/src/peer_record.rs | 108 ++++++++++++++++++++++++++++++++---- core/src/signed_envelope.rs | 78 +++++++++++++++++++++++--- 3 files changed, 169 insertions(+), 21 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index d782f1ec6eb..9041f02ef30 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -61,8 +61,8 @@ pub mod muxing; pub mod network; pub mod transport; pub mod upgrade; -mod signed_envelope; -mod peer_record; +pub mod signed_envelope; +pub mod peer_record; pub use multiaddr::Multiaddr; pub use multihash; diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index c6ca1fb3e95..a7cdde0a6d0 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -1,7 +1,8 @@ use crate::identity::Keypair; use crate::signed_envelope::SignedEnvelope; -use crate::{peer_record_proto, Multiaddr, PeerId}; +use crate::{peer_record_proto, signed_envelope, Multiaddr, PeerId}; use std::convert::TryInto; +use std::fmt; const PAYLOAD_TYPE: &str = "/libp2p/routing-state-record"; const DOMAIN_SEP: &str = "libp2p-routing-state"; @@ -38,21 +39,19 @@ impl PeerRecord { buf } - // TODO: wrap error to not expose `prost` as a dependency - pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { use prost::Message; let record = peer_record_proto::PeerRecord::decode(bytes)?; Ok(Self { - peer_id: PeerId::from_bytes(&record.peer_id).unwrap(), // TODO: Error handling + peer_id: PeerId::from_bytes(&record.peer_id)?, seq: record.seq, addresses: record .addresses .into_iter() .map(|a| a.multiaddr.try_into()) - .collect::, _>>() - .unwrap(), // TODO: error handlign + .collect::, _>>()?, }) } @@ -78,6 +77,58 @@ impl PeerRecord { } } +#[derive(Debug)] +pub enum DecodingError { + /// Failed to decode the provided bytes as a [`PeerRecord`]. + InvalidPeerRecord(prost::DecodeError), + /// Failed to decode the peer ID. + InvalidPeerId(multihash::Error), + /// Failed to decode a multi-address. + InvalidMultiaddr(multiaddr::Error), +} + +impl From for DecodingError { + fn from(e: prost::DecodeError) -> Self { + Self::InvalidPeerRecord(e) + } +} + +impl From for DecodingError { + fn from(e: multihash::Error) -> Self { + Self::InvalidPeerId(e) + } +} + +impl From for DecodingError { + fn from(e: multiaddr::Error) -> Self { + Self::InvalidMultiaddr(e) + } +} + +impl fmt::Display for DecodingError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DecodingError::InvalidPeerRecord(_) => { + write!(f, "Failed to decode bytes as PeerRecord") + } + DecodingError::InvalidPeerId(_) => write!(f, "Failed to decode bytes as PeerId"), + DecodingError::InvalidMultiaddr(_) => { + write!(f, "Failed to decode bytes as MultiAddress") + } + } + } +} + +impl std::error::Error for DecodingError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + DecodingError::InvalidPeerRecord(inner) => Some(inner), + DecodingError::InvalidPeerId(inner) => Some(inner), + DecodingError::InvalidMultiaddr(inner) => Some(inner), + } + } +} + // TODO: docs #[derive(Debug)] pub struct AuthenticatedPeerRecord { @@ -92,12 +143,9 @@ pub struct AuthenticatedPeerRecord { impl AuthenticatedPeerRecord { // TODO: docs - pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result { - let payload = envelope - .payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes()) - .unwrap(); // TODO: Error handling - - let record = PeerRecord::from_protobuf_encoding(payload).unwrap(); // TODO: Error handling + pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result { + let payload = envelope.payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes())?; + let record = PeerRecord::from_protobuf_encoding(payload)?; Ok(Self { inner: record, @@ -147,6 +195,42 @@ impl PartialEq for PeerRecord { } } +#[derive(Debug)] +pub enum FromEnvelopeError { + BadPayload(signed_envelope::ReadPayloadError), + InvalidPeerRecord(DecodingError), +} + +impl From for FromEnvelopeError { + fn from(e: signed_envelope::ReadPayloadError) -> Self { + Self::BadPayload(e) + } +} + +impl From for FromEnvelopeError { + fn from(e: DecodingError) -> Self { + Self::InvalidPeerRecord(e) + } +} + +impl fmt::Display for FromEnvelopeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BadPayload(_) => write!(f, "Failed to extract payload from envelope"), + Self::InvalidPeerRecord(_) => write!(f, "Failed to decode payload as PeerRecord"), + } + } +} + +impl std::error::Error for FromEnvelopeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InvalidPeerRecord(inner) => Some(inner), + Self::BadPayload(inner) => Some(inner), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index 490905d518b..8f6a883972e 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -1,8 +1,9 @@ use crate::identity::error::SigningError; use crate::identity::Keypair; -use crate::PublicKey; +use crate::{identity, PublicKey}; use prost::bytes::BufMut; use std::convert::TryInto; +use std::fmt; use unsigned_varint::encode::usize_buffer; // TODO: docs @@ -102,13 +103,16 @@ impl SignedEnvelope { &self, domain_separation: String, expected_payload_type: &[u8], - ) -> Result<&[u8], ()> { + ) -> Result<&[u8], ReadPayloadError> { if &self.payload_type != expected_payload_type { - panic!("bad payload type") // TODO: error handling + return Err(ReadPayloadError::UnexpectedPayloadType { + expected: expected_payload_type.to_vec(), + got: self.payload_type.clone(), + }); } if !self.verify(domain_separation) { - panic!("bad signature") // TODO: error handling + return Err(ReadPayloadError::InvalidSignature); } Ok(&self.payload) @@ -136,17 +140,77 @@ impl SignedEnvelope { buf } - // TODO: wrap error to not expose `prost` as a dependency - pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { use prost::Message; let envelope = crate::envelope_proto::Envelope::decode(bytes)?; Ok(Self { - key: envelope.public_key.try_into().unwrap(), // TODO: error handling + key: envelope.public_key.try_into()?, payload_type: envelope.payload_type, payload: envelope.payload, signature: envelope.signature, }) } } + +#[derive(Debug)] +pub enum DecodingError { + /// Decoding the provided bytes as a signed envelope failed. + InvalidEnvelope(prost::DecodeError), + /// The public key in the envelope could not be converted to our internal public key type. + InvalidPublicKey(identity::error::DecodingError), +} + +impl From for DecodingError { + fn from(e: prost::DecodeError) -> Self { + Self::InvalidEnvelope(e) + } +} + +impl From for DecodingError { + fn from(e: identity::error::DecodingError) -> Self { + Self::InvalidPublicKey(e) + } +} + +impl fmt::Display for DecodingError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidEnvelope(_) => write!(f, "Failed to decode envelope"), + Self::InvalidPublicKey(_) => write!(f, "Failed to convert public key"), + } + } +} + +impl std::error::Error for DecodingError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InvalidEnvelope(inner) => Some(inner), + Self::InvalidPublicKey(inner) => Some(inner), + } + } +} + +#[derive(Debug)] +pub enum ReadPayloadError { + /// The signature on the signed envelope does not verify with the provided domain separation string. + InvalidSignature, + /// The payload contained in the envelope is not of the expected type. + UnexpectedPayloadType { expected: Vec, got: Vec }, +} + +impl fmt::Display for ReadPayloadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidSignature => write!(f, "Invalid signature"), + Self::UnexpectedPayloadType { expected, got } => write!( + f, + "Unexpected payload type, expected {:?} but got {:?}", + expected, got + ), + } + } +} + +impl std::error::Error for ReadPayloadError {} From a15296e5201fb7c63be9494d0631ff22ad88eab5 Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 27 May 2021 12:43:05 +1000 Subject: [PATCH 015/242] Add feature to fix broken unsigned-varint dependency import --- protocols/rendezvous/src/codec.rs | 228 ++++++++++++++++++++---------- 1 file changed, 152 insertions(+), 76 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 459989ffdbc..4317251ff03 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -1,17 +1,17 @@ -use asynchronous_codec::{Encoder, BytesMut, Decoder, Bytes}; -use unsigned_varint::codec::UviBytes; +use asynchronous_codec::{Bytes, BytesMut, Decoder, Encoder}; +use libp2p_core::{peer_record, signed_envelope, AuthenticatedPeerRecord, SignedEnvelope}; use std::convert::{TryFrom, TryInto}; -use libp2p_core::{AuthenticatedPeerRecord, SignedEnvelope, signed_envelope, peer_record}; +use unsigned_varint::codec::UviBytes; #[derive(Debug)] pub enum Message { Register { namespace: String, ttl: Option, - record: AuthenticatedPeerRecord + record: AuthenticatedPeerRecord, }, SuccessfullyRegistered { - ttl: i64 + ttl: i64, }, FailedToRegister { error: ErrorCode, @@ -30,15 +30,14 @@ pub enum Message { // TODO cookie: Option }, FailedToDiscover { - error: ErrorCode + error: ErrorCode, }, } #[derive(Debug)] pub struct Registration { namespace: String, - record: AuthenticatedPeerRecord - // ttl: i64, TODO: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds + record: AuthenticatedPeerRecord, // ttl: i64, TODO: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds } #[derive(Debug)] @@ -61,7 +60,7 @@ pub enum Error { #[error("Failed to read/write")] // TODO: Better message Io(#[from] std::io::Error), #[error("Failed to convert wire message to internal data model")] - ConversionError(#[from] ConversionError) + ConversionError(#[from] ConversionError), } impl From for wire::Message { @@ -69,81 +68,90 @@ impl From for wire::Message { use wire::message::*; match message { - Message::Register { namespace, ttl, record } => wire::Message { + Message::Register { + namespace, + ttl, + record, + } => wire::Message { r#type: Some(MessageType::Register.into()), register: Some(Register { ns: Some(namespace), ttl, - signed_peer_record: Some(record.into_signed_envelope().into_protobuf_encoding()) + signed_peer_record: Some( + record.into_signed_envelope().into_protobuf_encoding(), + ), }), register_response: None, unregister: None, discover: None, - discover_response: None + discover_response: None, }, Message::SuccessfullyRegistered { ttl } => wire::Message { r#type: Some(MessageType::RegisterResponse.into()), register_response: Some(RegisterResponse { status: Some(ResponseStatus::Ok.into()), status_text: None, - ttl: Some(ttl) + ttl: Some(ttl), }), register: None, discover: None, unregister: None, - discover_response: None + discover_response: None, }, Message::FailedToRegister { error } => wire::Message { r#type: Some(MessageType::RegisterResponse.into()), register_response: Some(RegisterResponse { status: Some(ResponseStatus::from(error).into()), status_text: None, - ttl: None + ttl: None, }), register: None, discover: None, unregister: None, - discover_response: None + discover_response: None, }, Message::Unregister { namespace } => wire::Message { r#type: Some(MessageType::Unregister.into()), unregister: Some(Unregister { ns: Some(namespace), - id: None + id: None, }), register: None, register_response: None, discover: None, - discover_response: None + discover_response: None, }, Message::Discover { namespace } => wire::Message { r#type: Some(MessageType::Discover.into()), discover: Some(Discover { ns: namespace, cookie: None, - limit: None + limit: None, }), register: None, register_response: None, unregister: None, - discover_response: None + discover_response: None, }, Message::DiscoverResponse { registrations } => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), discover_response: Some(DiscoverResponse { - registrations: registrations.into_iter().map(|reggo| Register { - ns: Some(reggo.namespace), - ttl: None, - signed_peer_record: None - }).collect(), + registrations: registrations + .into_iter() + .map(|reggo| Register { + ns: Some(reggo.namespace), + ttl: None, + signed_peer_record: None, + }) + .collect(), status: Some(ResponseStatus::Ok.into()), status_text: None, - cookie: None + cookie: None, }), register: None, discover: None, unregister: None, - register_response: None + register_response: None, }, Message::FailedToDiscover { error } => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), @@ -151,12 +159,12 @@ impl From for wire::Message { registrations: Vec::new(), status: Some(ResponseStatus::from(error).into()), status_text: None, - cookie: None + cookie: None, }), register: None, discover: None, unregister: None, - register_response: None + register_response: None, }, } } @@ -169,56 +177,126 @@ impl TryFrom for Message { use wire::message::*; let message = match message { - wire::Message { r#type: Some(0), register: Some(Register { ns, ttl, signed_peer_record: Some(signed_peer_record) }), .. } => { - Message::Register { - namespace: ns.ok_or(ConversionError::MissingNamespace)?, - ttl, - record: AuthenticatedPeerRecord::from_signed_envelope(SignedEnvelope::from_protobuf_encoding(&signed_peer_record)?)? - } + wire::Message { + r#type: Some(0), + register: + Some(Register { + ns, + ttl, + signed_peer_record: Some(signed_peer_record), + }), + .. + } => Message::Register { + namespace: ns.ok_or(ConversionError::MissingNamespace)?, + ttl, + record: AuthenticatedPeerRecord::from_signed_envelope( + SignedEnvelope::from_protobuf_encoding(&signed_peer_record)?, + )?, }, - wire::Message { r#type: Some(1), register_response: Some(RegisterResponse { status: Some(0), ttl, .. }), .. } => { - Message::SuccessfullyRegistered { - ttl: ttl.ok_or(ConversionError::MissingTtl)? - } + wire::Message { + r#type: Some(1), + register_response: + Some(RegisterResponse { + status: Some(0), + ttl, + .. + }), + .. + } => Message::SuccessfullyRegistered { + ttl: ttl.ok_or(ConversionError::MissingTtl)?, }, - wire::Message { r#type: Some(1), register_response: Some(RegisterResponse { status: Some(0), ttl, .. }), .. } => { - Message::SuccessfullyRegistered { - ttl: ttl.ok_or(ConversionError::MissingTtl)? - } + wire::Message { + r#type: Some(3), + discover: Some(Discover { ns, .. }), + .. + } => Message::Discover { namespace: ns }, + wire::Message { + r#type: Some(4), + discover_response: + Some(DiscoverResponse { + registrations, + status: Some(0), + .. + }), + .. + } => Message::DiscoverResponse { + registrations: registrations + .into_iter() + .map(|reggo| { + Ok(Registration { + namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)?, + }) + }) + .collect::, ConversionError>>()?, }, - wire::Message { r#type: Some(1), register_response: Some(RegisterResponse { status: Some(error_code), .. }), .. } => { - Message::FailedToRegister { - error: wire::message::ResponseStatus::from_i32(error_code).ok_or(ConversionError::BadStatusCode)?.try_into()? - } + wire::Message { + r#type: Some(1), + register_response: + Some(RegisterResponse { + status: Some(error_code), + .. + }), + .. + } => Message::FailedToRegister { + error: wire::message::ResponseStatus::from_i32(error_code) + .ok_or(ConversionError::BadStatusCode)? + .try_into()?, }, - wire::Message { r#type: Some(2), unregister: Some(Unregister { ns, .. }), .. } => { - Message::Unregister { - namespace: ns.ok_or(ConversionError::MissingNamespace)? - } + wire::Message { + r#type: Some(2), + unregister: Some(Unregister { ns, .. }), + .. + } => Message::Unregister { + namespace: ns.ok_or(ConversionError::MissingNamespace)?, }, - wire::Message { r#type: Some(3), discover: Some(Discover { ns, .. }), .. } => { - Message::Discover { - namespace: ns - } + wire::Message { + r#type: Some(3), + discover: Some(Discover { ns, .. }), + .. + } => Message::Discover { namespace: ns }, + wire::Message { + r#type: Some(4), + discover_response: + Some(DiscoverResponse { + registrations, + status: Some(0), + .. + }), + .. + } => Message::DiscoverResponse { + registrations: registrations + .into_iter() + .map(|reggo| { + Ok(Registration { + namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)?, + record: AuthenticatedPeerRecord::from_signed_envelope( + SignedEnvelope::from_protobuf_encoding( + ®go + .signed_peer_record + .ok_or(ConversionError::MissingSignedPeerRecord)?, + )?, + )?, + }) + }) + .collect::, ConversionError>>()?, }, - wire::Message { r#type: Some(4), discover_response: Some(DiscoverResponse { registrations, status: Some(0), .. }), .. } => { - Message::DiscoverResponse { - registrations: registrations.into_iter().map(|reggo| Ok(Registration { - namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)?, - record: AuthenticatedPeerRecord::from_signed_envelope(SignedEnvelope::from_protobuf_encoding(®go.signed_peer_record.ok_or(ConversionError::MissingSignedPeerRecord)?)?)? - })).collect::, ConversionError>>()?, - } - }, - wire::Message { r#type: Some(4), discover_response: Some(DiscoverResponse { status: Some(error_code), .. }), .. } => { - let response_status = unsafe { - std::mem::transmute::<_, wire::message::ResponseStatus>(error_code) - }; + wire::Message { + r#type: Some(4), + discover_response: + Some(DiscoverResponse { + status: Some(error_code), + .. + }), + .. + } => { + let response_status = + unsafe { std::mem::transmute::<_, wire::message::ResponseStatus>(error_code) }; Message::FailedToDiscover { - error: response_status.try_into()? + error: response_status.try_into()?, } - }, - _ => return Err(ConversionError::InconsistentWireMessage) + } + _ => return Err(ConversionError::InconsistentWireMessage), }; Ok(message) @@ -300,9 +378,7 @@ impl Default for RendezvousCodec { let mut length_codec = UviBytes::default(); length_codec.set_max_len(1024 * 1024); // 1MB TODO clarify with spec what the default should be - Self { - length_codec - } + Self { length_codec } } } @@ -317,12 +393,12 @@ impl Encoder for RendezvousCodec { let mut buf = Vec::with_capacity(message.encoded_len()); - message.encode(&mut buf) + message + .encode(&mut buf) .expect("Buffer has sufficient capacity"); // length prefix the protobuf message, ensuring the max limit is not hit - self.length_codec - .encode(Bytes::from(buf), dst)?; + self.length_codec.encode(Bytes::from(buf), dst)?; Ok(()) } From 4208a02bae9915ad33e94c69a72d5fd44faa3dec Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 28 May 2021 16:00:37 +1000 Subject: [PATCH 016/242] WIP: Add behaviour and handler --- protocols/rendezvous/src/behaviour.rs | 142 ++++++++++-- protocols/rendezvous/src/codec.rs | 9 +- protocols/rendezvous/src/handler.rs | 300 +++++++++++++++++++++++--- protocols/rendezvous/src/lib.rs | 70 +----- 4 files changed, 395 insertions(+), 126 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index e3eba49c1bd..f40302f9027 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,22 +1,50 @@ -use crate::codec::{ErrorCode, Registration}; -use crate::handler::RendezvousHandler; +use crate::codec::{ErrorCode, Message, Registration}; +use crate::handler::{Input, RendezvousHandler}; use libp2p_core::connection::ConnectionId; use libp2p_core::{Multiaddr, PeerId}; use libp2p_swarm::{ - IntoProtocolsHandler, NetworkBehaviour, NetworkBehaviourAction, PollParameters, + NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet, VecDeque}; use std::task::{Context, Poll}; -struct Rendezvous; +struct Rendezvous { + events: VecDeque>, + registrations: HashMap>, +} impl Rendezvous { - pub fn register(&mut self, ns: String, rendezvous_node: PeerId) {} - pub fn unregister(&mut self, ns: String, rendezvous_node: PeerId) {} - pub fn discover(&mut self, ns: Option, rendezvous_node: PeerId) {} + pub fn register(&mut self, ns: String, rendezvous_node: PeerId) { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id: rendezvous_node, + event: Input::RegisterRequest { + namespace: ns, + ttl: None, + }, + handler: NotifyHandler::Any, + }); + } + + pub fn unregister(&mut self, ns: String, rendezvous_node: PeerId) { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id: rendezvous_node, + event: Input::UnregisterRequest { namespace: ns }, + handler: NotifyHandler::Any, + }); + } + pub fn discover(&mut self, ns: Option, rendezvous_node: PeerId) { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id: rendezvous_node, + event: Input::DiscoverRequest { namespace: ns }, + handler: NotifyHandler::Any, + }); + } } -enum OutEvent { +enum Event { Discovered { rendezvous_node: PeerId, ns: Vec, @@ -50,37 +78,107 @@ enum OutEvent { impl NetworkBehaviour for Rendezvous { type ProtocolsHandler = RendezvousHandler; - type OutEvent = OutEvent; + type OutEvent = Event; fn new_handler(&mut self) -> Self::ProtocolsHandler { RendezvousHandler::new() } - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { + fn addresses_of_peer(&mut self, _peer_id: &PeerId) -> Vec { todo!() } - fn inject_connected(&mut self, peer_id: &PeerId) { + fn inject_connected(&mut self, _peer_id: &PeerId) { todo!() } - fn inject_disconnected(&mut self, peer_id: &PeerId) { + fn inject_disconnected(&mut self, _peer_id: &PeerId) { todo!() } - fn inject_event(&mut self, peer_id: PeerId, connection: ConnectionId, event: _) { - todo!() + // inject event from handler + fn inject_event( + &mut self, + peer_id: PeerId, + _connection: ConnectionId, + event: crate::handler::HandlerEvent, + ) { + match event.0 { + Message::Register { namespace, ttl, .. } => { + todo!("add to hashmap"); + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::RegisterRequest { namespace, ttl }, + }) + } + Message::SuccessfullyRegistered { ttl } => { + // where to get namespace from + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::RegisteredWithRendezvousNode { + rendezvous_node: peer_id, + ns: "".to_string(), + }, + )) + } + Message::FailedToRegister { error } => { + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::FailedToRegisterWithRendezvousNode { + rendezvous_node: peer_id, + // todo: need to get the namespace somehow? + ns: "".to_string(), + err_code: error, + }, + )) + } + Message::Unregister { namespace } => { + // todo: implement Hash, Eq for Registration + // if let Some(peers) = self.registrations.get_mut() { + // if peers.contains(&peer_id) { + // peers.remove(&peer_id) + // } + // } + } + Message::Discover { namespace } => { + let registrations = todo!("get from hashmap using namespace"); + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::DiscoverResponse { registrations }, + }) + } + Message::DiscoverResponse { registrations } => { + self.events + .push_back(NetworkBehaviourAction::GenerateEvent(Event::Discovered { + rendezvous_node: peer_id, + ns: registrations, + })) + } + Message::FailedToDiscover { error } => self.events.push_back( + NetworkBehaviourAction::GenerateEvent(Event::FailedToDiscover { + rendezvous_node: peer_id, + err_code: error, + }), + ), + } } fn poll( &mut self, - cx: &mut Context<'_>, - params: &mut impl PollParameters< - SupportedProtocolsIter = _, - ListenedAddressesIter = _, - ExternalAddressesIter = _, + _cx: &mut Context<'_>, + _: &mut impl PollParameters, + ) -> Poll< + NetworkBehaviourAction< + ::InEvent, + Self::OutEvent, >, - ) -> Poll> { - todo!() + > { + if let Some(event) = self.events.pop_front() { + return Poll::Ready(event); + } + + Poll::Pending } } diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 4317251ff03..f5f0ba359ef 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -36,10 +36,16 @@ pub enum Message { #[derive(Debug)] pub struct Registration { - namespace: String, + pub namespace: String, record: AuthenticatedPeerRecord, // ttl: i64, TODO: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds } +impl Registration { + pub fn new(namespace: String, record: AuthenticatedPeerRecord) -> Self { + Self { namespace, record } + } +} + #[derive(Debug)] pub enum ErrorCode { InvalidNamespace, @@ -225,6 +231,7 @@ impl TryFrom for Message { .map(|reggo| { Ok(Registration { namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)?, + record: todo!(), }) }) .collect::, ConversionError>>()?, diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 7f8e1a410f1..564dd19841a 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,92 +1,193 @@ -use crate::codec::Message; use crate::codec::RendezvousCodec; +use crate::codec::{Message, Registration}; use crate::protocol; use asynchronous_codec::Framed; -use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; +use futures::Sink; +use libp2p_core::{InboundUpgrade, OutboundUpgrade}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; use std::collections::VecDeque; -use std::error::Error; -use std::fmt; +use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::Instant; +use void::Void; + +pub struct RendezvousRequest; pub struct RendezvousHandler { - /// Upgrade configuration for the rendezvous protocol. - listen_protocol: SubstreamProtocol, + outbound_substream: OutboundSubstreamState, - in_events: VecDeque, + inbound_substream: InboundSubstreamState, - /// Queue of values that we want to send to the remote. - out_events: VecDeque, + keep_alive: KeepAlive, } impl RendezvousHandler { pub fn new() -> Self { Self { - listen_protocol: SubstreamProtocol::new(Default::default(), ()), - in_events: VecDeque::new(), - out_events: VecDeque::new(), + outbound_substream: OutboundSubstreamState::Idle, + inbound_substream: InboundSubstreamState::Idle, + keep_alive: KeepAlive::Yes, } } } +pub struct HandlerEvent(pub Message); + #[derive(Debug)] -pub enum RendezvousHandlerOut { - DiscoverResponse(DiscoverResponse), - RegisterResponse, - RegisterRequest, - DiscoverRequest, +pub enum Input { + RegisterRequest { + namespace: String, + ttl: Option, + // TODO: Signed peer record field + }, + UnregisterRequest { + namespace: String, + // TODO: what is the `id` field here in the PB message + }, + DiscoverRequest { + namespace: Option, + // TODO limit: Option + // TODO cookie: Option + }, + RegisterResponse { + ttl: i64, + }, + DiscoverResponse { + registrations: Vec, + }, +} + +impl From for Message { + fn from(req: Input) -> Self { + match req { + Input::RegisterRequest { namespace, ttl } => Message::Register { + namespace, + ttl, + record: todo!(), + }, + Input::UnregisterRequest { namespace } => Message::Unregister { namespace }, + Input::DiscoverRequest { namespace } => Message::Discover { namespace }, + Input::RegisterResponse { ttl } => Message::SuccessfullyRegistered { ttl }, + Input::DiscoverResponse { registrations } => { + Message::DiscoverResponse { registrations } + } + } + } +} + +/// State of the inbound substream, opened either by us or by the remote. +enum InboundSubstreamState { + Idle, + /// Waiting for behaviour to respond to the inbound substream + WaitingToRespondToRemote(Framed, Message), + /// Waiting to send response to remote + PendingSend(Framed, Message), + PendingFlush(Framed), + Closing(Framed), + /// An error occurred during processing. + Poisoned, } -#[derive(Debug, Clone)] -pub enum RendezvousHandlerIn { - DiscoverRequest, - RegisterRequest, +/// State of the outbound substream, opened either by us or by the remote. +enum OutboundSubstreamState { + Idle, + WaitingUpgrade(Message), + /// Waiting to send a message to the remote. + PendingSend(Framed, Message), + /// Waiting to flush the substream so that the data arrives to the remote. + PendingFlush(Framed), + /// Waiting for remote to respond on the outbound substream + WaitingForRemoteToRespond(Framed), + Closing(Framed), + /// An error occurred during processing. + Poisoned, } impl ProtocolsHandler for RendezvousHandler { - type InEvent = RendezvousHandlerIn; - type OutEvent = RendezvousHandlerOut; - type Error = (); - type InboundOpenInfo = (); + type InEvent = Input; + type OutEvent = HandlerEvent; + type Error = crate::codec::Error; + type InboundOpenInfo = Message; type InboundProtocol = protocol::Rendezvous; - type OutboundOpenInfo = (); + type OutboundOpenInfo = Message; type OutboundProtocol = protocol::Rendezvous; fn listen_protocol(&self) -> SubstreamProtocol { let rendezvous_protocol = crate::protocol::Rendezvous::new(); - SubstreamProtocol::new(rendezvous_protocol, ()) + SubstreamProtocol::new(rendezvous_protocol, todo!()) } fn inject_fully_negotiated_inbound( &mut self, substream: >::Output, - _info: Self::InboundOpenInfo, + msg: Self::InboundOpenInfo, ) { + if let InboundSubstreamState::Idle = self.inbound_substream { + self.inbound_substream = + InboundSubstreamState::WaitingToRespondToRemote(substream, msg); + } } fn inject_fully_negotiated_outbound( &mut self, substream: >::Output, - _info: Self::OutboundOpenInfo, + msg: Self::OutboundOpenInfo, ) { + if let OutboundSubstreamState::Idle = self.outbound_substream { + self.outbound_substream = OutboundSubstreamState::PendingSend(substream, msg); + } } - fn inject_event(&mut self, message: RendezvousHandlerIn) {} + // event injected from NotifyHandler + fn inject_event(&mut self, req: Input) { + match &req { + Input::RegisterRequest { .. } => { + if let OutboundSubstreamState::Idle = self.outbound_substream { + self.outbound_substream = + OutboundSubstreamState::WaitingUpgrade(Message::from(req)) + } + } + Input::UnregisterRequest { .. } => { + if let OutboundSubstreamState::Idle = self.outbound_substream { + self.outbound_substream = + OutboundSubstreamState::WaitingUpgrade(Message::from(req)) + } + } + Input::DiscoverRequest { .. } => { + if let OutboundSubstreamState::Idle = self.outbound_substream { + self.outbound_substream = + OutboundSubstreamState::WaitingUpgrade(Message::from(req)) + } + } + Input::RegisterResponse { .. } => { + if let InboundSubstreamState::WaitingToRespondToRemote(substream, msg) = + std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned) + { + self.inbound_substream = InboundSubstreamState::PendingSend(substream, msg) + } + } + Input::DiscoverResponse { .. } => { + if let InboundSubstreamState::WaitingToRespondToRemote(substream, msg) = + std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned) + { + self.inbound_substream = InboundSubstreamState::PendingSend(substream, msg) + } + } + } + } fn inject_dial_upgrade_error( &mut self, - info: Self::OutboundOpenInfo, - error: ProtocolsHandlerUpgrErr<()>, + _info: Self::OutboundOpenInfo, + _error: ProtocolsHandlerUpgrErr, ) { todo!() } fn connection_keep_alive(&self) -> KeepAlive { - todo!() + self.keep_alive } fn poll( @@ -100,6 +201,135 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { - todo!() + match std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned) { + InboundSubstreamState::PendingSend(mut substream, message) => { + match Sink::poll_ready(Pin::new(&mut substream), cx) { + Poll::Ready(Ok(())) => { + match Sink::start_send(Pin::new(&mut substream), message) { + Ok(()) => { + self.inbound_substream = + InboundSubstreamState::PendingFlush(substream); + } + Err(e) => { + return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + } + } + } + Poll::Ready(Err(e)) => { + return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + } + Poll::Pending => { + self.keep_alive = KeepAlive::Yes; + self.inbound_substream = + InboundSubstreamState::PendingSend(substream, message); + } + } + } + InboundSubstreamState::PendingFlush(mut substream) => { + match Sink::poll_flush(Pin::new(&mut substream), cx) { + Poll::Ready(Ok(())) => { + self.inbound_substream = InboundSubstreamState::Idle; + } + Poll::Ready(Err(e)) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), + Poll::Pending => { + self.keep_alive = KeepAlive::Yes; + self.inbound_substream = InboundSubstreamState::PendingFlush(substream); + } + } + } + InboundSubstreamState::WaitingToRespondToRemote(_substream, msg) => { + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); + } + InboundSubstreamState::Closing(mut substream) => { + match Sink::poll_close(Pin::new(&mut substream), cx) { + Poll::Ready(..) => { + if let OutboundSubstreamState::Idle | OutboundSubstreamState::Poisoned = + self.outbound_substream + { + self.keep_alive = KeepAlive::No; + } + self.inbound_substream = InboundSubstreamState::Idle; + } + Poll::Pending => { + self.inbound_substream = InboundSubstreamState::Closing(substream); + } + } + } + InboundSubstreamState::Idle => self.outbound_substream = OutboundSubstreamState::Idle, + InboundSubstreamState::Poisoned => { + self.outbound_substream = OutboundSubstreamState::Idle + } + } + + match std::mem::replace( + &mut self.outbound_substream, + OutboundSubstreamState::Poisoned, + ) { + OutboundSubstreamState::WaitingUpgrade(msg) => { + self.outbound_substream = OutboundSubstreamState::WaitingUpgrade(msg); + } + OutboundSubstreamState::PendingSend(mut substream, message) => { + match Sink::poll_ready(Pin::new(&mut substream), cx) { + Poll::Ready(Ok(())) => { + match Sink::start_send(Pin::new(&mut substream), message) { + Ok(()) => { + self.outbound_substream = + OutboundSubstreamState::PendingFlush(substream); + } + Err(e) => { + return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + } + } + } + Poll::Ready(Err(e)) => { + return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + } + Poll::Pending => { + self.keep_alive = KeepAlive::Yes; + self.outbound_substream = + OutboundSubstreamState::PendingSend(substream, message); + } + } + } + OutboundSubstreamState::PendingFlush(mut substream) => { + match Sink::poll_flush(Pin::new(&mut substream), cx) { + Poll::Ready(Ok(())) => { + self.outbound_substream = + OutboundSubstreamState::WaitingForRemoteToRespond(substream) + } + Poll::Ready(Err(e)) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), + Poll::Pending => { + self.keep_alive = KeepAlive::Yes; + self.outbound_substream = OutboundSubstreamState::PendingFlush(substream); + } + } + } + OutboundSubstreamState::WaitingForRemoteToRespond(substream) => { + let message: Message = todo!("Sink::read??"); + self.outbound_substream = OutboundSubstreamState::Idle; + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(message))); + } + OutboundSubstreamState::Closing(mut substream) => { + match Sink::poll_close(Pin::new(&mut substream), cx) { + Poll::Ready(..) => { + if let InboundSubstreamState::Idle | InboundSubstreamState::Poisoned = + self.inbound_substream + { + self.keep_alive = KeepAlive::No; + } + self.outbound_substream = OutboundSubstreamState::Idle; + } + Poll::Pending => { + self.outbound_substream = OutboundSubstreamState::Closing(substream); + } + } + } + OutboundSubstreamState::Idle => self.outbound_substream = OutboundSubstreamState::Idle, + OutboundSubstreamState::Poisoned => { + self.outbound_substream = OutboundSubstreamState::Poisoned + } + } + + Poll::Pending } } diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 417a49f0c62..a18082bd40c 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,70 +1,4 @@ +mod behaviour; +mod codec; mod handler; mod protocol; -mod codec; - -use libp2p_core::connection::ConnectionId; -use libp2p_core::{Multiaddr, PeerId}; -use libp2p_swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}; -use std::collections::VecDeque; -use std::task::{Context, Poll}; - -// -// pub struct Rendezvous { -// #[behaviour(ignore)] -// events: VecDeque, -// } -// -// /// Event generated by the `Ping` network behaviour. -// #[derive(Debug)] -// pub struct RendezvousEvent { -// /// The peer ID of the remote. -// pub peer: PeerId, -// } -// -// impl Rendezvous { -// /// Creates a new `Ping` network behaviour with the given configuration. -// pub fn new() -> Self { -// Self { -// events: VecDeque::new(), -// } -// } -// } -// -// impl Default for Rendezvous { -// fn default() -> Self { -// Rendezvous::new() -// } -// } -// -// impl NetworkBehaviour for Rendezvous { -// type ProtocolsHandler = RendezvousHandler; -// type OutEvent = RendezvousEvent; -// -// fn new_handler(&mut self) -> Self::ProtocolsHandler { -// RendezvousHandler::new() -// } -// -// fn addresses_of_peer(&mut self, _peer_id: &PeerId) -> Vec { -// Vec::new() -// } -// -// fn inject_connected(&mut self, _: &PeerId) {} -// -// fn inject_disconnected(&mut self, _: &PeerId) {} -// -// fn inject_event(&mut self, peer: PeerId, _: ConnectionId, result: PingResult) { -// self.events.push_front(RendezvousEvent { peer }) -// } -// -// fn poll( -// &mut self, -// _: &mut Context<'_>, -// _: &mut impl PollParameters, -// ) -> Poll> { -// if let Some(e) = self.events.pop_back() { -// Poll::Ready(NetworkBehaviourAction::GenerateEvent(e)) -// } else { -// Poll::Pending -// } -// } -// } From d5ea66bac502d3fd56ed27dc26e242638bdfea3f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 28 May 2021 16:38:52 +1000 Subject: [PATCH 017/242] Remove usage of unsafe code --- protocols/rendezvous/src/codec.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index f5f0ba359ef..7d513a21fd0 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -295,14 +295,11 @@ impl TryFrom for Message { .. }), .. - } => { - let response_status = - unsafe { std::mem::transmute::<_, wire::message::ResponseStatus>(error_code) }; - - Message::FailedToDiscover { - error: response_status.try_into()?, - } - } + } => Message::FailedToDiscover { + error: wire::message::ResponseStatus::from_i32(error_code) + .ok_or(ConversionError::BadStatusCode)? + .try_into()?, + }, _ => return Err(ConversionError::InconsistentWireMessage), }; From 2166e7ed95242f75cb81f937014300b88bdc41e9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 28 May 2021 16:41:42 +1000 Subject: [PATCH 018/242] Re-order contents of codec module by importance --- protocols/rendezvous/src/codec.rs | 109 +++++++++++++++--------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 7d513a21fd0..e2127ec854c 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -57,6 +57,61 @@ pub enum ErrorCode { Unavailable, } + +pub struct RendezvousCodec { + /// Codec to encode/decode the Unsigned varint length prefix of the frames. + length_codec: UviBytes, +} + +impl Default for RendezvousCodec { + fn default() -> Self { + let mut length_codec = UviBytes::default(); + length_codec.set_max_len(1024 * 1024); // 1MB TODO clarify with spec what the default should be + + Self { length_codec } + } +} + +impl Encoder for RendezvousCodec { + type Item = Message; + type Error = Error; + + fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { + use prost::Message; + + let message = wire::Message::from(item); + + let mut buf = Vec::with_capacity(message.encoded_len()); + + message + .encode(&mut buf) + .expect("Buffer has sufficient capacity"); + + // length prefix the protobuf message, ensuring the max limit is not hit + self.length_codec.encode(Bytes::from(buf), dst)?; + + Ok(()) + } +} + +impl Decoder for RendezvousCodec { + type Item = Message; + type Error = Error; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + use prost::Message; + + let message = match self.length_codec.decode(src)? { + Some(p) => p, + None => return Ok(None), + }; + + let message = wire::Message::decode(message)?; + + Ok(Some(message.try_into()?)) + } +} + #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Failed to encode message as bytes")] @@ -372,60 +427,6 @@ impl From for ConversionError { #[error("The provided response code is not an error code")] pub struct NotAnError; -pub struct RendezvousCodec { - /// Codec to encode/decode the Unsigned varint length prefix of the frames. - length_codec: UviBytes, -} - -impl Default for RendezvousCodec { - fn default() -> Self { - let mut length_codec = UviBytes::default(); - length_codec.set_max_len(1024 * 1024); // 1MB TODO clarify with spec what the default should be - - Self { length_codec } - } -} - -impl Encoder for RendezvousCodec { - type Item = Message; - type Error = Error; - - fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { - use prost::Message; - - let message = wire::Message::from(item); - - let mut buf = Vec::with_capacity(message.encoded_len()); - - message - .encode(&mut buf) - .expect("Buffer has sufficient capacity"); - - // length prefix the protobuf message, ensuring the max limit is not hit - self.length_codec.encode(Bytes::from(buf), dst)?; - - Ok(()) - } -} - -impl Decoder for RendezvousCodec { - type Item = Message; - type Error = Error; - - fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - use prost::Message; - - let message = match self.length_codec.decode(src)? { - Some(p) => p, - None => return Ok(None), - }; - - let message = wire::Message::decode(message)?; - - Ok(Some(message.try_into()?)) - } -} - mod wire { include!(concat!(env!("OUT_DIR"), "/rendezvous.pb.rs")); } From 4bbf56dae4144e20a97c31c3bfb3ff1961e723a2 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 28 May 2021 16:52:07 +1000 Subject: [PATCH 019/242] Introduce `NewRegistration` type so we have a place for the default TTL --- protocols/rendezvous/src/behaviour.rs | 5 +++- protocols/rendezvous/src/codec.rs | 40 ++++++++++++++++----------- protocols/rendezvous/src/handler.rs | 6 +--- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index f40302f9027..7389f259b81 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -104,7 +104,10 @@ impl NetworkBehaviour for Rendezvous { event: crate::handler::HandlerEvent, ) { match event.0 { - Message::Register { namespace, ttl, .. } => { + Message::Register(new_reggo) => { + let namespace = new_reggo.namespace; + let ttl = unimplemented!(); + todo!("add to hashmap"); self.events .push_back(NetworkBehaviourAction::NotifyHandler { diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index e2127ec854c..1b767abc726 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -5,11 +5,7 @@ use unsigned_varint::codec::UviBytes; #[derive(Debug)] pub enum Message { - Register { - namespace: String, - ttl: Option, - record: AuthenticatedPeerRecord, - }, + Register(NewRegistration), SuccessfullyRegistered { ttl: i64, }, @@ -35,17 +31,30 @@ pub enum Message { } #[derive(Debug)] -pub struct Registration { +pub struct NewRegistration { pub namespace: String, - record: AuthenticatedPeerRecord, // ttl: i64, TODO: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds + pub record: AuthenticatedPeerRecord, + ttl: Option, } -impl Registration { - pub fn new(namespace: String, record: AuthenticatedPeerRecord) -> Self { - Self { namespace, record } +/// If unspecified, rendezvous nodes should assume a TTL of 2h. +/// +/// See https://github.com/libp2p/specs/blob/d21418638d5f09f2a4e5a1ceca17058df134a300/rendezvous/README.md#L116-L117. +const DEFAULT_TTL: i64 = 60 * 60 * 2; + +impl NewRegistration { + pub fn effective_ttl(&self) -> i64 { + self.ttl.unwrap_or(DEFAULT_TTL) } } +#[derive(Debug)] +pub struct Registration { + pub namespace: String, + pub record: AuthenticatedPeerRecord, + // pub ttl: i64, // TODO: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds +} + #[derive(Debug)] pub enum ErrorCode { InvalidNamespace, @@ -57,7 +66,6 @@ pub enum ErrorCode { Unavailable, } - pub struct RendezvousCodec { /// Codec to encode/decode the Unsigned varint length prefix of the frames. length_codec: UviBytes, @@ -129,11 +137,11 @@ impl From for wire::Message { use wire::message::*; match message { - Message::Register { + Message::Register(NewRegistration { namespace, - ttl, record, - } => wire::Message { + ttl, + }) => wire::Message { r#type: Some(MessageType::Register.into()), register: Some(Register { ns: Some(namespace), @@ -247,13 +255,13 @@ impl TryFrom for Message { signed_peer_record: Some(signed_peer_record), }), .. - } => Message::Register { + } => Message::Register(NewRegistration { namespace: ns.ok_or(ConversionError::MissingNamespace)?, ttl, record: AuthenticatedPeerRecord::from_signed_envelope( SignedEnvelope::from_protobuf_encoding(&signed_peer_record)?, )?, - }, + }), wire::Message { r#type: Some(1), register_response: diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 564dd19841a..39e854e9dba 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -62,11 +62,7 @@ pub enum Input { impl From for Message { fn from(req: Input) -> Self { match req { - Input::RegisterRequest { namespace, ttl } => Message::Register { - namespace, - ttl, - record: todo!(), - }, + Input::RegisterRequest { namespace, ttl } => Message::Register(todo!()), Input::UnregisterRequest { namespace } => Message::Unregister { namespace }, Input::DiscoverRequest { namespace } => Message::Discover { namespace }, Input::RegisterResponse { ttl } => Message::SuccessfullyRegistered { ttl }, From c36ac8b664722580ef3673e6e1d56f0b417327f3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 28 May 2021 16:59:25 +1000 Subject: [PATCH 020/242] Make `ttl` public so we can actually construct the type --- 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 1b767abc726..5212c262a87 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -34,7 +34,7 @@ pub enum Message { pub struct NewRegistration { pub namespace: String, pub record: AuthenticatedPeerRecord, - ttl: Option, + pub ttl: Option, } /// If unspecified, rendezvous nodes should assume a TTL of 2h. From b45688d4d6eeb81af6649c81fbd43c4451f53b96 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 28 May 2021 17:00:16 +1000 Subject: [PATCH 021/242] Actually a constructor is better --- protocols/rendezvous/src/codec.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 5212c262a87..7f90e4eb91c 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -34,7 +34,7 @@ pub enum Message { pub struct NewRegistration { pub namespace: String, pub record: AuthenticatedPeerRecord, - pub ttl: Option, + ttl: Option, } /// If unspecified, rendezvous nodes should assume a TTL of 2h. @@ -43,6 +43,14 @@ pub struct NewRegistration { const DEFAULT_TTL: i64 = 60 * 60 * 2; impl NewRegistration { + pub fn new(namespace: String, record: AuthenticatedPeerRecord, ttl: Option) -> Self { + Self { + namespace, + record, + ttl + } + } + pub fn effective_ttl(&self) -> i64 { self.ttl.unwrap_or(DEFAULT_TTL) } From 1dc258050919a8ce56dca14de8dab4347a63fd38 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 28 May 2021 17:19:56 +1000 Subject: [PATCH 022/242] Use convenience _unpin functions instead of manual pinning --- protocols/rendezvous/src/handler.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 39e854e9dba..d6d582c4deb 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -2,7 +2,7 @@ use crate::codec::RendezvousCodec; use crate::codec::{Message, Registration}; use crate::protocol; use asynchronous_codec::Framed; -use futures::Sink; +use futures::SinkExt; use libp2p_core::{InboundUpgrade, OutboundUpgrade}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, @@ -199,9 +199,9 @@ impl ProtocolsHandler for RendezvousHandler { > { match std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned) { InboundSubstreamState::PendingSend(mut substream, message) => { - match Sink::poll_ready(Pin::new(&mut substream), cx) { + match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => { - match Sink::start_send(Pin::new(&mut substream), message) { + match substream.start_send_unpin(message) { Ok(()) => { self.inbound_substream = InboundSubstreamState::PendingFlush(substream); @@ -222,7 +222,7 @@ impl ProtocolsHandler for RendezvousHandler { } } InboundSubstreamState::PendingFlush(mut substream) => { - match Sink::poll_flush(Pin::new(&mut substream), cx) { + match substream.poll_flush_unpin(cx) { Poll::Ready(Ok(())) => { self.inbound_substream = InboundSubstreamState::Idle; } @@ -237,7 +237,7 @@ impl ProtocolsHandler for RendezvousHandler { return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); } InboundSubstreamState::Closing(mut substream) => { - match Sink::poll_close(Pin::new(&mut substream), cx) { + match substream.poll_close_unpin(cx) { Poll::Ready(..) => { if let OutboundSubstreamState::Idle | OutboundSubstreamState::Poisoned = self.outbound_substream @@ -265,9 +265,9 @@ impl ProtocolsHandler for RendezvousHandler { self.outbound_substream = OutboundSubstreamState::WaitingUpgrade(msg); } OutboundSubstreamState::PendingSend(mut substream, message) => { - match Sink::poll_ready(Pin::new(&mut substream), cx) { + match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => { - match Sink::start_send(Pin::new(&mut substream), message) { + match substream.start_send_unpin(message) { Ok(()) => { self.outbound_substream = OutboundSubstreamState::PendingFlush(substream); @@ -288,7 +288,7 @@ impl ProtocolsHandler for RendezvousHandler { } } OutboundSubstreamState::PendingFlush(mut substream) => { - match Sink::poll_flush(Pin::new(&mut substream), cx) { + match substream.poll_flush_unpin(cx) { Poll::Ready(Ok(())) => { self.outbound_substream = OutboundSubstreamState::WaitingForRemoteToRespond(substream) @@ -306,7 +306,7 @@ impl ProtocolsHandler for RendezvousHandler { return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(message))); } OutboundSubstreamState::Closing(mut substream) => { - match Sink::poll_close(Pin::new(&mut substream), cx) { + match substream.poll_close_unpin(cx) { Poll::Ready(..) => { if let InboundSubstreamState::Idle | InboundSubstreamState::Poisoned = self.inbound_substream From e079510bcd88be0c534b4f2719fd917b64da1da1 Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 28 May 2021 18:12:26 +1000 Subject: [PATCH 023/242] Add intermediate states to handler --- protocols/rendezvous/src/handler.rs | 174 ++++++++++++++++------------ 1 file changed, 97 insertions(+), 77 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index d6d582c4deb..30c176c2faa 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,8 +1,9 @@ -use crate::codec::RendezvousCodec; +use crate::codec::{Error, RendezvousCodec}; use crate::codec::{Message, Registration}; use crate::protocol; +use crate::protocol::Rendezvous; use asynchronous_codec::Framed; -use futures::SinkExt; +use futures::{SinkExt, Stream, StreamExt}; use libp2p_core::{InboundUpgrade, OutboundUpgrade}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, @@ -77,7 +78,9 @@ impl From for Message { enum InboundSubstreamState { Idle, /// Waiting for behaviour to respond to the inbound substream - WaitingToRespondToRemote(Framed, Message), + Reading(Framed), + /// Waiting for behaviour to respond to the inbound substream + WaitingToRespondToRemote(Framed), /// Waiting to send response to remote PendingSend(Framed, Message), PendingFlush(Framed), @@ -89,7 +92,8 @@ enum InboundSubstreamState { /// State of the outbound substream, opened either by us or by the remote. enum OutboundSubstreamState { Idle, - WaitingUpgrade(Message), + Start(Message), + WaitingUpgrade, /// Waiting to send a message to the remote. PendingSend(Framed, Message), /// Waiting to flush the substream so that the data arrives to the remote. @@ -105,24 +109,23 @@ impl ProtocolsHandler for RendezvousHandler { type InEvent = Input; type OutEvent = HandlerEvent; type Error = crate::codec::Error; - type InboundOpenInfo = Message; + type InboundOpenInfo = (); type InboundProtocol = protocol::Rendezvous; type OutboundOpenInfo = Message; type OutboundProtocol = protocol::Rendezvous; fn listen_protocol(&self) -> SubstreamProtocol { let rendezvous_protocol = crate::protocol::Rendezvous::new(); - SubstreamProtocol::new(rendezvous_protocol, todo!()) + SubstreamProtocol::new(rendezvous_protocol, ()) } fn inject_fully_negotiated_inbound( &mut self, substream: >::Output, - msg: Self::InboundOpenInfo, + _msg: Self::InboundOpenInfo, ) { if let InboundSubstreamState::Idle = self.inbound_substream { - self.inbound_substream = - InboundSubstreamState::WaitingToRespondToRemote(substream, msg); + self.inbound_substream = InboundSubstreamState::Reading(substream); } } @@ -138,40 +141,44 @@ impl ProtocolsHandler for RendezvousHandler { // event injected from NotifyHandler fn inject_event(&mut self, req: Input) { - match &req { - Input::RegisterRequest { .. } => { - if let OutboundSubstreamState::Idle = self.outbound_substream { - self.outbound_substream = - OutboundSubstreamState::WaitingUpgrade(Message::from(req)) - } - } - Input::UnregisterRequest { .. } => { - if let OutboundSubstreamState::Idle = self.outbound_substream { - self.outbound_substream = - OutboundSubstreamState::WaitingUpgrade(Message::from(req)) - } - } - Input::DiscoverRequest { .. } => { - if let OutboundSubstreamState::Idle = self.outbound_substream { - self.outbound_substream = - OutboundSubstreamState::WaitingUpgrade(Message::from(req)) - } + let (inbound_substream, outbound_substream) = match ( + &req, + std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned), + std::mem::replace( + &mut self.outbound_substream, + OutboundSubstreamState::Poisoned, + ), + ) { + (Input::RegisterRequest { .. }, inbound, OutboundSubstreamState::Idle) => { + (inbound, OutboundSubstreamState::Start(Message::from(req))) } - Input::RegisterResponse { .. } => { - if let InboundSubstreamState::WaitingToRespondToRemote(substream, msg) = - std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned) - { - self.inbound_substream = InboundSubstreamState::PendingSend(substream, msg) - } + (Input::UnregisterRequest { .. }, inbound, OutboundSubstreamState::Idle) => { + (inbound, OutboundSubstreamState::Start(Message::from(req))) } - Input::DiscoverResponse { .. } => { - if let InboundSubstreamState::WaitingToRespondToRemote(substream, msg) = - std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned) - { - self.inbound_substream = InboundSubstreamState::PendingSend(substream, msg) - } + (Input::DiscoverRequest { .. }, inbound, OutboundSubstreamState::Idle) => { + (inbound, OutboundSubstreamState::Start(Message::from(req))) } - } + ( + Input::RegisterResponse { .. }, + InboundSubstreamState::WaitingToRespondToRemote(substream), + outbound, + ) => ( + InboundSubstreamState::PendingSend(substream, todo!()), + outbound, + ), + ( + Input::DiscoverResponse { .. }, + InboundSubstreamState::WaitingToRespondToRemote(substream), + outbound, + ) => ( + InboundSubstreamState::PendingSend(substream, todo!()), + outbound, + ), + (_) => unreachable!("Handler in invalid state"), + }; + + self.inbound_substream = inbound_substream; + self.outbound_substream = outbound_substream; } fn inject_dial_upgrade_error( @@ -200,17 +207,14 @@ impl ProtocolsHandler for RendezvousHandler { match std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned) { InboundSubstreamState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => { - match substream.start_send_unpin(message) { - Ok(()) => { - self.inbound_substream = - InboundSubstreamState::PendingFlush(substream); - } - Err(e) => { - return Poll::Ready(ProtocolsHandlerEvent::Close(e)); - } + Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { + Ok(()) => { + self.inbound_substream = InboundSubstreamState::PendingFlush(substream); } - } + Err(e) => { + return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + } + }, Poll::Ready(Err(e)) => { return Poll::Ready(ProtocolsHandlerEvent::Close(e)); } @@ -233,24 +237,36 @@ impl ProtocolsHandler for RendezvousHandler { } } } - InboundSubstreamState::WaitingToRespondToRemote(_substream, msg) => { - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); + InboundSubstreamState::Reading(mut substream) => match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(msg))) => { + self.inbound_substream = + InboundSubstreamState::WaitingToRespondToRemote(substream); + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); + } + Poll::Ready(Some(Err(_e))) => { + todo!() + } + Poll::Ready(None) => todo!(), + Poll::Pending => { + self.inbound_substream = InboundSubstreamState::Reading(substream); + } + }, + InboundSubstreamState::WaitingToRespondToRemote(substream) => { + self.inbound_substream = InboundSubstreamState::WaitingToRespondToRemote(substream); } - InboundSubstreamState::Closing(mut substream) => { - match substream.poll_close_unpin(cx) { - Poll::Ready(..) => { - if let OutboundSubstreamState::Idle | OutboundSubstreamState::Poisoned = - self.outbound_substream - { - self.keep_alive = KeepAlive::No; - } - self.inbound_substream = InboundSubstreamState::Idle; - } - Poll::Pending => { - self.inbound_substream = InboundSubstreamState::Closing(substream); + InboundSubstreamState::Closing(mut substream) => match substream.poll_close_unpin(cx) { + Poll::Ready(..) => { + if let OutboundSubstreamState::Idle | OutboundSubstreamState::Poisoned = + self.outbound_substream + { + self.keep_alive = KeepAlive::No; } + self.inbound_substream = InboundSubstreamState::Idle; } - } + Poll::Pending => { + self.inbound_substream = InboundSubstreamState::Closing(substream); + } + }, InboundSubstreamState::Idle => self.outbound_substream = OutboundSubstreamState::Idle, InboundSubstreamState::Poisoned => { self.outbound_substream = OutboundSubstreamState::Idle @@ -261,22 +277,26 @@ impl ProtocolsHandler for RendezvousHandler { &mut self.outbound_substream, OutboundSubstreamState::Poisoned, ) { - OutboundSubstreamState::WaitingUpgrade(msg) => { - self.outbound_substream = OutboundSubstreamState::WaitingUpgrade(msg); + OutboundSubstreamState::Start(msg) => { + self.outbound_substream = OutboundSubstreamState::WaitingUpgrade; + return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(Rendezvous, msg), + }); + } + OutboundSubstreamState::WaitingUpgrade => { + self.outbound_substream = OutboundSubstreamState::WaitingUpgrade } OutboundSubstreamState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => { - match substream.start_send_unpin(message) { - Ok(()) => { - self.outbound_substream = - OutboundSubstreamState::PendingFlush(substream); - } - Err(e) => { - return Poll::Ready(ProtocolsHandlerEvent::Close(e)); - } + Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { + Ok(()) => { + self.outbound_substream = + OutboundSubstreamState::PendingFlush(substream); } - } + Err(e) => { + return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + } + }, Poll::Ready(Err(e)) => { return Poll::Ready(ProtocolsHandlerEvent::Close(e)); } From a03868a72833ad3af247bc3677335ddd699cb30d Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 28 May 2021 18:17:40 +1000 Subject: [PATCH 024/242] Rename substream states --- protocols/rendezvous/src/handler.rs | 191 ++++++++++++---------------- 1 file changed, 80 insertions(+), 111 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 30c176c2faa..5cd66af37e8 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -17,9 +17,9 @@ use void::Void; pub struct RendezvousRequest; pub struct RendezvousHandler { - outbound_substream: OutboundSubstreamState, + outbound_substream: OutboundState, - inbound_substream: InboundSubstreamState, + inbound_substream: InboundState, keep_alive: KeepAlive, } @@ -27,8 +27,8 @@ pub struct RendezvousHandler { impl RendezvousHandler { pub fn new() -> Self { Self { - outbound_substream: OutboundSubstreamState::Idle, - inbound_substream: InboundSubstreamState::Idle, + outbound_substream: OutboundState::None, + inbound_substream: InboundState::None, keep_alive: KeepAlive::Yes, } } @@ -75,12 +75,12 @@ impl From for Message { } /// State of the inbound substream, opened either by us or by the remote. -enum InboundSubstreamState { - Idle, +enum InboundState { + None, /// Waiting for behaviour to respond to the inbound substream Reading(Framed), /// Waiting for behaviour to respond to the inbound substream - WaitingToRespondToRemote(Framed), + WaitForBehaviour(Framed), /// Waiting to send response to remote PendingSend(Framed, Message), PendingFlush(Framed), @@ -90,8 +90,8 @@ enum InboundSubstreamState { } /// State of the outbound substream, opened either by us or by the remote. -enum OutboundSubstreamState { - Idle, +enum OutboundState { + None, Start(Message), WaitingUpgrade, /// Waiting to send a message to the remote. @@ -99,7 +99,7 @@ enum OutboundSubstreamState { /// Waiting to flush the substream so that the data arrives to the remote. PendingFlush(Framed), /// Waiting for remote to respond on the outbound substream - WaitingForRemoteToRespond(Framed), + WaitForRemote(Framed), Closing(Framed), /// An error occurred during processing. Poisoned, @@ -124,8 +124,8 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, _msg: Self::InboundOpenInfo, ) { - if let InboundSubstreamState::Idle = self.inbound_substream { - self.inbound_substream = InboundSubstreamState::Reading(substream); + if let InboundState::None = self.inbound_substream { + self.inbound_substream = InboundState::Reading(substream); } } @@ -134,8 +134,8 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, msg: Self::OutboundOpenInfo, ) { - if let OutboundSubstreamState::Idle = self.outbound_substream { - self.outbound_substream = OutboundSubstreamState::PendingSend(substream, msg); + if let OutboundState::None = self.outbound_substream { + self.outbound_substream = OutboundState::PendingSend(substream, msg); } } @@ -143,37 +143,28 @@ impl ProtocolsHandler for RendezvousHandler { fn inject_event(&mut self, req: Input) { let (inbound_substream, outbound_substream) = match ( &req, - std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned), - std::mem::replace( - &mut self.outbound_substream, - OutboundSubstreamState::Poisoned, - ), + std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned), + std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned), ) { - (Input::RegisterRequest { .. }, inbound, OutboundSubstreamState::Idle) => { - (inbound, OutboundSubstreamState::Start(Message::from(req))) + (Input::RegisterRequest { .. }, inbound, OutboundState::None) => { + (inbound, OutboundState::Start(Message::from(req))) } - (Input::UnregisterRequest { .. }, inbound, OutboundSubstreamState::Idle) => { - (inbound, OutboundSubstreamState::Start(Message::from(req))) + (Input::UnregisterRequest { .. }, inbound, OutboundState::None) => { + (inbound, OutboundState::Start(Message::from(req))) } - (Input::DiscoverRequest { .. }, inbound, OutboundSubstreamState::Idle) => { - (inbound, OutboundSubstreamState::Start(Message::from(req))) + (Input::DiscoverRequest { .. }, inbound, OutboundState::None) => { + (inbound, OutboundState::Start(Message::from(req))) } ( Input::RegisterResponse { .. }, - InboundSubstreamState::WaitingToRespondToRemote(substream), + InboundState::WaitForBehaviour(substream), outbound, - ) => ( - InboundSubstreamState::PendingSend(substream, todo!()), - outbound, - ), + ) => (InboundState::PendingSend(substream, todo!()), outbound), ( Input::DiscoverResponse { .. }, - InboundSubstreamState::WaitingToRespondToRemote(substream), - outbound, - ) => ( - InboundSubstreamState::PendingSend(substream, todo!()), + InboundState::WaitForBehaviour(substream), outbound, - ), + ) => (InboundState::PendingSend(substream, todo!()), outbound), (_) => unreachable!("Handler in invalid state"), }; @@ -204,12 +195,12 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { - match std::mem::replace(&mut self.inbound_substream, InboundSubstreamState::Poisoned) { - InboundSubstreamState::PendingSend(mut substream, message) => { + match std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned) { + InboundState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { Ok(()) => { - self.inbound_substream = InboundSubstreamState::PendingFlush(substream); + self.inbound_substream = InboundState::PendingFlush(substream); } Err(e) => { return Poll::Ready(ProtocolsHandlerEvent::Close(e)); @@ -220,27 +211,23 @@ impl ProtocolsHandler for RendezvousHandler { } Poll::Pending => { self.keep_alive = KeepAlive::Yes; - self.inbound_substream = - InboundSubstreamState::PendingSend(substream, message); + self.inbound_substream = InboundState::PendingSend(substream, message); } } } - InboundSubstreamState::PendingFlush(mut substream) => { - match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => { - self.inbound_substream = InboundSubstreamState::Idle; - } - Poll::Ready(Err(e)) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), - Poll::Pending => { - self.keep_alive = KeepAlive::Yes; - self.inbound_substream = InboundSubstreamState::PendingFlush(substream); - } + InboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { + Poll::Ready(Ok(())) => { + self.inbound_substream = InboundState::None; } - } - InboundSubstreamState::Reading(mut substream) => match substream.poll_next_unpin(cx) { + Poll::Ready(Err(e)) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), + Poll::Pending => { + self.keep_alive = KeepAlive::Yes; + self.inbound_substream = InboundState::PendingFlush(substream); + } + }, + InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { - self.inbound_substream = - InboundSubstreamState::WaitingToRespondToRemote(substream); + self.inbound_substream = InboundState::WaitForBehaviour(substream); return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); } Poll::Ready(Some(Err(_e))) => { @@ -248,50 +235,42 @@ impl ProtocolsHandler for RendezvousHandler { } Poll::Ready(None) => todo!(), Poll::Pending => { - self.inbound_substream = InboundSubstreamState::Reading(substream); + self.inbound_substream = InboundState::Reading(substream); } }, - InboundSubstreamState::WaitingToRespondToRemote(substream) => { - self.inbound_substream = InboundSubstreamState::WaitingToRespondToRemote(substream); + InboundState::WaitForBehaviour(substream) => { + self.inbound_substream = InboundState::WaitForBehaviour(substream); } - InboundSubstreamState::Closing(mut substream) => match substream.poll_close_unpin(cx) { + InboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { Poll::Ready(..) => { - if let OutboundSubstreamState::Idle | OutboundSubstreamState::Poisoned = - self.outbound_substream - { + if let OutboundState::None | OutboundState::Poisoned = self.outbound_substream { self.keep_alive = KeepAlive::No; } - self.inbound_substream = InboundSubstreamState::Idle; + self.inbound_substream = InboundState::None; } Poll::Pending => { - self.inbound_substream = InboundSubstreamState::Closing(substream); + self.inbound_substream = InboundState::Closing(substream); } }, - InboundSubstreamState::Idle => self.outbound_substream = OutboundSubstreamState::Idle, - InboundSubstreamState::Poisoned => { - self.outbound_substream = OutboundSubstreamState::Idle - } + InboundState::None => self.outbound_substream = OutboundState::None, + InboundState::Poisoned => self.outbound_substream = OutboundState::None, } - match std::mem::replace( - &mut self.outbound_substream, - OutboundSubstreamState::Poisoned, - ) { - OutboundSubstreamState::Start(msg) => { - self.outbound_substream = OutboundSubstreamState::WaitingUpgrade; + match std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned) { + OutboundState::Start(msg) => { + self.outbound_substream = OutboundState::WaitingUpgrade; return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol: SubstreamProtocol::new(Rendezvous, msg), }); } - OutboundSubstreamState::WaitingUpgrade => { - self.outbound_substream = OutboundSubstreamState::WaitingUpgrade + OutboundState::WaitingUpgrade => { + self.outbound_substream = OutboundState::WaitingUpgrade } - OutboundSubstreamState::PendingSend(mut substream, message) => { + OutboundState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { Ok(()) => { - self.outbound_substream = - OutboundSubstreamState::PendingFlush(substream); + self.outbound_substream = OutboundState::PendingFlush(substream); } Err(e) => { return Poll::Ready(ProtocolsHandlerEvent::Close(e)); @@ -302,48 +281,38 @@ impl ProtocolsHandler for RendezvousHandler { } Poll::Pending => { self.keep_alive = KeepAlive::Yes; - self.outbound_substream = - OutboundSubstreamState::PendingSend(substream, message); + self.outbound_substream = OutboundState::PendingSend(substream, message); } } } - OutboundSubstreamState::PendingFlush(mut substream) => { - match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => { - self.outbound_substream = - OutboundSubstreamState::WaitingForRemoteToRespond(substream) - } - Poll::Ready(Err(e)) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), - Poll::Pending => { - self.keep_alive = KeepAlive::Yes; - self.outbound_substream = OutboundSubstreamState::PendingFlush(substream); - } + OutboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { + Poll::Ready(Ok(())) => { + self.outbound_substream = OutboundState::WaitForRemote(substream) } - } - OutboundSubstreamState::WaitingForRemoteToRespond(substream) => { + Poll::Ready(Err(e)) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), + Poll::Pending => { + self.keep_alive = KeepAlive::Yes; + self.outbound_substream = OutboundState::PendingFlush(substream); + } + }, + OutboundState::WaitForRemote(substream) => { let message: Message = todo!("Sink::read??"); - self.outbound_substream = OutboundSubstreamState::Idle; + self.outbound_substream = OutboundState::None; return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(message))); } - OutboundSubstreamState::Closing(mut substream) => { - match substream.poll_close_unpin(cx) { - Poll::Ready(..) => { - if let InboundSubstreamState::Idle | InboundSubstreamState::Poisoned = - self.inbound_substream - { - self.keep_alive = KeepAlive::No; - } - self.outbound_substream = OutboundSubstreamState::Idle; - } - Poll::Pending => { - self.outbound_substream = OutboundSubstreamState::Closing(substream); + OutboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { + Poll::Ready(..) => { + if let InboundState::None | InboundState::Poisoned = self.inbound_substream { + self.keep_alive = KeepAlive::No; } + self.outbound_substream = OutboundState::None; } - } - OutboundSubstreamState::Idle => self.outbound_substream = OutboundSubstreamState::Idle, - OutboundSubstreamState::Poisoned => { - self.outbound_substream = OutboundSubstreamState::Poisoned - } + Poll::Pending => { + self.outbound_substream = OutboundState::Closing(substream); + } + }, + OutboundState::None => self.outbound_substream = OutboundState::None, + OutboundState::Poisoned => self.outbound_substream = OutboundState::Poisoned, } Poll::Pending From 436682e2a98e68a18ba9b627dec99136a61cc34d Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 28 May 2021 18:22:38 +1000 Subject: [PATCH 025/242] Fix inbound/outbound injection --- protocols/rendezvous/src/handler.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 5cd66af37e8..da0959a5b3b 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -126,6 +126,8 @@ impl ProtocolsHandler for RendezvousHandler { ) { if let InboundState::None = self.inbound_substream { self.inbound_substream = InboundState::Reading(substream); + } else { + unreachable!("Invalid inbound state") } } @@ -134,8 +136,10 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, msg: Self::OutboundOpenInfo, ) { - if let OutboundState::None = self.outbound_substream { + if let OutboundState::WaitingUpgrade = self.outbound_substream { self.outbound_substream = OutboundState::PendingSend(substream, msg); + } else { + unreachable!("Invalid outbound state") } } From 32a504b807d2c62b07e2115ea3eff6ccc8c3eef9 Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 28 May 2021 18:35:10 +1000 Subject: [PATCH 026/242] Go to closing after reading from outbound --- protocols/rendezvous/src/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index da0959a5b3b..32db0f0a78c 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -301,7 +301,7 @@ impl ProtocolsHandler for RendezvousHandler { }, OutboundState::WaitForRemote(substream) => { let message: Message = todo!("Sink::read??"); - self.outbound_substream = OutboundState::None; + self.outbound_substream = OutboundState::Closing(substream); return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(message))); } OutboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { From e377d5ddeb0cc29dd5da7dba1e97a6ac2b06c35d Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 28 May 2021 18:40:12 +1000 Subject: [PATCH 027/242] Prepare everything for writing tests Pull in all the code that creates and connects the test swarms that include the behaviour to be tested. A first initial test stub is there, but no test functionality added yet. --- protocols/rendezvous/Cargo.toml | 7 + protocols/rendezvous/src/behaviour.rs | 13 +- protocols/rendezvous/src/codec.rs | 2 +- protocols/rendezvous/src/lib.rs | 2 +- protocols/rendezvous/tests/harness/mod.rs | 160 ++++++++++++++++++++++ protocols/rendezvous/tests/rendezvous.rs | 40 +++++- 6 files changed, 218 insertions(+), 6 deletions(-) create mode 100644 protocols/rendezvous/tests/harness/mod.rs diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 51ff424f8cf..5faf0886c35 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -20,5 +20,12 @@ futures = { version = "0.3", default-features = false, features = ["std"] } thiserror = "1" # TODO: REMOVE FOR PRODUCTION USE unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } +[dev-dependencies] +libp2p-mplex = { version = "0.28", path = "../../muxers/mplex" } +libp2p-noise = { version = "0.31", path = "../../transports/noise" } +libp2p-yamux = { version = "0.32", path = "../../muxers/yamux" } +rand = "0.8" +tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } + [build-dependencies] prost-build = "0.7" diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 7389f259b81..d68e41c11c3 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -8,12 +8,20 @@ use libp2p_swarm::{ use std::collections::{HashMap, HashSet, VecDeque}; use std::task::{Context, Poll}; -struct Rendezvous { +pub struct Rendezvous { events: VecDeque>, registrations: HashMap>, } impl Rendezvous { + + pub fn new() -> Self { + Self { + events: Default::default(), + registrations: Default::default() + } + } + pub fn register(&mut self, ns: String, rendezvous_node: PeerId) { self.events .push_back(NetworkBehaviourAction::NotifyHandler { @@ -44,7 +52,8 @@ impl Rendezvous { } } -enum Event { +#[derive(Debug)] +pub enum Event { Discovered { rendezvous_node: PeerId, ns: Vec, diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 7f90e4eb91c..f30a9d47f5e 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -47,7 +47,7 @@ impl NewRegistration { Self { namespace, record, - ttl + ttl, } } diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index a18082bd40c..e109e41765f 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,4 +1,4 @@ -mod behaviour; +pub mod behaviour; mod codec; mod handler; mod protocol; diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs new file mode 100644 index 00000000000..8c1f3bed112 --- /dev/null +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -0,0 +1,160 @@ +use futures::future; +use libp2p_core::{Executor, Multiaddr, PeerId, identity, Transport}; +use std::pin::Pin; +use futures::Future; +use libp2p_swarm::{NetworkBehaviour, Swarm, IntoProtocolsHandler, ProtocolsHandler, SwarmBuilder, SwarmEvent}; +use std::fmt::Debug; +use libp2p_core::transport::MemoryTransport; +use libp2p_core::transport::upgrade::Version; +use libp2p_core::upgrade::SelectUpgrade; +use std::time::Duration; +use libp2p_core::muxing::StreamMuxerBox; +use libp2p_noise::{self, NoiseConfig, X25519Spec, Keypair}; +use libp2p_yamux::YamuxConfig; +use libp2p_mplex::MplexConfig; + +/// An adaptor struct for libp2p that spawns futures into the current +/// thread-local runtime. +struct GlobalSpawnTokioExecutor; + +impl Executor for GlobalSpawnTokioExecutor { + fn exec(&self, future: Pin + Send>>) { + let _ = tokio::spawn(future); + } +} + +#[allow(missing_debug_implementations)] +pub struct Actor { + pub swarm: Swarm, + pub addr: Multiaddr, + pub peer_id: PeerId, +} + +pub async fn new_connected_swarm_pair(behaviour_fn: F) -> (Actor, Actor) +where + B: NetworkBehaviour, + F: Fn(PeerId, identity::Keypair) -> B + Clone, + <<::ProtocolsHandler as IntoProtocolsHandler>::Handler as ProtocolsHandler>::InEvent: Clone, +::OutEvent: Debug{ + let (swarm, addr, peer_id) = new_swarm(behaviour_fn.clone()); + let mut alice = Actor { + swarm, + addr, + peer_id, + }; + + let (swarm, addr, peer_id) = new_swarm(behaviour_fn); + let mut bob = Actor { + swarm, + addr, + peer_id, + }; + + connect(&mut alice.swarm, &mut bob.swarm).await; + + (alice, bob) +} + +pub fn new_swarm B>( + behaviour_fn: F, +) -> (Swarm, Multiaddr, PeerId) +where + B: NetworkBehaviour, +{ + let id_keys = identity::Keypair::generate_ed25519(); + let peer_id = PeerId::from(id_keys.public()); + + let dh_keys = Keypair::::new() + .into_authentic(&id_keys) + .expect("failed to create dh_keys"); + let noise = NoiseConfig::xx(dh_keys).into_authenticated(); + + let transport = MemoryTransport::default() + .upgrade(Version::V1) + .authenticate(noise) + .multiplex(SelectUpgrade::new( + YamuxConfig::default(), + MplexConfig::new(), + )) + .timeout(Duration::from_secs(5)) + .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) + .boxed(); + + let mut swarm: Swarm = SwarmBuilder::new(transport, behaviour_fn(peer_id, id_keys), peer_id) + .executor(Box::new(GlobalSpawnTokioExecutor)) + .build(); + + let address_port = rand::random::(); + let addr = format!("/memory/{}", address_port) + .parse::() + .unwrap(); + + Swarm::listen_on(&mut swarm, addr.clone()).unwrap(); + + (swarm, addr, peer_id) +} + +pub async fn await_events_or_timeout( + alice_event: impl Future, + bob_event: impl Future, +) -> (A, B) { + tokio::time::timeout( + Duration::from_secs(10), + future::join(alice_event, bob_event), + ) + .await + .expect("network behaviours to emit an event within 10 seconds") +} + +/// Connects two swarms with each other. +/// +/// This assumes the transport that is in use can be used by Bob to connect to +/// the listen address that is emitted by Alice. In other words, they have to be +/// on the same network. The memory transport used by the above `new_swarm` +/// function fulfills this. +/// +/// We also assume that the swarms don't emit any behaviour events during the +/// connection phase. Any event emitted is considered a bug from this functions +/// PoV because they would be lost. +pub async fn connect(alice: &mut Swarm, bob: &mut Swarm) +where + BA: NetworkBehaviour, + BB: NetworkBehaviour, + ::OutEvent: Debug, + ::OutEvent: Debug, +{ + let mut alice_connected = false; + let mut bob_connected = false; + + while !alice_connected && !bob_connected { + let (alice_event, bob_event) = future::join(alice.next_event(), bob.next_event()).await; + + match alice_event { + SwarmEvent::ConnectionEstablished { .. } => { + alice_connected = true; + } + SwarmEvent::NewListenAddr(addr) => { + bob.dial_addr(addr).unwrap(); + } + SwarmEvent::Behaviour(event) => { + panic!( + "alice unexpectedly emitted a behaviour event during connection: {:?}", + event + ); + } + _ => {} + } + match bob_event { + SwarmEvent::ConnectionEstablished { .. } => { + bob_connected = true; + } + SwarmEvent::Behaviour(event) => { + panic!( + "bob unexpectedly emitted a behaviour event during connection: {:?}", + event + ); + } + _ => {} + } + } +} diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 8490513213d..c7d6562f4c4 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,2 +1,38 @@ -#[test] -fn rendesvous() {} +pub mod harness; +use libp2p_swarm::Swarm; +use libp2p_core::PeerId; +use crate::harness::{new_swarm, connect}; +use libp2p_rendezvous::behaviour::Rendezvous; + +#[tokio::test] +async fn given_successful_registration_then_successful_discovery() { + let mut test = RendezvousTest::setup().await; + +} + +struct RendezvousTest { + alice_swarm: Swarm, + bob_swarm: Swarm, + + alice_peer_id: PeerId, +} + +impl RendezvousTest { + pub async fn setup() -> Self { + let (mut alice_swarm, _, alice_peer_id) = harness::new_swarm(|_, _| { + Rendezvous::new() + }); + let (mut bob_swarm, ..) = new_swarm(|_, _| Rendezvous::new() ); + + connect(&mut alice_swarm, &mut bob_swarm).await; + + Self { + alice_swarm, + bob_swarm, + alice_peer_id, + } + } + + pub async fn alice_registers_with_bob(&self, namespace: String) { + } +} From f925f2621a31237ada441c5c566a622f8839895b Mon Sep 17 00:00:00 2001 From: rishflab Date: Mon, 31 May 2021 14:52:14 +1000 Subject: [PATCH 028/242] Retreive discovered from hashmap --- protocols/rendezvous/src/behaviour.rs | 34 +++++++++++++++++++-------- protocols/rendezvous/src/handler.rs | 26 ++++++++++++-------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index d68e41c11c3..2802fc21e2a 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -10,7 +10,7 @@ use std::task::{Context, Poll}; pub struct Rendezvous { events: VecDeque>, - registrations: HashMap>, + registrations: HashMap>, } impl Rendezvous { @@ -77,11 +77,11 @@ pub enum Event { }, PeerRegistered { peer_id: PeerId, - ns: Registration, + ns: String, }, PeerUnregistered { peer_id: PeerId, - ns: Registration, + ns: String, }, } @@ -105,7 +105,6 @@ impl NetworkBehaviour for Rendezvous { todo!() } - // inject event from handler fn inject_event( &mut self, peer_id: PeerId, @@ -114,15 +113,16 @@ impl NetworkBehaviour for Rendezvous { ) { match event.0 { Message::Register(new_reggo) => { - let namespace = new_reggo.namespace; - let ttl = unimplemented!(); + let ttl = new_reggo.effective_ttl(); - todo!("add to hashmap"); self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::RegisterRequest { namespace, ttl }, + event: Input::RegisterRequest { + namespace: new_reggo.namespace, + ttl: Some(ttl), + }, }) } Message::SuccessfullyRegistered { ttl } => { @@ -153,12 +153,26 @@ impl NetworkBehaviour for Rendezvous { // } } Message::Discover { namespace } => { - let registrations = todo!("get from hashmap using namespace"); + if let Some(ns) = namespace { + if let Some(peers) = self.registrations.get_mut(&ns) { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::DiscoverResponse { + discovered: peers + .iter() + .map(|a| (ns.clone(), peer_id)) + .collect(), + }, + }); + } + } self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::DiscoverResponse { registrations }, + event: Input::DiscoverResponse { discovered: vec![] }, }) } Message::DiscoverResponse { registrations } => { diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 32db0f0a78c..6ffe6c9a4fe 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,16 +1,14 @@ -use crate::codec::{Error, RendezvousCodec}; +use crate::codec::RendezvousCodec; use crate::codec::{Message, Registration}; use crate::protocol; use crate::protocol::Rendezvous; use asynchronous_codec::Framed; -use futures::{SinkExt, Stream, StreamExt}; -use libp2p_core::{InboundUpgrade, OutboundUpgrade}; +use futures::{SinkExt, StreamExt}; +use libp2p_core::{InboundUpgrade, OutboundUpgrade, PeerId}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; -use std::collections::VecDeque; -use std::pin::Pin; use std::task::{Context, Poll}; use void::Void; @@ -56,7 +54,7 @@ pub enum Input { ttl: i64, }, DiscoverResponse { - registrations: Vec, + discovered: Vec<(String, PeerId)>, }, } @@ -67,9 +65,17 @@ impl From for Message { Input::UnregisterRequest { namespace } => Message::Unregister { namespace }, Input::DiscoverRequest { namespace } => Message::Discover { namespace }, Input::RegisterResponse { ttl } => Message::SuccessfullyRegistered { ttl }, - Input::DiscoverResponse { registrations } => { - Message::DiscoverResponse { registrations } - } + Input::DiscoverResponse { + discovered: namespaces, + } => Message::DiscoverResponse { + registrations: namespaces + .iter() + .map(|a| Registration { + namespace: a.0.to_string(), + record: todo!(), + }) + .collect(), + }, } } } @@ -169,7 +175,7 @@ impl ProtocolsHandler for RendezvousHandler { InboundState::WaitForBehaviour(substream), outbound, ) => (InboundState::PendingSend(substream, todo!()), outbound), - (_) => unreachable!("Handler in invalid state"), + _ => unreachable!("Handler in invalid state"), }; self.inbound_substream = inbound_substream; From a61742a5826343cdaac6b9ffb41b4014f51ae0d7 Mon Sep 17 00:00:00 2001 From: rishflab Date: Mon, 31 May 2021 15:58:35 +1000 Subject: [PATCH 029/242] Remove signed peer record todo from handler --- core/src/peer_record.rs | 2 +- protocols/rendezvous/src/behaviour.rs | 6 +++++- protocols/rendezvous/src/codec.rs | 8 +++++++- protocols/rendezvous/src/handler.rs | 10 +++++++--- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index a7cdde0a6d0..8b97dc65329 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -130,7 +130,7 @@ impl std::error::Error for DecodingError { } // TODO: docs -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AuthenticatedPeerRecord { inner: PeerRecord, diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 2802fc21e2a..9a429e6d15e 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -160,6 +160,7 @@ impl NetworkBehaviour for Rendezvous { peer_id, handler: NotifyHandler::Any, event: Input::DiscoverResponse { + record: todo!(), discovered: peers .iter() .map(|a| (ns.clone(), peer_id)) @@ -172,7 +173,10 @@ impl NetworkBehaviour for Rendezvous { .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::DiscoverResponse { discovered: vec![] }, + event: Input::DiscoverResponse { + record: todo!(), + discovered: vec![], + }, }) } Message::DiscoverResponse { registrations } => { diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index f30a9d47f5e..a2bac19392a 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -302,7 +302,13 @@ impl TryFrom for Message { .map(|reggo| { Ok(Registration { namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)?, - record: todo!(), + record: AuthenticatedPeerRecord::from_signed_envelope( + SignedEnvelope::from_protobuf_encoding( + ®go + .signed_peer_record + .ok_or(ConversionError::MissingSignedPeerRecord)?, + )?, + )?, }) }) .collect::, ConversionError>>()?, diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 6ffe6c9a4fe..3d0d37941ee 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,10 +1,12 @@ -use crate::codec::RendezvousCodec; +use crate::codec::{ConversionError, RendezvousCodec}; use crate::codec::{Message, Registration}; use crate::protocol; use crate::protocol::Rendezvous; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; -use libp2p_core::{InboundUpgrade, OutboundUpgrade, PeerId}; +use libp2p_core::{ + AuthenticatedPeerRecord, InboundUpgrade, OutboundUpgrade, PeerId, SignedEnvelope, +}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, @@ -54,6 +56,7 @@ pub enum Input { ttl: i64, }, DiscoverResponse { + record: AuthenticatedPeerRecord, discovered: Vec<(String, PeerId)>, }, } @@ -67,12 +70,13 @@ impl From for Message { Input::RegisterResponse { ttl } => Message::SuccessfullyRegistered { ttl }, Input::DiscoverResponse { discovered: namespaces, + record, } => Message::DiscoverResponse { registrations: namespaces .iter() .map(|a| Registration { namespace: a.0.to_string(), - record: todo!(), + record: record.clone(), }) .collect(), }, From b39449d94a62edaca5fa991c739383c43e5a136d Mon Sep 17 00:00:00 2001 From: rishflab Date: Mon, 31 May 2021 16:03:18 +1000 Subject: [PATCH 030/242] Handle unregister request in behaviour Fix imports --- protocols/rendezvous/src/behaviour.rs | 14 ++++++-------- protocols/rendezvous/src/handler.rs | 6 ++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 9a429e6d15e..0155270b3cb 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -14,11 +14,10 @@ pub struct Rendezvous { } impl Rendezvous { - pub fn new() -> Self { Self { events: Default::default(), - registrations: Default::default() + registrations: Default::default(), } } @@ -145,12 +144,11 @@ impl NetworkBehaviour for Rendezvous { )) } Message::Unregister { namespace } => { - // todo: implement Hash, Eq for Registration - // if let Some(peers) = self.registrations.get_mut() { - // if peers.contains(&peer_id) { - // peers.remove(&peer_id) - // } - // } + if let Some(peers) = self.registrations.get_mut(&namespace) { + if peers.contains(&peer_id) { + peers.remove(&peer_id); + } + } } Message::Discover { namespace } => { if let Some(ns) = namespace { diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 3d0d37941ee..fa938b8924f 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,12 +1,10 @@ -use crate::codec::{ConversionError, RendezvousCodec}; +use crate::codec::RendezvousCodec; use crate::codec::{Message, Registration}; use crate::protocol; use crate::protocol::Rendezvous; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; -use libp2p_core::{ - AuthenticatedPeerRecord, InboundUpgrade, OutboundUpgrade, PeerId, SignedEnvelope, -}; +use libp2p_core::{AuthenticatedPeerRecord, InboundUpgrade, OutboundUpgrade, PeerId}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, From d6fc13f9ed1df0e70c98443977121e2f6a44c64d Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 31 May 2021 21:38:19 +1000 Subject: [PATCH 031/242] WIP - Happy path registration and discovery test --- protocols/rendezvous/src/lib.rs | 2 +- protocols/rendezvous/tests/harness/mod.rs | 16 ++--- protocols/rendezvous/tests/rendezvous.rs | 79 +++++++++++++++++++---- 3 files changed, 75 insertions(+), 22 deletions(-) diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index e109e41765f..8a04e547830 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,4 +1,4 @@ pub mod behaviour; -mod codec; +pub mod codec; mod handler; mod protocol; diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 8c1f3bed112..5c980f1a3b7 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -95,15 +95,15 @@ where } pub async fn await_events_or_timeout( - alice_event: impl Future, - bob_event: impl Future, + swarm_1_event: impl Future, + swarm_2_event: impl Future, ) -> (A, B) { tokio::time::timeout( Duration::from_secs(10), - future::join(alice_event, bob_event), + future::join(swarm_1_event, swarm_2_event), ) - .await - .expect("network behaviours to emit an event within 10 seconds") + .await + .expect("network behaviours to emit an event within 10 seconds") } /// Connects two swarms with each other. @@ -116,7 +116,7 @@ pub async fn await_events_or_timeout( /// We also assume that the swarms don't emit any behaviour events during the /// connection phase. Any event emitted is considered a bug from this functions /// PoV because they would be lost. -pub async fn connect(alice: &mut Swarm, bob: &mut Swarm) +pub async fn connect(receiver: &mut Swarm, dialer: &mut Swarm) where BA: NetworkBehaviour, BB: NetworkBehaviour, @@ -127,14 +127,14 @@ where let mut bob_connected = false; while !alice_connected && !bob_connected { - let (alice_event, bob_event) = future::join(alice.next_event(), bob.next_event()).await; + let (alice_event, bob_event) = future::join(receiver.next_event(), dialer.next_event()).await; match alice_event { SwarmEvent::ConnectionEstablished { .. } => { alice_connected = true; } SwarmEvent::NewListenAddr(addr) => { - bob.dial_addr(addr).unwrap(); + dialer.dial_addr(addr).unwrap(); } SwarmEvent::Behaviour(event) => { panic!( diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index c7d6562f4c4..fad7548d9e4 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,38 +1,91 @@ pub mod harness; use libp2p_swarm::Swarm; -use libp2p_core::PeerId; -use crate::harness::{new_swarm, connect}; -use libp2p_rendezvous::behaviour::Rendezvous; +use libp2p_core::{PeerId, AuthenticatedPeerRecord}; +use crate::harness::{new_swarm, connect, await_events_or_timeout}; +use libp2p_rendezvous::behaviour::{Rendezvous, Event}; +use libp2p_rendezvous::codec::Registration; +use std::time::Duration; +use tokio::time::error::Elapsed; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { let mut test = RendezvousTest::setup().await; + let namespace = "some-namespace".to_string(); + + // register + test.registration_swarm.behaviour_mut().register(namespace.clone(), test.rendezvous_peer_id); + + test.assert_successful_registration(namespace.clone()).await; + + // discover + test.discovery_swarm.behaviour_mut().discover(Some(namespace), test.rendezvous_peer_id); + + test.assert_successful_discovery().await; + } struct RendezvousTest { - alice_swarm: Swarm, - bob_swarm: Swarm, + pub registration_swarm: Swarm, + pub registration_peer_id: PeerId, + + pub discovery_swarm: Swarm, + pub discovery_peer_id: PeerId, - alice_peer_id: PeerId, + pub rendezvous_swarm: Swarm, + pub rendezvous_peer_id: PeerId, } impl RendezvousTest { pub async fn setup() -> Self { - let (mut alice_swarm, _, alice_peer_id) = harness::new_swarm(|_, _| { + let (mut registration_swarm, _, registration_peer_id) = harness::new_swarm(|_, _| { Rendezvous::new() }); - let (mut bob_swarm, ..) = new_swarm(|_, _| Rendezvous::new() ); + let (mut discovery_swarm, _, discovery_peer_id) = new_swarm(|_, _| Rendezvous::new() ); - connect(&mut alice_swarm, &mut bob_swarm).await; + let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm(|_, _| Rendezvous::new() ); + + connect(&mut rendezvous_swarm, &mut discovery_swarm).await; + connect(&mut rendezvous_swarm, &mut registration_swarm).await; Self { - alice_swarm, - bob_swarm, - alice_peer_id, + registration_swarm, + registration_peer_id, + discovery_swarm, + discovery_peer_id, + rendezvous_swarm, + rendezvous_peer_id, } } - pub async fn alice_registers_with_bob(&self, namespace: String) { + pub async fn assert_successful_registration(&mut self, namespace: String) { + match await_events_or_timeout(self.rendezvous_swarm.next(), self.registration_swarm.next()).await { + ( + rendezvous_swarm_event, + registration_swarm_event, + ) => { + + // TODO: Assertion against the actual peer record possible? + // let expected = Event::PeerRegistered { peer_id: self.registration_peer_id, ns: Registration { namespace, record: AuthenticatedPeerRecord::from_record() } }; + + + assert!(matches!(rendezvous_swarm_event, Event::PeerRegistered { .. })); + assert!(matches!(registration_swarm_event, Event::RegisteredWithRendezvousNode { .. })); + } + (rendezvous_swarm_event, registration_swarm_event) => panic!( + "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", + rendezvous_swarm_event, registration_swarm_event + ), + } + } + + pub async fn assert_successful_discovery(&mut self) { + // TODO: Is it by design that there is no event emitted on the rendezvous side for discovery? + + match tokio::time::timeout(Duration::from_secs(10), self.discovery_swarm.next()).await.expect("") { + // TODO: Assert against the actual registration + Event::Discovered { .. } => {} + event => panic!("Discovery swarm emitted unexpected event {:?}", event) + } } } From 2edc1672317667bfe3dfe7eb2cbd71a9e38feffb Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 1 Jun 2021 00:06:46 +1000 Subject: [PATCH 032/242] "Handle" connect/disconnect events --- protocols/rendezvous/src/behaviour.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 0155270b3cb..22ef3ccf06f 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -5,6 +5,7 @@ use libp2p_core::{Multiaddr, PeerId}; use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; +use log::debug; use std::collections::{HashMap, HashSet, VecDeque}; use std::task::{Context, Poll}; @@ -92,16 +93,18 @@ impl NetworkBehaviour for Rendezvous { RendezvousHandler::new() } - fn addresses_of_peer(&mut self, _peer_id: &PeerId) -> Vec { - todo!() + fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { + Vec::new() } - fn inject_connected(&mut self, _peer_id: &PeerId) { - todo!() + fn inject_connected(&mut self, peer_id: &PeerId) { + debug!("New peer connected: {}", peer_id); + // Dont need to do anything here? } - fn inject_disconnected(&mut self, _peer_id: &PeerId) { - todo!() + fn inject_disconnected(&mut self, peer_id: &PeerId) { + debug!("Peer disconnected: {}", peer_id); + // Don't need to do anything? } fn inject_event( @@ -137,7 +140,8 @@ impl NetworkBehaviour for Rendezvous { self.events.push_back(NetworkBehaviourAction::GenerateEvent( Event::FailedToRegisterWithRendezvousNode { rendezvous_node: peer_id, - // todo: need to get the namespace somehow? + // todo: need to get the namespace somehow? The handler will probably have to remember + // the request this message is a response to as the wire message does not contain this info ns: "".to_string(), err_code: error, }, From d590741d63d470e77b9cbf2f76091278031a94b4 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 1 Jun 2021 09:58:03 +1000 Subject: [PATCH 033/242] Fix todos/unimplemented except for signed peer record related --- protocols/rendezvous/src/behaviour.rs | 29 ++++- protocols/rendezvous/src/handler.rs | 129 +++++++++++++---------- protocols/rendezvous/tests/rendezvous.rs | 33 +++--- 3 files changed, 119 insertions(+), 72 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 22ef3ccf06f..4d26c976270 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -65,6 +65,7 @@ pub enum Event { RegisteredWithRendezvousNode { rendezvous_node: PeerId, ns: String, + ttl: i64, }, FailedToRegisterWithRendezvousNode { rendezvous_node: PeerId, @@ -133,6 +134,7 @@ impl NetworkBehaviour for Rendezvous { Event::RegisteredWithRendezvousNode { rendezvous_node: peer_id, ns: "".to_string(), + ttl, }, )) } @@ -162,14 +164,37 @@ impl NetworkBehaviour for Rendezvous { peer_id, handler: NotifyHandler::Any, event: Input::DiscoverResponse { - record: todo!(), discovered: peers .iter() - .map(|a| (ns.clone(), peer_id)) + .map(|peer| (ns.clone(), peer.clone())) .collect(), + record: todo!(), }, }); } + } else { + let discovered = self + .registrations + .iter() + .map(|(ns, peers)| { + peers + .iter() + .map(|peer_id| (ns.clone(), peer_id.clone())) + .collect::>() + .into_iter() + }) + .flatten() + .collect::>(); + + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::DiscoverResponse { + discovered, + record: todo!(), + }, + }); } self.events .push_back(NetworkBehaviourAction::NotifyHandler { diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index fa938b8924f..8c9252f5962 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,5 +1,5 @@ -use crate::codec::RendezvousCodec; use crate::codec::{Message, Registration}; +use crate::codec::{NewRegistration, RendezvousCodec}; use crate::protocol; use crate::protocol::Rendezvous; use asynchronous_codec::Framed; @@ -9,16 +9,13 @@ use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; +use log::error; use std::task::{Context, Poll}; use void::Void; -pub struct RendezvousRequest; - pub struct RendezvousHandler { outbound_substream: OutboundState, - inbound_substream: InboundState, - keep_alive: KeepAlive, } @@ -52,6 +49,7 @@ pub enum Input { }, RegisterResponse { ttl: i64, + message: Message, }, DiscoverResponse { record: AuthenticatedPeerRecord, @@ -59,29 +57,6 @@ pub enum Input { }, } -impl From for Message { - fn from(req: Input) -> Self { - match req { - Input::RegisterRequest { namespace, ttl } => Message::Register(todo!()), - Input::UnregisterRequest { namespace } => Message::Unregister { namespace }, - Input::DiscoverRequest { namespace } => Message::Discover { namespace }, - Input::RegisterResponse { ttl } => Message::SuccessfullyRegistered { ttl }, - Input::DiscoverResponse { - discovered: namespaces, - record, - } => Message::DiscoverResponse { - registrations: namespaces - .iter() - .map(|a| Registration { - namespace: a.0.to_string(), - record: record.clone(), - }) - .collect(), - }, - } - } -} - /// State of the inbound substream, opened either by us or by the remote. enum InboundState { None, @@ -154,29 +129,51 @@ impl ProtocolsHandler for RendezvousHandler { // event injected from NotifyHandler fn inject_event(&mut self, req: Input) { let (inbound_substream, outbound_substream) = match ( - &req, + req, std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned), std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned), ) { - (Input::RegisterRequest { .. }, inbound, OutboundState::None) => { - (inbound, OutboundState::Start(Message::from(req))) - } - (Input::UnregisterRequest { .. }, inbound, OutboundState::None) => { - (inbound, OutboundState::Start(Message::from(req))) - } - (Input::DiscoverRequest { .. }, inbound, OutboundState::None) => { - (inbound, OutboundState::Start(Message::from(req))) - } + (Input::RegisterRequest { namespace, ttl }, inbound, OutboundState::None) => ( + inbound, + OutboundState::Start(Message::Register(NewRegistration::new( + namespace.clone(), + todo!(), + ttl, + ))), + ), + (Input::UnregisterRequest { namespace }, inbound, OutboundState::None) => ( + inbound, + OutboundState::Start(Message::Unregister { + namespace: namespace.clone(), + }), + ), + (Input::DiscoverRequest { namespace }, inbound, OutboundState::None) => ( + inbound, + OutboundState::Start(Message::Discover { + namespace: namespace.clone(), + }), + ), ( - Input::RegisterResponse { .. }, + Input::RegisterResponse { ttl: _ttl, message }, InboundState::WaitForBehaviour(substream), outbound, - ) => (InboundState::PendingSend(substream, todo!()), outbound), + ) => (InboundState::PendingSend(substream, message), outbound), ( - Input::DiscoverResponse { .. }, + Input::DiscoverResponse { record, discovered }, InboundState::WaitForBehaviour(substream), outbound, - ) => (InboundState::PendingSend(substream, todo!()), outbound), + ) => { + let msg = Message::DiscoverResponse { + registrations: discovered + .iter() + .map(|d| Registration { + namespace: d.0.to_string(), + record: record.clone(), + }) + .collect(), + }; + (InboundState::PendingSend(substream, msg), outbound) + } _ => unreachable!("Handler in invalid state"), }; @@ -187,9 +184,9 @@ impl ProtocolsHandler for RendezvousHandler { fn inject_dial_upgrade_error( &mut self, _info: Self::OutboundOpenInfo, - _error: ProtocolsHandlerUpgrErr, + error: ProtocolsHandlerUpgrErr, ) { - todo!() + error!("Dial upgrade error {:?}", error); } fn connection_keep_alive(&self) -> KeepAlive { @@ -242,10 +239,12 @@ impl ProtocolsHandler for RendezvousHandler { self.inbound_substream = InboundState::WaitForBehaviour(substream); return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); } - Poll::Ready(Some(Err(_e))) => { - todo!() + Poll::Ready(Some(Err(e))) => { + error!("Error when sending outbound: {:?}", e); + } + Poll::Ready(None) => { + error!("Honestly no idea what to do if this happens"); } - Poll::Ready(None) => todo!(), Poll::Pending => { self.inbound_substream = InboundState::Reading(substream); } @@ -276,7 +275,7 @@ impl ProtocolsHandler for RendezvousHandler { }); } OutboundState::WaitingUpgrade => { - self.outbound_substream = OutboundState::WaitingUpgrade + self.outbound_substream = OutboundState::WaitingUpgrade; } OutboundState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { @@ -285,6 +284,7 @@ impl ProtocolsHandler for RendezvousHandler { self.outbound_substream = OutboundState::PendingFlush(substream); } Err(e) => { + error!("Error when sending outbound: {:?}", e); return Poll::Ready(ProtocolsHandlerEvent::Close(e)); } }, @@ -301,17 +301,31 @@ impl ProtocolsHandler for RendezvousHandler { Poll::Ready(Ok(())) => { self.outbound_substream = OutboundState::WaitForRemote(substream) } - Poll::Ready(Err(e)) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), + Poll::Ready(Err(e)) => { + error!("Error when flushing outbound: {:?}", e); + return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + } Poll::Pending => { self.keep_alive = KeepAlive::Yes; self.outbound_substream = OutboundState::PendingFlush(substream); } }, - OutboundState::WaitForRemote(substream) => { - let message: Message = todo!("Sink::read??"); - self.outbound_substream = OutboundState::Closing(substream); - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(message))); - } + OutboundState::WaitForRemote(mut substream) => match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(msg))) => { + self.outbound_substream = OutboundState::Closing(substream); + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); + } + Poll::Ready(Some(Err(e))) => { + self.outbound_substream = OutboundState::Closing(substream); + error!("Error when receiving message from outbound: {:?}", e) + } + Poll::Ready(None) => { + error!("Honestly no idea what to do if this happens"); + } + Poll::Pending => { + self.outbound_substream = OutboundState::WaitForRemote(substream); + } + }, OutboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { Poll::Ready(..) => { if let InboundState::None | InboundState::Poisoned = self.inbound_substream { @@ -324,7 +338,12 @@ impl ProtocolsHandler for RendezvousHandler { } }, OutboundState::None => self.outbound_substream = OutboundState::None, - OutboundState::Poisoned => self.outbound_substream = OutboundState::Poisoned, + OutboundState::Poisoned => { + self.outbound_substream = { + error!("outbound poisoned"); + OutboundState::Poisoned + } + } } Poll::Pending diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index fad7548d9e4..dc98ff3c729 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,11 +1,9 @@ pub mod harness; +use crate::harness::{await_events_or_timeout, connect, new_swarm}; +use libp2p_core::PeerId; +use libp2p_rendezvous::behaviour::{Event, Rendezvous}; use libp2p_swarm::Swarm; -use libp2p_core::{PeerId, AuthenticatedPeerRecord}; -use crate::harness::{new_swarm, connect, await_events_or_timeout}; -use libp2p_rendezvous::behaviour::{Rendezvous, Event}; -use libp2p_rendezvous::codec::Registration; use std::time::Duration; -use tokio::time::error::Elapsed; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { @@ -14,15 +12,18 @@ async fn given_successful_registration_then_successful_discovery() { let namespace = "some-namespace".to_string(); // register - test.registration_swarm.behaviour_mut().register(namespace.clone(), test.rendezvous_peer_id); + test.registration_swarm + .behaviour_mut() + .register(namespace.clone(), test.rendezvous_peer_id); test.assert_successful_registration(namespace.clone()).await; // discover - test.discovery_swarm.behaviour_mut().discover(Some(namespace), test.rendezvous_peer_id); + test.discovery_swarm + .behaviour_mut() + .discover(Some(namespace), test.rendezvous_peer_id); test.assert_successful_discovery().await; - } struct RendezvousTest { @@ -38,12 +39,11 @@ struct RendezvousTest { impl RendezvousTest { pub async fn setup() -> Self { - let (mut registration_swarm, _, registration_peer_id) = harness::new_swarm(|_, _| { - Rendezvous::new() - }); - let (mut discovery_swarm, _, discovery_peer_id) = new_swarm(|_, _| Rendezvous::new() ); + let (mut registration_swarm, _, registration_peer_id) = + harness::new_swarm(|_, _| Rendezvous::new()); + let (mut discovery_swarm, _, discovery_peer_id) = new_swarm(|_, _| Rendezvous::new()); - let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm(|_, _| Rendezvous::new() ); + let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm(|_, _| Rendezvous::new()); connect(&mut rendezvous_swarm, &mut discovery_swarm).await; connect(&mut rendezvous_swarm, &mut registration_swarm).await; @@ -82,10 +82,13 @@ impl RendezvousTest { pub async fn assert_successful_discovery(&mut self) { // TODO: Is it by design that there is no event emitted on the rendezvous side for discovery? - match tokio::time::timeout(Duration::from_secs(10), self.discovery_swarm.next()).await.expect("") { + match tokio::time::timeout(Duration::from_secs(10), self.discovery_swarm.next()) + .await + .expect("") + { // TODO: Assert against the actual registration Event::Discovered { .. } => {} - event => panic!("Discovery swarm emitted unexpected event {:?}", event) + event => panic!("Discovery swarm emitted unexpected event {:?}", event), } } } From 81388db725dc974fcc1cfb858c1389bb4d8c8351 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 1 Jun 2021 10:18:07 +1000 Subject: [PATCH 034/242] Bubble up signed record todo to behaviour --- protocols/rendezvous/src/behaviour.rs | 11 +++++++++-- protocols/rendezvous/src/handler.rs | 14 +++++++++++--- protocols/rendezvous/tests/rendezvous.rs | 8 +++++--- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 4d26c976270..2811bc627f9 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,7 +1,7 @@ use crate::codec::{ErrorCode, Message, Registration}; use crate::handler::{Input, RendezvousHandler}; use libp2p_core::connection::ConnectionId; -use libp2p_core::{Multiaddr, PeerId}; +use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId}; use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; @@ -22,13 +22,19 @@ impl Rendezvous { } } - pub fn register(&mut self, ns: String, rendezvous_node: PeerId) { + pub fn register( + &mut self, + ns: String, + rendezvous_node: PeerId, + record: AuthenticatedPeerRecord, + ) { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, event: Input::RegisterRequest { namespace: ns, ttl: None, + record, }, handler: NotifyHandler::Any, }); @@ -125,6 +131,7 @@ impl NetworkBehaviour for Rendezvous { event: Input::RegisterRequest { namespace: new_reggo.namespace, ttl: Some(ttl), + record: todo!(), }, }) } diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 8c9252f5962..8e552634b3d 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -36,7 +36,7 @@ pub enum Input { RegisterRequest { namespace: String, ttl: Option, - // TODO: Signed peer record field + record: AuthenticatedPeerRecord, }, UnregisterRequest { namespace: String, @@ -133,11 +133,19 @@ impl ProtocolsHandler for RendezvousHandler { std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned), std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned), ) { - (Input::RegisterRequest { namespace, ttl }, inbound, OutboundState::None) => ( + ( + Input::RegisterRequest { + namespace, + ttl, + record, + }, + inbound, + OutboundState::None, + ) => ( inbound, OutboundState::Start(Message::Register(NewRegistration::new( namespace.clone(), - todo!(), + record, ttl, ))), ), diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index dc98ff3c729..429082ed3eb 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -12,9 +12,11 @@ async fn given_successful_registration_then_successful_discovery() { let namespace = "some-namespace".to_string(); // register - test.registration_swarm - .behaviour_mut() - .register(namespace.clone(), test.rendezvous_peer_id); + test.registration_swarm.behaviour_mut().register( + namespace.clone(), + test.rendezvous_peer_id, + todo!(), + ); test.assert_successful_registration(namespace.clone()).await; From 404b484f58b1a04fcf0002f9b0d824c0fe6c6378 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 1 Jun 2021 11:00:28 +1000 Subject: [PATCH 035/242] WIP: signed peer record --- protocols/rendezvous/src/behaviour.rs | 26 ++++++++-------- protocols/rendezvous/src/handler.rs | 3 +- protocols/rendezvous/src/registration.rs | 12 ++++++++ protocols/rendezvous/tests/harness/mod.rs | 37 ++++++++++++----------- protocols/rendezvous/tests/rendezvous.rs | 8 ++--- 5 files changed, 49 insertions(+), 37 deletions(-) create mode 100644 protocols/rendezvous/src/registration.rs diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 2811bc627f9..2b906454897 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -124,19 +124,23 @@ impl NetworkBehaviour for Rendezvous { Message::Register(new_reggo) => { let ttl = new_reggo.effective_ttl(); + self.registrations + .entry(new_reggo.namespace) + .or_insert_with(|| HashSet::new()) + .insert(new_reggo.record.peer_id()); + self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::RegisterRequest { - namespace: new_reggo.namespace, - ttl: Some(ttl), - record: todo!(), + event: Input::RegisterResponse { + ttl, + message: Message::SuccessfullyRegistered { ttl }, }, }) } Message::SuccessfullyRegistered { ttl } => { - // where to get namespace from + // where to get namespace from? self.events.push_back(NetworkBehaviourAction::GenerateEvent( Event::RegisteredWithRendezvousNode { rendezvous_node: peer_id, @@ -162,6 +166,7 @@ impl NetworkBehaviour for Rendezvous { peers.remove(&peer_id); } } + // todo: maybe send a unregister response to the remote? } Message::Discover { namespace } => { if let Some(ns) = namespace { @@ -175,7 +180,6 @@ impl NetworkBehaviour for Rendezvous { .iter() .map(|peer| (ns.clone(), peer.clone())) .collect(), - record: todo!(), }, }); } @@ -197,20 +201,14 @@ impl NetworkBehaviour for Rendezvous { .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::DiscoverResponse { - discovered, - record: todo!(), - }, + event: Input::DiscoverResponse { discovered }, }); } self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::DiscoverResponse { - record: todo!(), - discovered: vec![], - }, + event: Input::DiscoverResponse { discovered: vec![] }, }) } Message::DiscoverResponse { registrations } => { diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 8e552634b3d..b977c54d148 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -52,7 +52,6 @@ pub enum Input { message: Message, }, DiscoverResponse { - record: AuthenticatedPeerRecord, discovered: Vec<(String, PeerId)>, }, } @@ -167,7 +166,7 @@ impl ProtocolsHandler for RendezvousHandler { outbound, ) => (InboundState::PendingSend(substream, message), outbound), ( - Input::DiscoverResponse { record, discovered }, + Input::DiscoverResponse { discovered }, InboundState::WaitForBehaviour(substream), outbound, ) => { diff --git a/protocols/rendezvous/src/registration.rs b/protocols/rendezvous/src/registration.rs new file mode 100644 index 00000000000..06ca7735767 --- /dev/null +++ b/protocols/rendezvous/src/registration.rs @@ -0,0 +1,12 @@ +use libp2p_core::PeerId; +use std::collections::{HashMap, HashSet}; + +pub struct RegistrationStore { + registrations: HashMap>, +} + +impl RegistrationStore { + pub fn register(&self, registration: String, peer: PeerId) { + if let Some(peers) = self.registrations.get(®istration) {} + } +} diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 5c980f1a3b7..49fd5ffaa54 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -1,17 +1,19 @@ use futures::future; -use libp2p_core::{Executor, Multiaddr, PeerId, identity, Transport}; -use std::pin::Pin; use futures::Future; -use libp2p_swarm::{NetworkBehaviour, Swarm, IntoProtocolsHandler, ProtocolsHandler, SwarmBuilder, SwarmEvent}; -use std::fmt::Debug; -use libp2p_core::transport::MemoryTransport; +use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; +use libp2p_core::transport::MemoryTransport; use libp2p_core::upgrade::SelectUpgrade; -use std::time::Duration; -use libp2p_core::muxing::StreamMuxerBox; -use libp2p_noise::{self, NoiseConfig, X25519Spec, Keypair}; -use libp2p_yamux::YamuxConfig; +use libp2p_core::{identity, AuthenticatedPeerRecord, Executor, Multiaddr, Transport}; use libp2p_mplex::MplexConfig; +use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; +use libp2p_swarm::{ + IntoProtocolsHandler, NetworkBehaviour, ProtocolsHandler, Swarm, SwarmBuilder, SwarmEvent, +}; +use libp2p_yamux::YamuxConfig; +use std::fmt::Debug; +use std::pin::Pin; +use std::time::Duration; /// An adaptor struct for libp2p that spawns futures into the current /// thread-local runtime. @@ -27,13 +29,13 @@ impl Executor for GlobalSpawnTokioExecutor { pub struct Actor { pub swarm: Swarm, pub addr: Multiaddr, - pub peer_id: PeerId, + pub peer_id: AuthenticatedPeerRecord, } pub async fn new_connected_swarm_pair(behaviour_fn: F) -> (Actor, Actor) where B: NetworkBehaviour, - F: Fn(PeerId, identity::Keypair) -> B + Clone, + F: Fn(AuthenticatedPeerRecord, identity::Keypair) -> B + Clone, <<::ProtocolsHandler as IntoProtocolsHandler>::Handler as ProtocolsHandler>::InEvent: Clone, ::OutEvent: Debug{ let (swarm, addr, peer_id) = new_swarm(behaviour_fn.clone()); @@ -55,14 +57,14 @@ where (alice, bob) } -pub fn new_swarm B>( +pub fn new_swarm B>( behaviour_fn: F, -) -> (Swarm, Multiaddr, PeerId) +) -> (Swarm, Multiaddr, AuthenticatedPeerRecord) where B: NetworkBehaviour, { let id_keys = identity::Keypair::generate_ed25519(); - let peer_id = PeerId::from(id_keys.public()); + let peer_id = AuthenticatedPeerRecord::from(id_keys.public()); let dh_keys = Keypair::::new() .into_authentic(&id_keys) @@ -102,8 +104,8 @@ pub async fn await_events_or_timeout( Duration::from_secs(10), future::join(swarm_1_event, swarm_2_event), ) - .await - .expect("network behaviours to emit an event within 10 seconds") + .await + .expect("network behaviours to emit an event within 10 seconds") } /// Connects two swarms with each other. @@ -127,7 +129,8 @@ where let mut bob_connected = false; while !alice_connected && !bob_connected { - let (alice_event, bob_event) = future::join(receiver.next_event(), dialer.next_event()).await; + let (alice_event, bob_event) = + future::join(receiver.next_event(), dialer.next_event()).await; match alice_event { SwarmEvent::ConnectionEstablished { .. } => { diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 429082ed3eb..ce176d2db2c 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,6 +1,6 @@ pub mod harness; use crate::harness::{await_events_or_timeout, connect, new_swarm}; -use libp2p_core::PeerId; +use libp2p_core::AuthenticatedPeerRecord; use libp2p_rendezvous::behaviour::{Event, Rendezvous}; use libp2p_swarm::Swarm; use std::time::Duration; @@ -30,13 +30,13 @@ async fn given_successful_registration_then_successful_discovery() { struct RendezvousTest { pub registration_swarm: Swarm, - pub registration_peer_id: PeerId, + pub registration_peer_id: AuthenticatedPeerRecord, pub discovery_swarm: Swarm, - pub discovery_peer_id: PeerId, + pub discovery_peer_id: AuthenticatedPeerRecord, pub rendezvous_swarm: Swarm, - pub rendezvous_peer_id: PeerId, + pub rendezvous_peer_id: AuthenticatedPeerRecord, } impl RendezvousTest { From a89cffc7fc954e7d92fe20d9521a463012837640 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 1 Jun 2021 16:02:56 +1000 Subject: [PATCH 036/242] WIP - Aligen Behaviour, Handler and Codec --- protocols/rendezvous/src/behaviour.rs | 174 +++++++++++++--------- protocols/rendezvous/src/codec.rs | 14 +- protocols/rendezvous/src/handler.rs | 23 +-- protocols/rendezvous/tests/harness/mod.rs | 12 +- protocols/rendezvous/tests/rendezvous.rs | 9 +- 5 files changed, 126 insertions(+), 106 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 2b906454897..f51a356e79d 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,4 +1,4 @@ -use crate::codec::{ErrorCode, Message, Registration}; +use crate::codec::{ErrorCode, Message, Registration, NewRegistration}; use crate::handler::{Input, RendezvousHandler}; use libp2p_core::connection::ConnectionId; use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId}; @@ -9,22 +9,81 @@ use log::debug; use std::collections::{HashMap, HashSet, VecDeque}; use std::task::{Context, Poll}; +// TODO: Unit Tests +pub struct Registrations { + registrations_for_namespace: HashMap>, +} + +impl Registrations { + pub fn new() -> Self { + Self { + registrations_for_namespace: Default::default() + } + } + + pub fn add(&mut self, new_registration: NewRegistration) -> (String, i64) { + let ttl = new_registration.effective_ttl(); + let namespace = new_registration.namespace; + + self.registrations_for_namespace.entry(namespace.clone()) + .or_insert_with(|| HashMap::new()) + .insert( new_registration.record.peer_id(), Registration { + namespace: namespace.clone(), + record: new_registration.record, + ttl + }); + + (namespace, ttl) + } + + pub fn remove(&mut self, namespace: String, peer_id: PeerId) { + if let Some(registrations) = self.registrations_for_namespace.get_mut(&namespace) { + registrations.remove(&peer_id); + } + } + + pub fn get(&mut self, namespace: Option) -> Option> { + if self.registrations_for_namespace.is_empty() { + return None; + } + + if let Some(namespace) = namespace { + if let Some(registrations) = self.registrations_for_namespace.get(&namespace) { + Some(registrations.values().cloned().collect::>()) + } else { + None + } + } else { + let discovered = self + .registrations_for_namespace + .iter() + .map(|(ns, registrations)| { + registrations.values().cloned().collect::>() + }) + .flatten() + .collect::>(); + + Some(discovered) + } + } +} + pub struct Rendezvous { events: VecDeque>, - registrations: HashMap>, + registrations: Registrations, } impl Rendezvous { pub fn new() -> Self { Self { events: Default::default(), - registrations: Default::default(), + registrations: Registrations::new(), } } pub fn register( &mut self, - ns: String, + namespace: String, rendezvous_node: PeerId, record: AuthenticatedPeerRecord, ) { @@ -32,19 +91,21 @@ impl Rendezvous { .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, event: Input::RegisterRequest { - namespace: ns, - ttl: None, - record, + request: NewRegistration { + namespace, + record, + ttl: None + } }, handler: NotifyHandler::Any, }); } - pub fn unregister(&mut self, ns: String, rendezvous_node: PeerId) { + pub fn unregister(&mut self, namespace: String, rendezvous_node: PeerId) { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: Input::UnregisterRequest { namespace: ns }, + event: Input::UnregisterRequest { namespace }, handler: NotifyHandler::Any, }); } @@ -62,7 +123,7 @@ impl Rendezvous { pub enum Event { Discovered { rendezvous_node: PeerId, - ns: Vec, + registrations: Vec, }, FailedToDiscover { rendezvous_node: PeerId, @@ -70,25 +131,25 @@ pub enum Event { }, RegisteredWithRendezvousNode { rendezvous_node: PeerId, - ns: String, ttl: i64, + // TODO: get the namespace in as well, needs association between the registration request and the response }, FailedToRegisterWithRendezvousNode { rendezvous_node: PeerId, - ns: String, err_code: ErrorCode, + // TODO: get the namespace in as well, needs association between the registration request and the response }, DeclinedRegisterRequest { peer: PeerId, - ns: String, + // TODO: get the namespace in as well, needs association between the registration request and the response }, PeerRegistered { peer_id: PeerId, - ns: String, + namespace: String, }, PeerUnregistered { peer_id: PeerId, - ns: String, + namespace: String, }, } @@ -121,30 +182,24 @@ impl NetworkBehaviour for Rendezvous { event: crate::handler::HandlerEvent, ) { match event.0 { - Message::Register(new_reggo) => { - let ttl = new_reggo.effective_ttl(); + Message::Register(new_registration) => { + let (namespace, ttl) = self.registrations.add(new_registration); - self.registrations - .entry(new_reggo.namespace) - .or_insert_with(|| HashSet::new()) - .insert(new_reggo.record.peer_id()); + // emit behaviour event + self.events.push_back(NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { peer_id, namespace })); + // notify the handler that to send a response self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::RegisterResponse { - ttl, - message: Message::SuccessfullyRegistered { ttl }, - }, - }) + event: Input::RegisterResponse { ttl }, + }); } - Message::SuccessfullyRegistered { ttl } => { - // where to get namespace from? + Message::RegisterResponse { ttl } => { self.events.push_back(NetworkBehaviourAction::GenerateEvent( Event::RegisteredWithRendezvousNode { rendezvous_node: peer_id, - ns: "".to_string(), ttl, }, )) @@ -153,69 +208,40 @@ impl NetworkBehaviour for Rendezvous { self.events.push_back(NetworkBehaviourAction::GenerateEvent( Event::FailedToRegisterWithRendezvousNode { rendezvous_node: peer_id, - // todo: need to get the namespace somehow? The handler will probably have to remember - // the request this message is a response to as the wire message does not contain this info - ns: "".to_string(), err_code: error, }, )) } Message::Unregister { namespace } => { - if let Some(peers) = self.registrations.get_mut(&namespace) { - if peers.contains(&peer_id) { - peers.remove(&peer_id); - } - } - // todo: maybe send a unregister response to the remote? + self.registrations.remove(namespace, peer_id); + // TODO: Should send unregister response? } Message::Discover { namespace } => { - if let Some(ns) = namespace { - if let Some(peers) = self.registrations.get_mut(&ns) { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::DiscoverResponse { - discovered: peers - .iter() - .map(|peer| (ns.clone(), peer.clone())) - .collect(), - }, - }); - } - } else { - let discovered = self - .registrations - .iter() - .map(|(ns, peers)| { - peers - .iter() - .map(|peer_id| (ns.clone(), peer_id.clone())) - .collect::>() - .into_iter() - }) - .flatten() - .collect::>(); + let registrations = self.registrations.get(namespace); + + if let Some(registrations) = registrations { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::DiscoverResponse { discovered }, - }); + event: Input::DiscoverResponse { + discovered: registrations + }}) + } else { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::DiscoverResponse { discovered: vec![] }, + }) } - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::DiscoverResponse { discovered: vec![] }, - }) } Message::DiscoverResponse { registrations } => { self.events .push_back(NetworkBehaviourAction::GenerateEvent(Event::Discovered { rendezvous_node: peer_id, - ns: registrations, + registrations, })) } Message::FailedToDiscover { error } => self.events.push_back( diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index a2bac19392a..aff29d4a4c0 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -6,7 +6,7 @@ use unsigned_varint::codec::UviBytes; #[derive(Debug)] pub enum Message { Register(NewRegistration), - SuccessfullyRegistered { + RegisterResponse { ttl: i64, }, FailedToRegister { @@ -34,7 +34,7 @@ pub enum Message { pub struct NewRegistration { pub namespace: String, pub record: AuthenticatedPeerRecord, - ttl: Option, + pub ttl: Option, } /// If unspecified, rendezvous nodes should assume a TTL of 2h. @@ -56,11 +56,11 @@ impl NewRegistration { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Registration { pub namespace: String, pub record: AuthenticatedPeerRecord, - // pub ttl: i64, // TODO: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds + pub ttl: i64, // TODO THEZ: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds } #[derive(Debug)] @@ -163,7 +163,7 @@ impl From for wire::Message { discover: None, discover_response: None, }, - Message::SuccessfullyRegistered { ttl } => wire::Message { + Message::RegisterResponse { ttl } => wire::Message { r#type: Some(MessageType::RegisterResponse.into()), register_response: Some(RegisterResponse { status: Some(ResponseStatus::Ok.into()), @@ -279,7 +279,7 @@ impl TryFrom for Message { .. }), .. - } => Message::SuccessfullyRegistered { + } => Message::RegisterResponse { ttl: ttl.ok_or(ConversionError::MissingTtl)?, }, wire::Message { @@ -309,6 +309,7 @@ impl TryFrom for Message { .ok_or(ConversionError::MissingSignedPeerRecord)?, )?, )?, + ttl: reggo.ttl.ok_or(ConversionError::MissingTtl)?, }) }) .collect::, ConversionError>>()?, @@ -360,6 +361,7 @@ impl TryFrom for Message { .ok_or(ConversionError::MissingSignedPeerRecord)?, )?, )?, + ttl: reggo.ttl.ok_or(ConversionError::MissingTtl)?, }) }) .collect::, ConversionError>>()?, diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index b977c54d148..f00bfb3994b 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -34,9 +34,7 @@ pub struct HandlerEvent(pub Message); #[derive(Debug)] pub enum Input { RegisterRequest { - namespace: String, - ttl: Option, - record: AuthenticatedPeerRecord, + request: NewRegistration }, UnregisterRequest { namespace: String, @@ -49,10 +47,9 @@ pub enum Input { }, RegisterResponse { ttl: i64, - message: Message, }, DiscoverResponse { - discovered: Vec<(String, PeerId)>, + discovered: Vec, }, } @@ -134,9 +131,9 @@ impl ProtocolsHandler for RendezvousHandler { ) { ( Input::RegisterRequest { - namespace, - ttl, - record, + request: NewRegistration { + namespace, record, ttl + } }, inbound, OutboundState::None, @@ -161,10 +158,10 @@ impl ProtocolsHandler for RendezvousHandler { }), ), ( - Input::RegisterResponse { ttl: _ttl, message }, + Input::RegisterResponse { ttl }, InboundState::WaitForBehaviour(substream), outbound, - ) => (InboundState::PendingSend(substream, message), outbound), + ) => (InboundState::PendingSend(substream, Message::RegisterResponse { ttl }), outbound), ( Input::DiscoverResponse { discovered }, InboundState::WaitForBehaviour(substream), @@ -172,12 +169,6 @@ impl ProtocolsHandler for RendezvousHandler { ) => { let msg = Message::DiscoverResponse { registrations: discovered - .iter() - .map(|d| Registration { - namespace: d.0.to_string(), - record: record.clone(), - }) - .collect(), }; (InboundState::PendingSend(substream, msg), outbound) } diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 49fd5ffaa54..cb7e06b96ea 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -4,7 +4,7 @@ use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; use libp2p_core::transport::MemoryTransport; use libp2p_core::upgrade::SelectUpgrade; -use libp2p_core::{identity, AuthenticatedPeerRecord, Executor, Multiaddr, Transport}; +use libp2p_core::{identity, Executor, Multiaddr, Transport, PeerId}; use libp2p_mplex::MplexConfig; use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; use libp2p_swarm::{ @@ -29,13 +29,13 @@ impl Executor for GlobalSpawnTokioExecutor { pub struct Actor { pub swarm: Swarm, pub addr: Multiaddr, - pub peer_id: AuthenticatedPeerRecord, + pub peer_id: PeerId, } pub async fn new_connected_swarm_pair(behaviour_fn: F) -> (Actor, Actor) where B: NetworkBehaviour, - F: Fn(AuthenticatedPeerRecord, identity::Keypair) -> B + Clone, + F: Fn(PeerId, identity::Keypair) -> B + Clone, <<::ProtocolsHandler as IntoProtocolsHandler>::Handler as ProtocolsHandler>::InEvent: Clone, ::OutEvent: Debug{ let (swarm, addr, peer_id) = new_swarm(behaviour_fn.clone()); @@ -57,14 +57,14 @@ where (alice, bob) } -pub fn new_swarm B>( +pub fn new_swarm B>( behaviour_fn: F, -) -> (Swarm, Multiaddr, AuthenticatedPeerRecord) +) -> (Swarm, Multiaddr, PeerId) where B: NetworkBehaviour, { let id_keys = identity::Keypair::generate_ed25519(); - let peer_id = AuthenticatedPeerRecord::from(id_keys.public()); + let peer_id = PeerId::from(id_keys.public()); let dh_keys = Keypair::::new() .into_authentic(&id_keys) diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index ce176d2db2c..5b7f9ad1782 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,9 +1,10 @@ pub mod harness; use crate::harness::{await_events_or_timeout, connect, new_swarm}; -use libp2p_core::AuthenticatedPeerRecord; +use libp2p_core::{AuthenticatedPeerRecord, PeerId}; use libp2p_rendezvous::behaviour::{Event, Rendezvous}; use libp2p_swarm::Swarm; use std::time::Duration; +use libp2p_core::network::Peer; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { @@ -30,13 +31,13 @@ async fn given_successful_registration_then_successful_discovery() { struct RendezvousTest { pub registration_swarm: Swarm, - pub registration_peer_id: AuthenticatedPeerRecord, + pub registration_peer_id: PeerId, pub discovery_swarm: Swarm, - pub discovery_peer_id: AuthenticatedPeerRecord, + pub discovery_peer_id: PeerId, pub rendezvous_swarm: Swarm, - pub rendezvous_peer_id: AuthenticatedPeerRecord, + pub rendezvous_peer_id: PeerId, } impl RendezvousTest { From 4ffaa5f8049ccf097d45984a836578fcf68543cc Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 1 Jun 2021 17:32:34 +1000 Subject: [PATCH 037/242] WIP - Test running, but behaviour does not emit events as expected --- protocols/rendezvous/src/behaviour.rs | 23 +++++++-- protocols/rendezvous/tests/harness/mod.rs | 37 ++------------- protocols/rendezvous/tests/rendezvous.rs | 57 +++++++++++++++++++---- 3 files changed, 70 insertions(+), 47 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index f51a356e79d..9694a83feae 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,13 +1,14 @@ use crate::codec::{ErrorCode, Message, Registration, NewRegistration}; use crate::handler::{Input, RendezvousHandler}; use libp2p_core::connection::ConnectionId; -use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId}; +use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId, PeerRecord, SignedEnvelope}; use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; use log::debug; use std::collections::{HashMap, HashSet, VecDeque}; use std::task::{Context, Poll}; +use libp2p_core::identity::Keypair; // TODO: Unit Tests pub struct Registrations { @@ -68,16 +69,28 @@ impl Registrations { } } +pub const DOMAIN: &str = "libp2p-rendezvous"; + pub struct Rendezvous { events: VecDeque>, registrations: Registrations, + key_pair: Keypair, + peer_record: PeerRecord, } impl Rendezvous { - pub fn new() -> Self { + pub fn new(key_pair: Keypair, listen_addresses: Vec) -> Self { + let peer_record = PeerRecord { + peer_id: key_pair.public().into_peer_id(), + seq: 0, + addresses: listen_addresses + }; + Self { events: Default::default(), registrations: Registrations::new(), + key_pair, + peer_record, } } @@ -85,15 +98,17 @@ impl Rendezvous { &mut self, namespace: String, rendezvous_node: PeerId, - record: AuthenticatedPeerRecord, ) { + + let authenticated_peer_record = AuthenticatedPeerRecord::from_record(self.key_pair.clone(), self.peer_record.clone()); + self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, event: Input::RegisterRequest { request: NewRegistration { namespace, - record, + record: authenticated_peer_record, ttl: None } }, diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index cb7e06b96ea..42bfec052a7 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -32,38 +32,12 @@ pub struct Actor { pub peer_id: PeerId, } -pub async fn new_connected_swarm_pair(behaviour_fn: F) -> (Actor, Actor) -where - B: NetworkBehaviour, - F: Fn(PeerId, identity::Keypair) -> B + Clone, - <<::ProtocolsHandler as IntoProtocolsHandler>::Handler as ProtocolsHandler>::InEvent: Clone, -::OutEvent: Debug{ - let (swarm, addr, peer_id) = new_swarm(behaviour_fn.clone()); - let mut alice = Actor { - swarm, - addr, - peer_id, - }; - - let (swarm, addr, peer_id) = new_swarm(behaviour_fn); - let mut bob = Actor { - swarm, - addr, - peer_id, - }; - - connect(&mut alice.swarm, &mut bob.swarm).await; - - (alice, bob) -} - pub fn new_swarm B>( - behaviour_fn: F, + behaviour_fn: F, id_keys: identity::Keypair, listen_address: Multiaddr, ) -> (Swarm, Multiaddr, PeerId) where B: NetworkBehaviour, { - let id_keys = identity::Keypair::generate_ed25519(); let peer_id = PeerId::from(id_keys.public()); let dh_keys = Keypair::::new() @@ -86,14 +60,9 @@ where .executor(Box::new(GlobalSpawnTokioExecutor)) .build(); - let address_port = rand::random::(); - let addr = format!("/memory/{}", address_port) - .parse::() - .unwrap(); - - Swarm::listen_on(&mut swarm, addr.clone()).unwrap(); + Swarm::listen_on(&mut swarm, listen_address.clone()).unwrap(); - (swarm, addr, peer_id) + (swarm, listen_address, peer_id) } pub async fn await_events_or_timeout( diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 5b7f9ad1782..ccfc59d0498 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,10 +1,12 @@ pub mod harness; use crate::harness::{await_events_or_timeout, connect, new_swarm}; -use libp2p_core::{AuthenticatedPeerRecord, PeerId}; +use libp2p_core::{AuthenticatedPeerRecord, PeerId, Multiaddr}; use libp2p_rendezvous::behaviour::{Event, Rendezvous}; use libp2p_swarm::Swarm; use std::time::Duration; use libp2p_core::network::Peer; +use libp2p_core::identity::Keypair; +use std::str::FromStr; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { @@ -12,15 +14,18 @@ async fn given_successful_registration_then_successful_discovery() { let namespace = "some-namespace".to_string(); + println!("registring"); + // register test.registration_swarm.behaviour_mut().register( namespace.clone(), test.rendezvous_peer_id, - todo!(), ); test.assert_successful_registration(namespace.clone()).await; + println!("Registration worked!"); + // discover test.discovery_swarm .behaviour_mut() @@ -40,13 +45,49 @@ struct RendezvousTest { pub rendezvous_peer_id: PeerId, } +fn get_rand_listen_addr() -> Multiaddr { + let address_port = rand::random::(); + let addr = format!("/memory/{}", address_port) + .parse::() + .unwrap(); + + addr +} + impl RendezvousTest { pub async fn setup() -> Self { - let (mut registration_swarm, _, registration_peer_id) = - harness::new_swarm(|_, _| Rendezvous::new()); - let (mut discovery_swarm, _, discovery_peer_id) = new_swarm(|_, _| Rendezvous::new()); - let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm(|_, _| Rendezvous::new()); + let registration_keys = Keypair::generate_ed25519(); + let discovery_keys = Keypair::generate_ed25519(); + let rendezvous_keys = Keypair::generate_ed25519(); + + let registration_addr = get_rand_listen_addr(); + let discovery_addr = get_rand_listen_addr(); + let rendezvous_addr = get_rand_listen_addr(); + + let (mut registration_swarm, _, registration_peer_id) = + new_swarm(|_, _| Rendezvous::new( + registration_keys.clone(), + vec![registration_addr.clone()]), + registration_keys.clone(), + registration_addr.clone() + ); + + let (mut discovery_swarm, _, discovery_peer_id) = + new_swarm(|_, _| Rendezvous::new( + discovery_keys.clone(), + vec![discovery_addr.clone()]), + discovery_keys.clone(), + discovery_addr.clone() + ); + + let (mut rendezvous_swarm, _, rendezvous_peer_id) = + new_swarm(|_, _| Rendezvous::new( + rendezvous_keys.clone(), + vec![rendezvous_addr.clone()]), + rendezvous_keys.clone(), + rendezvous_addr.clone(), + ); connect(&mut rendezvous_swarm, &mut discovery_swarm).await; connect(&mut rendezvous_swarm, &mut registration_swarm).await; @@ -68,9 +109,7 @@ impl RendezvousTest { registration_swarm_event, ) => { - // TODO: Assertion against the actual peer record possible? - // let expected = Event::PeerRegistered { peer_id: self.registration_peer_id, ns: Registration { namespace, record: AuthenticatedPeerRecord::from_record() } }; - + // TODO: Assertion against the actual peer record, pass the peer record in for assertion assert!(matches!(rendezvous_swarm_event, Event::PeerRegistered { .. })); assert!(matches!(registration_swarm_event, Event::RegisteredWithRendezvousNode { .. })); From 585e4a793cc908547f923545b78a1f442be69998 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 1 Jun 2021 17:44:40 +1000 Subject: [PATCH 038/242] Fix unused imports --- protocols/rendezvous/src/behaviour.rs | 94 ++++++++++++----------- protocols/rendezvous/src/handler.rs | 20 +++-- protocols/rendezvous/tests/harness/mod.rs | 10 +-- protocols/rendezvous/tests/rendezvous.rs | 49 +++++------- 4 files changed, 88 insertions(+), 85 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 9694a83feae..5e1e54366a1 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,14 +1,14 @@ -use crate::codec::{ErrorCode, Message, Registration, NewRegistration}; +use crate::codec::{ErrorCode, Message, NewRegistration, Registration}; use crate::handler::{Input, RendezvousHandler}; use libp2p_core::connection::ConnectionId; -use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId, PeerRecord, SignedEnvelope}; +use libp2p_core::identity::Keypair; +use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId, PeerRecord}; use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; use log::debug; -use std::collections::{HashMap, HashSet, VecDeque}; +use std::collections::{HashMap, VecDeque}; use std::task::{Context, Poll}; -use libp2p_core::identity::Keypair; // TODO: Unit Tests pub struct Registrations { @@ -18,7 +18,7 @@ pub struct Registrations { impl Registrations { pub fn new() -> Self { Self { - registrations_for_namespace: Default::default() + registrations_for_namespace: Default::default(), } } @@ -26,13 +26,17 @@ impl Registrations { let ttl = new_registration.effective_ttl(); let namespace = new_registration.namespace; - self.registrations_for_namespace.entry(namespace.clone()) + self.registrations_for_namespace + .entry(namespace.clone()) .or_insert_with(|| HashMap::new()) - .insert( new_registration.record.peer_id(), Registration { - namespace: namespace.clone(), - record: new_registration.record, - ttl - }); + .insert( + new_registration.record.peer_id(), + Registration { + namespace: namespace.clone(), + record: new_registration.record, + ttl, + }, + ); (namespace, ttl) } @@ -50,7 +54,12 @@ impl Registrations { if let Some(namespace) = namespace { if let Some(registrations) = self.registrations_for_namespace.get(&namespace) { - Some(registrations.values().cloned().collect::>()) + Some( + registrations + .values() + .cloned() + .collect::>(), + ) } else { None } @@ -58,8 +67,11 @@ impl Registrations { let discovered = self .registrations_for_namespace .iter() - .map(|(ns, registrations)| { - registrations.values().cloned().collect::>() + .map(|(_, registrations)| { + registrations + .values() + .cloned() + .collect::>() }) .flatten() .collect::>(); @@ -83,7 +95,7 @@ impl Rendezvous { let peer_record = PeerRecord { peer_id: key_pair.public().into_peer_id(), seq: 0, - addresses: listen_addresses + addresses: listen_addresses, }; Self { @@ -94,13 +106,9 @@ impl Rendezvous { } } - pub fn register( - &mut self, - namespace: String, - rendezvous_node: PeerId, - ) { - - let authenticated_peer_record = AuthenticatedPeerRecord::from_record(self.key_pair.clone(), self.peer_record.clone()); + pub fn register(&mut self, namespace: String, rendezvous_node: PeerId) { + let authenticated_peer_record = + AuthenticatedPeerRecord::from_record(self.key_pair.clone(), self.peer_record.clone()); self.events .push_back(NetworkBehaviourAction::NotifyHandler { @@ -109,8 +117,8 @@ impl Rendezvous { request: NewRegistration { namespace, record: authenticated_peer_record, - ttl: None - } + ttl: None, + }, }, handler: NotifyHandler::Any, }); @@ -201,7 +209,9 @@ impl NetworkBehaviour for Rendezvous { let (namespace, ttl) = self.registrations.add(new_registration); // emit behaviour event - self.events.push_back(NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { peer_id, namespace })); + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::PeerRegistered { peer_id, namespace }, + )); // notify the handler that to send a response self.events @@ -211,28 +221,23 @@ impl NetworkBehaviour for Rendezvous { event: Input::RegisterResponse { ttl }, }); } - Message::RegisterResponse { ttl } => { - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::RegisteredWithRendezvousNode { - rendezvous_node: peer_id, - ttl, - }, - )) - } - Message::FailedToRegister { error } => { - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::FailedToRegisterWithRendezvousNode { - rendezvous_node: peer_id, - err_code: error, - }, - )) - } + Message::RegisterResponse { ttl } => self.events.push_back( + NetworkBehaviourAction::GenerateEvent(Event::RegisteredWithRendezvousNode { + rendezvous_node: peer_id, + ttl, + }), + ), + Message::FailedToRegister { error } => self.events.push_back( + NetworkBehaviourAction::GenerateEvent(Event::FailedToRegisterWithRendezvousNode { + rendezvous_node: peer_id, + err_code: error, + }), + ), Message::Unregister { namespace } => { self.registrations.remove(namespace, peer_id); // TODO: Should send unregister response? } Message::Discover { namespace } => { - let registrations = self.registrations.get(namespace); if let Some(registrations) = registrations { @@ -241,8 +246,9 @@ impl NetworkBehaviour for Rendezvous { peer_id, handler: NotifyHandler::Any, event: Input::DiscoverResponse { - discovered: registrations - }}) + discovered: registrations, + }, + }) } else { self.events .push_back(NetworkBehaviourAction::NotifyHandler { diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index f00bfb3994b..9b41462abe5 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -4,7 +4,7 @@ use crate::protocol; use crate::protocol::Rendezvous; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; -use libp2p_core::{AuthenticatedPeerRecord, InboundUpgrade, OutboundUpgrade, PeerId}; +use libp2p_core::{InboundUpgrade, OutboundUpgrade}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, @@ -34,7 +34,7 @@ pub struct HandlerEvent(pub Message); #[derive(Debug)] pub enum Input { RegisterRequest { - request: NewRegistration + request: NewRegistration, }, UnregisterRequest { namespace: String, @@ -131,9 +131,12 @@ impl ProtocolsHandler for RendezvousHandler { ) { ( Input::RegisterRequest { - request: NewRegistration { - namespace, record, ttl - } + request: + NewRegistration { + namespace, + record, + ttl, + }, }, inbound, OutboundState::None, @@ -161,14 +164,17 @@ impl ProtocolsHandler for RendezvousHandler { Input::RegisterResponse { ttl }, InboundState::WaitForBehaviour(substream), outbound, - ) => (InboundState::PendingSend(substream, Message::RegisterResponse { ttl }), outbound), + ) => ( + InboundState::PendingSend(substream, Message::RegisterResponse { ttl }), + outbound, + ), ( Input::DiscoverResponse { discovered }, InboundState::WaitForBehaviour(substream), outbound, ) => { let msg = Message::DiscoverResponse { - registrations: discovered + registrations: discovered, }; (InboundState::PendingSend(substream, msg), outbound) } diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 42bfec052a7..acd3b220385 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -4,12 +4,10 @@ use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; use libp2p_core::transport::MemoryTransport; use libp2p_core::upgrade::SelectUpgrade; -use libp2p_core::{identity, Executor, Multiaddr, Transport, PeerId}; +use libp2p_core::{identity, Executor, Multiaddr, PeerId, Transport}; use libp2p_mplex::MplexConfig; use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; -use libp2p_swarm::{ - IntoProtocolsHandler, NetworkBehaviour, ProtocolsHandler, Swarm, SwarmBuilder, SwarmEvent, -}; +use libp2p_swarm::{NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; use libp2p_yamux::YamuxConfig; use std::fmt::Debug; use std::pin::Pin; @@ -33,7 +31,9 @@ pub struct Actor { } pub fn new_swarm B>( - behaviour_fn: F, id_keys: identity::Keypair, listen_address: Multiaddr, + behaviour_fn: F, + id_keys: identity::Keypair, + listen_address: Multiaddr, ) -> (Swarm, Multiaddr, PeerId) where B: NetworkBehaviour, diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index ccfc59d0498..761a29a1ae5 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,12 +1,11 @@ pub mod harness; use crate::harness::{await_events_or_timeout, connect, new_swarm}; -use libp2p_core::{AuthenticatedPeerRecord, PeerId, Multiaddr}; +use libp2p_core::{Multiaddr, PeerId}; use libp2p_rendezvous::behaviour::{Event, Rendezvous}; use libp2p_swarm::Swarm; use std::time::Duration; -use libp2p_core::network::Peer; + use libp2p_core::identity::Keypair; -use std::str::FromStr; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { @@ -17,10 +16,9 @@ async fn given_successful_registration_then_successful_discovery() { println!("registring"); // register - test.registration_swarm.behaviour_mut().register( - namespace.clone(), - test.rendezvous_peer_id, - ); + test.registration_swarm + .behaviour_mut() + .register(namespace.clone(), test.rendezvous_peer_id); test.assert_successful_registration(namespace.clone()).await; @@ -56,7 +54,6 @@ fn get_rand_listen_addr() -> Multiaddr { impl RendezvousTest { pub async fn setup() -> Self { - let registration_keys = Keypair::generate_ed25519(); let discovery_keys = Keypair::generate_ed25519(); let rendezvous_keys = Keypair::generate_ed25519(); @@ -65,29 +62,23 @@ impl RendezvousTest { let discovery_addr = get_rand_listen_addr(); let rendezvous_addr = get_rand_listen_addr(); - let (mut registration_swarm, _, registration_peer_id) = - new_swarm(|_, _| Rendezvous::new( - registration_keys.clone(), - vec![registration_addr.clone()]), - registration_keys.clone(), - registration_addr.clone() - ); + let (mut registration_swarm, _, registration_peer_id) = new_swarm( + |_, _| Rendezvous::new(registration_keys.clone(), vec![registration_addr.clone()]), + registration_keys.clone(), + registration_addr.clone(), + ); - let (mut discovery_swarm, _, discovery_peer_id) = - new_swarm(|_, _| Rendezvous::new( + let (mut discovery_swarm, _, discovery_peer_id) = new_swarm( + |_, _| Rendezvous::new(discovery_keys.clone(), vec![discovery_addr.clone()]), discovery_keys.clone(), - vec![discovery_addr.clone()]), - discovery_keys.clone(), - discovery_addr.clone() - ); - - let (mut rendezvous_swarm, _, rendezvous_peer_id) = - new_swarm(|_, _| Rendezvous::new( - rendezvous_keys.clone(), - vec![rendezvous_addr.clone()]), - rendezvous_keys.clone(), - rendezvous_addr.clone(), - ); + discovery_addr.clone(), + ); + + let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm( + |_, _| Rendezvous::new(rendezvous_keys.clone(), vec![rendezvous_addr.clone()]), + rendezvous_keys.clone(), + rendezvous_addr.clone(), + ); connect(&mut rendezvous_swarm, &mut discovery_swarm).await; connect(&mut rendezvous_swarm, &mut registration_swarm).await; From 20ce11fc00e1b5aade96a5555362ec5f4ac2ca77 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 1 Jun 2021 18:39:34 +1000 Subject: [PATCH 039/242] Reorg behaviour --- protocols/rendezvous/src/behaviour.rs | 142 +++++++++++++------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 5e1e54366a1..e2d8a5dd812 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -10,77 +10,6 @@ use log::debug; use std::collections::{HashMap, VecDeque}; use std::task::{Context, Poll}; -// TODO: Unit Tests -pub struct Registrations { - registrations_for_namespace: HashMap>, -} - -impl Registrations { - pub fn new() -> Self { - Self { - registrations_for_namespace: Default::default(), - } - } - - pub fn add(&mut self, new_registration: NewRegistration) -> (String, i64) { - let ttl = new_registration.effective_ttl(); - let namespace = new_registration.namespace; - - self.registrations_for_namespace - .entry(namespace.clone()) - .or_insert_with(|| HashMap::new()) - .insert( - new_registration.record.peer_id(), - Registration { - namespace: namespace.clone(), - record: new_registration.record, - ttl, - }, - ); - - (namespace, ttl) - } - - pub fn remove(&mut self, namespace: String, peer_id: PeerId) { - if let Some(registrations) = self.registrations_for_namespace.get_mut(&namespace) { - registrations.remove(&peer_id); - } - } - - pub fn get(&mut self, namespace: Option) -> Option> { - if self.registrations_for_namespace.is_empty() { - return None; - } - - if let Some(namespace) = namespace { - if let Some(registrations) = self.registrations_for_namespace.get(&namespace) { - Some( - registrations - .values() - .cloned() - .collect::>(), - ) - } else { - None - } - } else { - let discovered = self - .registrations_for_namespace - .iter() - .map(|(_, registrations)| { - registrations - .values() - .cloned() - .collect::>() - }) - .flatten() - .collect::>(); - - Some(discovered) - } - } -} - pub const DOMAIN: &str = "libp2p-rendezvous"; pub struct Rendezvous { @@ -291,3 +220,74 @@ impl NetworkBehaviour for Rendezvous { Poll::Pending } } + +// TODO: Unit Tests +pub struct Registrations { + registrations_for_namespace: HashMap>, +} + +impl Registrations { + pub fn new() -> Self { + Self { + registrations_for_namespace: Default::default(), + } + } + + pub fn add(&mut self, new_registration: NewRegistration) -> (String, i64) { + let ttl = new_registration.effective_ttl(); + let namespace = new_registration.namespace; + + self.registrations_for_namespace + .entry(namespace.clone()) + .or_insert_with(|| HashMap::new()) + .insert( + new_registration.record.peer_id(), + Registration { + namespace: namespace.clone(), + record: new_registration.record, + ttl, + }, + ); + + (namespace, ttl) + } + + pub fn remove(&mut self, namespace: String, peer_id: PeerId) { + if let Some(registrations) = self.registrations_for_namespace.get_mut(&namespace) { + registrations.remove(&peer_id); + } + } + + pub fn get(&mut self, namespace: Option) -> Option> { + if self.registrations_for_namespace.is_empty() { + return None; + } + + if let Some(namespace) = namespace { + if let Some(registrations) = self.registrations_for_namespace.get(&namespace) { + Some( + registrations + .values() + .cloned() + .collect::>(), + ) + } else { + None + } + } else { + let discovered = self + .registrations_for_namespace + .iter() + .map(|(_, registrations)| { + registrations + .values() + .cloned() + .collect::>() + }) + .flatten() + .collect::>(); + + Some(discovered) + } + } +} From b4847186cb61e90e05689e21e3db63871f0ef556 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 1 Jun 2021 18:56:45 +1000 Subject: [PATCH 040/242] Added debug prints, Handler not being polled? The protocol handler is being spawned once during initialisation and then again during the test phase. This seems weird? --- protocols/rendezvous/Cargo.toml | 1 + protocols/rendezvous/src/behaviour.rs | 35 ++++++++++++++++ protocols/rendezvous/src/handler.rs | 49 ++++++++++++++++++++++- protocols/rendezvous/src/protocol.rs | 11 +++-- protocols/rendezvous/tests/harness/mod.rs | 12 ++++-- protocols/rendezvous/tests/rendezvous.rs | 21 ++++++---- 6 files changed, 111 insertions(+), 18 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 5faf0886c35..38ed09e2ccf 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -26,6 +26,7 @@ libp2p-noise = { version = "0.31", path = "../../transports/noise" } libp2p-yamux = { version = "0.32", path = "../../muxers/yamux" } rand = "0.8" tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } +env_logger = "0.8" [build-dependencies] prost-build = "0.7" diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index e2d8a5dd812..a38b8e66cb5 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -110,6 +110,7 @@ impl NetworkBehaviour for Rendezvous { type OutEvent = Event; fn new_handler(&mut self) -> Self::ProtocolsHandler { + debug!("spawning protocol handler"); RendezvousHandler::new() } @@ -133,6 +134,7 @@ impl NetworkBehaviour for Rendezvous { _connection: ConnectionId, event: crate::handler::HandlerEvent, ) { + debug!("behaviour::inject_event {:?}", &event); match event.0 { Message::Register(new_registration) => { let (namespace, ttl) = self.registrations.add(new_registration); @@ -214,9 +216,42 @@ impl NetworkBehaviour for Rendezvous { >, > { if let Some(event) = self.events.pop_front() { + debug!("polling behaviour: {:?}", &event); return Poll::Ready(event); } + // if let Some(event) = self.events.pop_front() { + // return Poll::Ready(match event { + // NetworkBehaviourAction::NotifyHandler { + // peer_id, + // handler, + // event, + // } => { + // debug!("polling notifying handler: {:?}", &event); + // return Poll::Ready(NetworkBehaviourAction::NotifyHandler { + // peer_id, + // event, + // handler, + // }); + // } + // NetworkBehaviourAction::GenerateEvent(e) => { + // return Poll::Ready(NetworkBehaviourAction::GenerateEvent(e)); + // } + // NetworkBehaviourAction::DialAddress { address } => { + // return Poll::Ready(NetworkBehaviourAction::DialAddress { address }); + // } + // NetworkBehaviourAction::DialPeer { peer_id, condition } => { + // return Poll::Ready(NetworkBehaviourAction::DialPeer { peer_id, condition }); + // } + // NetworkBehaviourAction::ReportObservedAddr { address, score } => { + // return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { + // address, + // score, + // }) + // } + // }); + // } + Poll::Pending } } diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 9b41462abe5..f3af2860cbe 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -9,10 +9,14 @@ use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; +use log::debug; use log::error; +use std::fmt; +use std::fmt::{Debug, Formatter}; use std::task::{Context, Poll}; use void::Void; +#[derive(Debug)] pub struct RendezvousHandler { outbound_substream: OutboundState, inbound_substream: InboundState, @@ -29,6 +33,7 @@ impl RendezvousHandler { } } +#[derive(Debug)] pub struct HandlerEvent(pub Message); #[derive(Debug)] @@ -68,6 +73,20 @@ enum InboundState { Poisoned, } +impl Debug for InboundState { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + InboundState::None => f.write_str("none"), + InboundState::Reading(_) => f.write_str("reading"), + InboundState::PendingSend(_, _) => f.write_str("pending_send"), + InboundState::PendingFlush(_) => f.write_str("pending_flush"), + InboundState::WaitForBehaviour(_) => f.write_str("waiting_for_behaviour"), + InboundState::Closing(_) => f.write_str("closing"), + InboundState::Poisoned => f.write_str("poisoned"), + } + } +} + /// State of the outbound substream, opened either by us or by the remote. enum OutboundState { None, @@ -84,6 +103,21 @@ enum OutboundState { Poisoned, } +impl Debug for OutboundState { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + OutboundState::None => f.write_str("none"), + OutboundState::Start(_) => f.write_str("start"), + OutboundState::WaitingUpgrade => f.write_str("waiting_upgrade"), + OutboundState::PendingSend(_, _) => f.write_str("pending_send"), + OutboundState::PendingFlush(_) => f.write_str("pending_flush"), + OutboundState::WaitForRemote(_) => f.write_str("waiting_for_remote"), + OutboundState::Closing(_) => f.write_str("closing"), + OutboundState::Poisoned => f.write_str("poisoned"), + } + } +} + impl ProtocolsHandler for RendezvousHandler { type InEvent = Input; type OutEvent = HandlerEvent; @@ -94,8 +128,8 @@ impl ProtocolsHandler for RendezvousHandler { type OutboundProtocol = protocol::Rendezvous; fn listen_protocol(&self) -> SubstreamProtocol { - let rendezvous_protocol = crate::protocol::Rendezvous::new(); - SubstreamProtocol::new(rendezvous_protocol, ()) + debug!("creating substream protocol"); + SubstreamProtocol::new(protocol::Rendezvous::new(), ()) } fn inject_fully_negotiated_inbound( @@ -103,6 +137,7 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, _msg: Self::InboundOpenInfo, ) { + debug!("injected inbound"); if let InboundState::None = self.inbound_substream { self.inbound_substream = InboundState::Reading(substream); } else { @@ -115,6 +150,7 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, msg: Self::OutboundOpenInfo, ) { + debug!("injected outbound"); if let OutboundState::WaitingUpgrade = self.outbound_substream { self.outbound_substream = OutboundState::PendingSend(substream, msg); } else { @@ -124,6 +160,7 @@ impl ProtocolsHandler for RendezvousHandler { // event injected from NotifyHandler fn inject_event(&mut self, req: Input) { + debug!("injecting event into handler from behaviour: {:?}", &req); let (inbound_substream, outbound_substream) = match ( req, std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned), @@ -208,6 +245,14 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { + debug!( + "polling handler: inbound_state: {:?}", + &self.inbound_substream + ); + debug!( + "polling handler: outbound_state {:?}", + &self.outbound_substream + ); match std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned) { InboundState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { diff --git a/protocols/rendezvous/src/protocol.rs b/protocols/rendezvous/src/protocol.rs index 661340e4123..6b596a5464d 100644 --- a/protocols/rendezvous/src/protocol.rs +++ b/protocols/rendezvous/src/protocol.rs @@ -1,10 +1,11 @@ -use asynchronous_codec::{Framed}; +use crate::codec::RendezvousCodec; +use asynchronous_codec::Framed; +use futures::AsyncRead; +use futures::AsyncWrite; use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; +use log::debug; use std::{future, iter}; use void::Void; -use futures::AsyncRead; -use futures::AsyncWrite; -use crate::codec::RendezvousCodec; #[derive(Default, Debug, Copy, Clone)] pub struct Rendezvous; @@ -30,6 +31,7 @@ impl InboundUpgrade for Rendezvous { type Future = future::Ready>; fn upgrade_inbound(self, socket: TSocket, _: Self::Info) -> Self::Future { + debug!("upgrading inbound"); future::ready(Ok(Framed::new(socket, RendezvousCodec::default()))) } } @@ -40,6 +42,7 @@ impl OutboundUpgrade for Rendezvous { type Future = future::Ready>; fn upgrade_outbound(self, socket: TSocket, _: Self::Info) -> Self::Future { + debug!("upgrading outbound"); future::ready(Ok(Framed::new(socket, RendezvousCodec::default()))) } } diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index acd3b220385..ef17527ba76 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -9,10 +9,10 @@ use libp2p_mplex::MplexConfig; use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; use libp2p_swarm::{NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; use libp2p_yamux::YamuxConfig; +use log::debug; use std::fmt::Debug; use std::pin::Pin; use std::time::Duration; - /// An adaptor struct for libp2p that spawns futures into the current /// thread-local runtime. struct GlobalSpawnTokioExecutor; @@ -114,9 +114,11 @@ where event ); } - _ => {} + _ => { + debug!("{:?}", alice_event); + } } - match bob_event { + match &bob_event { SwarmEvent::ConnectionEstablished { .. } => { bob_connected = true; } @@ -126,7 +128,9 @@ where event ); } - _ => {} + _ => { + debug!("{:?}", bob_event); + } } } } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 761a29a1ae5..ee3e73ddb03 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,3 +1,4 @@ +use log::debug; pub mod harness; use crate::harness::{await_events_or_timeout, connect, new_swarm}; use libp2p_core::{Multiaddr, PeerId}; @@ -22,14 +23,14 @@ async fn given_successful_registration_then_successful_discovery() { test.assert_successful_registration(namespace.clone()).await; - println!("Registration worked!"); - - // discover - test.discovery_swarm - .behaviour_mut() - .discover(Some(namespace), test.rendezvous_peer_id); - - test.assert_successful_discovery().await; + // println!("Registration worked!"); + // + // // discover + // test.discovery_swarm + // .behaviour_mut() + // .discover(Some(namespace), test.rendezvous_peer_id); + // + // test.assert_successful_discovery().await; } struct RendezvousTest { @@ -54,6 +55,8 @@ fn get_rand_listen_addr() -> Multiaddr { impl RendezvousTest { pub async fn setup() -> Self { + let _ = env_logger::builder().is_test(true).try_init().unwrap(); + let registration_keys = Keypair::generate_ed25519(); let discovery_keys = Keypair::generate_ed25519(); let rendezvous_keys = Keypair::generate_ed25519(); @@ -100,6 +103,8 @@ impl RendezvousTest { registration_swarm_event, ) => { + debug!("{:?}", &rendezvous_swarm_event); + debug!("{:?}", ®istration_swarm_event); // TODO: Assertion against the actual peer record, pass the peer record in for assertion assert!(matches!(rendezvous_swarm_event, Event::PeerRegistered { .. })); From 6f923c2dc8388a92e562c78d81faa5cf25651cfe Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 3 Jun 2021 09:57:35 +1000 Subject: [PATCH 041/242] Fix bug where inbound state never went to closing --- protocols/rendezvous/src/handler.rs | 100 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index f3af2860cbe..3f4c2be393f 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -73,20 +73,6 @@ enum InboundState { Poisoned, } -impl Debug for InboundState { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - InboundState::None => f.write_str("none"), - InboundState::Reading(_) => f.write_str("reading"), - InboundState::PendingSend(_, _) => f.write_str("pending_send"), - InboundState::PendingFlush(_) => f.write_str("pending_flush"), - InboundState::WaitForBehaviour(_) => f.write_str("waiting_for_behaviour"), - InboundState::Closing(_) => f.write_str("closing"), - InboundState::Poisoned => f.write_str("poisoned"), - } - } -} - /// State of the outbound substream, opened either by us or by the remote. enum OutboundState { None, @@ -103,21 +89,6 @@ enum OutboundState { Poisoned, } -impl Debug for OutboundState { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - OutboundState::None => f.write_str("none"), - OutboundState::Start(_) => f.write_str("start"), - OutboundState::WaitingUpgrade => f.write_str("waiting_upgrade"), - OutboundState::PendingSend(_, _) => f.write_str("pending_send"), - OutboundState::PendingFlush(_) => f.write_str("pending_flush"), - OutboundState::WaitForRemote(_) => f.write_str("waiting_for_remote"), - OutboundState::Closing(_) => f.write_str("closing"), - OutboundState::Poisoned => f.write_str("poisoned"), - } - } -} - impl ProtocolsHandler for RendezvousHandler { type InEvent = Input; type OutEvent = HandlerEvent; @@ -254,6 +225,25 @@ impl ProtocolsHandler for RendezvousHandler { &self.outbound_substream ); match std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned) { + InboundState::None => self.outbound_substream = OutboundState::None, + InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(msg))) => { + self.inbound_substream = InboundState::WaitForBehaviour(substream); + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); + } + Poll::Ready(Some(Err(e))) => { + error!("Error when sending outbound: {:?}", e); + } + Poll::Ready(None) => { + error!("Honestly no idea what to do if this happens"); + } + Poll::Pending => { + self.inbound_substream = InboundState::Reading(substream); + } + }, + InboundState::WaitForBehaviour(substream) => { + self.inbound_substream = InboundState::WaitForBehaviour(substream); + } InboundState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { @@ -275,7 +265,7 @@ impl ProtocolsHandler for RendezvousHandler { } InboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { Poll::Ready(Ok(())) => { - self.inbound_substream = InboundState::None; + self.inbound_substream = InboundState::Closing(substream); } Poll::Ready(Err(e)) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), Poll::Pending => { @@ -283,24 +273,6 @@ impl ProtocolsHandler for RendezvousHandler { self.inbound_substream = InboundState::PendingFlush(substream); } }, - InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(msg))) => { - self.inbound_substream = InboundState::WaitForBehaviour(substream); - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); - } - Poll::Ready(Some(Err(e))) => { - error!("Error when sending outbound: {:?}", e); - } - Poll::Ready(None) => { - error!("Honestly no idea what to do if this happens"); - } - Poll::Pending => { - self.inbound_substream = InboundState::Reading(substream); - } - }, - InboundState::WaitForBehaviour(substream) => { - self.inbound_substream = InboundState::WaitForBehaviour(substream); - } InboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { Poll::Ready(..) => { if let OutboundState::None | OutboundState::Poisoned = self.outbound_substream { @@ -312,11 +284,11 @@ impl ProtocolsHandler for RendezvousHandler { self.inbound_substream = InboundState::Closing(substream); } }, - InboundState::None => self.outbound_substream = OutboundState::None, InboundState::Poisoned => self.outbound_substream = OutboundState::None, } match std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned) { + OutboundState::None => self.outbound_substream = OutboundState::None, OutboundState::Start(msg) => { self.outbound_substream = OutboundState::WaitingUpgrade; return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { @@ -386,7 +358,6 @@ impl ProtocolsHandler for RendezvousHandler { self.outbound_substream = OutboundState::Closing(substream); } }, - OutboundState::None => self.outbound_substream = OutboundState::None, OutboundState::Poisoned => { self.outbound_substream = { error!("outbound poisoned"); @@ -398,3 +369,32 @@ impl ProtocolsHandler for RendezvousHandler { Poll::Pending } } + +impl Debug for OutboundState { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + OutboundState::None => f.write_str("none"), + OutboundState::Start(_) => f.write_str("start"), + OutboundState::WaitingUpgrade => f.write_str("waiting_upgrade"), + OutboundState::PendingSend(_, _) => f.write_str("pending_send"), + OutboundState::PendingFlush(_) => f.write_str("pending_flush"), + OutboundState::WaitForRemote(_) => f.write_str("waiting_for_remote"), + OutboundState::Closing(_) => f.write_str("closing"), + OutboundState::Poisoned => f.write_str("poisoned"), + } + } +} + +impl Debug for InboundState { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + InboundState::None => f.write_str("none"), + InboundState::Reading(_) => f.write_str("reading"), + InboundState::PendingSend(_, _) => f.write_str("pending_send"), + InboundState::PendingFlush(_) => f.write_str("pending_flush"), + InboundState::WaitForBehaviour(_) => f.write_str("waiting_for_behaviour"), + InboundState::Closing(_) => f.write_str("closing"), + InboundState::Poisoned => f.write_str("poisoned"), + } + } +} From 6a4bbec8fe1ecf10cfe837ad42bd6e74791d2993 Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 3 Jun 2021 10:04:18 +1000 Subject: [PATCH 042/242] Remove duplicated code --- protocols/rendezvous/src/codec.rs | 32 ------------------------------- 1 file changed, 32 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index aff29d4a4c0..8faf3babaac 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -334,38 +334,6 @@ impl TryFrom for Message { } => Message::Unregister { namespace: ns.ok_or(ConversionError::MissingNamespace)?, }, - wire::Message { - r#type: Some(3), - discover: Some(Discover { ns, .. }), - .. - } => Message::Discover { namespace: ns }, - wire::Message { - r#type: Some(4), - discover_response: - Some(DiscoverResponse { - registrations, - status: Some(0), - .. - }), - .. - } => Message::DiscoverResponse { - registrations: registrations - .into_iter() - .map(|reggo| { - Ok(Registration { - namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)?, - record: AuthenticatedPeerRecord::from_signed_envelope( - SignedEnvelope::from_protobuf_encoding( - ®go - .signed_peer_record - .ok_or(ConversionError::MissingSignedPeerRecord)?, - )?, - )?, - ttl: reggo.ttl.ok_or(ConversionError::MissingTtl)?, - }) - }) - .collect::, ConversionError>>()?, - }, wire::Message { r#type: Some(4), discover_response: From e1c2d17f9b8e6008e5d0c707293923b2f4769ec0 Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 3 Jun 2021 10:04:56 +1000 Subject: [PATCH 043/242] Delete unused file --- protocols/rendezvous/src/registration.rs | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 protocols/rendezvous/src/registration.rs diff --git a/protocols/rendezvous/src/registration.rs b/protocols/rendezvous/src/registration.rs deleted file mode 100644 index 06ca7735767..00000000000 --- a/protocols/rendezvous/src/registration.rs +++ /dev/null @@ -1,12 +0,0 @@ -use libp2p_core::PeerId; -use std::collections::{HashMap, HashSet}; - -pub struct RegistrationStore { - registrations: HashMap>, -} - -impl RegistrationStore { - pub fn register(&self, registration: String, peer: PeerId) { - if let Some(peers) = self.registrations.get(®istration) {} - } -} From 7796a692bec2919b6ba50a9819a1a21cdab59967 Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 3 Jun 2021 12:02:05 +1000 Subject: [PATCH 044/242] Use tcp transport in tests --- protocols/rendezvous/Cargo.toml | 7 ++++--- protocols/rendezvous/src/behaviour.rs | 2 +- protocols/rendezvous/tests/harness/mod.rs | 22 ++++++++++------------ protocols/rendezvous/tests/rendezvous.rs | 4 ++-- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 38ed09e2ccf..927451e3af6 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -21,9 +21,10 @@ thiserror = "1" # TODO: REMOVE FOR PRODUCTION USE unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } [dev-dependencies] -libp2p-mplex = { version = "0.28", path = "../../muxers/mplex" } -libp2p-noise = { version = "0.31", path = "../../transports/noise" } -libp2p-yamux = { version = "0.32", path = "../../muxers/yamux" } +libp2p-tcp = { path = "../../transports/tcp" } +libp2p-noise = { path = "../../transports/noise" } +libp2p-yamux = { path = "../../muxers/yamux" } +libp2p-mplex = { path = "../../muxers/mplex" } rand = "0.8" tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } env_logger = "0.8" diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index a38b8e66cb5..ea43cda91ed 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -215,8 +215,8 @@ impl NetworkBehaviour for Rendezvous { Self::OutEvent, >, > { + debug!("polling behaviour events: {:?}", &self.events); if let Some(event) = self.events.pop_front() { - debug!("polling behaviour: {:?}", &event); return Poll::Ready(event); } diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index ef17527ba76..420b8d98dd7 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -3,16 +3,19 @@ use futures::Future; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; use libp2p_core::transport::MemoryTransport; -use libp2p_core::upgrade::SelectUpgrade; +use libp2p_core::upgrade::{EitherUpgrade, SelectUpgrade}; use libp2p_core::{identity, Executor, Multiaddr, PeerId, Transport}; use libp2p_mplex::MplexConfig; +use libp2p_noise as noise; use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; use libp2p_swarm::{NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; +use libp2p_tcp::TcpConfig; use libp2p_yamux::YamuxConfig; use log::debug; use std::fmt::Debug; use std::pin::Pin; use std::time::Duration; + /// An adaptor struct for libp2p that spawns futures into the current /// thread-local runtime. struct GlobalSpawnTokioExecutor; @@ -40,20 +43,15 @@ where { let peer_id = PeerId::from(id_keys.public()); - let dh_keys = Keypair::::new() + let noise_keys = noise::Keypair::::new() .into_authentic(&id_keys) - .expect("failed to create dh_keys"); - let noise = NoiseConfig::xx(dh_keys).into_authenticated(); + .unwrap(); - let transport = MemoryTransport::default() + let transport = TcpConfig::new() + .nodelay(true) .upgrade(Version::V1) - .authenticate(noise) - .multiplex(SelectUpgrade::new( - YamuxConfig::default(), - MplexConfig::new(), - )) - .timeout(Duration::from_secs(5)) - .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) + .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) + .multiplex(YamuxConfig::default()) .boxed(); let mut swarm: Swarm = SwarmBuilder::new(transport, behaviour_fn(peer_id, id_keys), peer_id) diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index ee3e73ddb03..f515ad7950a 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -45,8 +45,8 @@ struct RendezvousTest { } fn get_rand_listen_addr() -> Multiaddr { - let address_port = rand::random::(); - let addr = format!("/memory/{}", address_port) + let address_port = rand::random::(); + let addr = format!("/ip4/127.0.0.1/tcp/{}", address_port) .parse::() .unwrap(); From f2f89cf5c04829fcc4609b82658e73f374093f19 Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 3 Jun 2021 22:16:44 +1000 Subject: [PATCH 045/242] Fix bug where inbound substream was being poisoned --- protocols/rendezvous/src/handler.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 3f4c2be393f..d4e9e02cb4e 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -189,6 +189,8 @@ impl ProtocolsHandler for RendezvousHandler { _ => unreachable!("Handler in invalid state"), }; + debug!("inbound: {:?}", inbound_substream); + debug!("outbound: {:?}", outbound_substream); self.inbound_substream = inbound_substream; self.outbound_substream = outbound_substream; } @@ -225,7 +227,7 @@ impl ProtocolsHandler for RendezvousHandler { &self.outbound_substream ); match std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned) { - InboundState::None => self.outbound_substream = OutboundState::None, + InboundState::None => self.inbound_substream = InboundState::None, InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { self.inbound_substream = InboundState::WaitForBehaviour(substream); From dbd6745cc61b78b6d55b3c4ab932054a7a1fedaa Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 4 Jun 2021 10:03:31 +1000 Subject: [PATCH 046/242] Hack: Return poll ready to force swarm to be polled This hack fix makes the outbound state move past PendingSend. Emitting a Poll::Ready from the behaviour after sending the response to the register request seems to make the swarm get polled? The register swarm still is not emitting a SucesfullyRegisteredWithRendezvous node event. --- protocols/rendezvous/src/behaviour.rs | 8 +++--- protocols/rendezvous/src/codec.rs | 6 ++--- protocols/rendezvous/src/handler.rs | 37 +++++++++++++-------------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index ea43cda91ed..29359473848 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -139,10 +139,10 @@ impl NetworkBehaviour for Rendezvous { Message::Register(new_registration) => { let (namespace, ttl) = self.registrations.add(new_registration); - // emit behaviour event - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::PeerRegistered { peer_id, namespace }, - )); + // // emit behaviour event + // self.events.push_back(NetworkBehaviourAction::GenerateEvent( + // Event::PeerRegistered { peer_id, namespace }, + // )); // notify the handler that to send a response self.events diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 8faf3babaac..f2cb5fbec4e 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -3,7 +3,7 @@ use libp2p_core::{peer_record, signed_envelope, AuthenticatedPeerRecord, SignedE use std::convert::{TryFrom, TryInto}; use unsigned_varint::codec::UviBytes; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Message { Register(NewRegistration), RegisterResponse { @@ -30,7 +30,7 @@ pub enum Message { }, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct NewRegistration { pub namespace: String, pub record: AuthenticatedPeerRecord, @@ -63,7 +63,7 @@ pub struct Registration { pub ttl: i64, // TODO THEZ: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ErrorCode { InvalidNamespace, InvalidSignedPeerRecord, diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index d4e9e02cb4e..552319d8bec 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -234,10 +234,10 @@ impl ProtocolsHandler for RendezvousHandler { return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); } Poll::Ready(Some(Err(e))) => { - error!("Error when sending outbound: {:?}", e); + panic!("Error when sending outbound: {:?}", e); } Poll::Ready(None) => { - error!("Honestly no idea what to do if this happens"); + panic!("Honestly no idea what to do if this happens"); } Poll::Pending => { self.inbound_substream = InboundState::Reading(substream); @@ -248,16 +248,20 @@ impl ProtocolsHandler for RendezvousHandler { } InboundState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { + Poll::Ready(Ok(())) => match substream.start_send_unpin(message.clone()) { Ok(()) => { self.inbound_substream = InboundState::PendingFlush(substream); + // todo: remove this line/ figure out why i need to do this to keep it + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent( + message, + ))); } Err(e) => { - return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + panic!("pending send from inbound error: {:?}", e); } }, Poll::Ready(Err(e)) => { - return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + panic!("pending send from inbound error: {:?}", e); } Poll::Pending => { self.keep_alive = KeepAlive::Yes; @@ -269,7 +273,7 @@ impl ProtocolsHandler for RendezvousHandler { Poll::Ready(Ok(())) => { self.inbound_substream = InboundState::Closing(substream); } - Poll::Ready(Err(e)) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), + Poll::Ready(Err(e)) => panic!("pending send from inbound error: {:?}", e), Poll::Pending => { self.keep_alive = KeepAlive::Yes; self.inbound_substream = InboundState::PendingFlush(substream); @@ -286,7 +290,7 @@ impl ProtocolsHandler for RendezvousHandler { self.inbound_substream = InboundState::Closing(substream); } }, - InboundState::Poisoned => self.outbound_substream = OutboundState::None, + InboundState::Poisoned => panic!("inbound poisoned"), } match std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned) { @@ -307,12 +311,11 @@ impl ProtocolsHandler for RendezvousHandler { self.outbound_substream = OutboundState::PendingFlush(substream); } Err(e) => { - error!("Error when sending outbound: {:?}", e); - return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + panic!("Error when sending outbound: {:?}", e); } }, Poll::Ready(Err(e)) => { - return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + panic!("Error when sending outbound: {:?}", e); } Poll::Pending => { self.keep_alive = KeepAlive::Yes; @@ -325,8 +328,7 @@ impl ProtocolsHandler for RendezvousHandler { self.outbound_substream = OutboundState::WaitForRemote(substream) } Poll::Ready(Err(e)) => { - error!("Error when flushing outbound: {:?}", e); - return Poll::Ready(ProtocolsHandlerEvent::Close(e)); + panic!("Error when flushing outbound: {:?}", e); } Poll::Pending => { self.keep_alive = KeepAlive::Yes; @@ -339,13 +341,13 @@ impl ProtocolsHandler for RendezvousHandler { return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); } Poll::Ready(Some(Err(e))) => { - self.outbound_substream = OutboundState::Closing(substream); - error!("Error when receiving message from outbound: {:?}", e) + panic!("Error when receiving message from outbound: {:?}", e) } Poll::Ready(None) => { - error!("Honestly no idea what to do if this happens"); + panic!("Honestly no idea what to do if this happens"); } Poll::Pending => { + self.keep_alive = KeepAlive::Yes; self.outbound_substream = OutboundState::WaitForRemote(substream); } }, @@ -361,10 +363,7 @@ impl ProtocolsHandler for RendezvousHandler { } }, OutboundState::Poisoned => { - self.outbound_substream = { - error!("outbound poisoned"); - OutboundState::Poisoned - } + panic!("outbound poisoned"); } } From 7b22d1661573b26836eb8243c1fcbe070dc74f1f Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 4 Jun 2021 21:54:03 +1000 Subject: [PATCH 047/242] Delete unused code --- protocols/rendezvous/Cargo.toml | 2 +- protocols/rendezvous/src/behaviour.rs | 40 +---- protocols/rendezvous/tests/harness/mod.rs | 126 ++------------ protocols/rendezvous/tests/rendezvous.rs | 201 +++++++++------------- 4 files changed, 94 insertions(+), 275 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 927451e3af6..83254e0e2d3 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -26,7 +26,7 @@ libp2p-noise = { path = "../../transports/noise" } libp2p-yamux = { path = "../../muxers/yamux" } libp2p-mplex = { path = "../../muxers/mplex" } rand = "0.8" -tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } +async-std = "1.6.2" env_logger = "0.8" [build-dependencies] diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 29359473848..621f767c9fd 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -139,10 +139,10 @@ impl NetworkBehaviour for Rendezvous { Message::Register(new_registration) => { let (namespace, ttl) = self.registrations.add(new_registration); - // // emit behaviour event - // self.events.push_back(NetworkBehaviourAction::GenerateEvent( - // Event::PeerRegistered { peer_id, namespace }, - // )); + // emit behaviour event + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::PeerRegistered { peer_id, namespace }, + )); // notify the handler that to send a response self.events @@ -220,38 +220,6 @@ impl NetworkBehaviour for Rendezvous { return Poll::Ready(event); } - // if let Some(event) = self.events.pop_front() { - // return Poll::Ready(match event { - // NetworkBehaviourAction::NotifyHandler { - // peer_id, - // handler, - // event, - // } => { - // debug!("polling notifying handler: {:?}", &event); - // return Poll::Ready(NetworkBehaviourAction::NotifyHandler { - // peer_id, - // event, - // handler, - // }); - // } - // NetworkBehaviourAction::GenerateEvent(e) => { - // return Poll::Ready(NetworkBehaviourAction::GenerateEvent(e)); - // } - // NetworkBehaviourAction::DialAddress { address } => { - // return Poll::Ready(NetworkBehaviourAction::DialAddress { address }); - // } - // NetworkBehaviourAction::DialPeer { peer_id, condition } => { - // return Poll::Ready(NetworkBehaviourAction::DialPeer { peer_id, condition }); - // } - // NetworkBehaviourAction::ReportObservedAddr { address, score } => { - // return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { - // address, - // score, - // }) - // } - // }); - // } - Poll::Pending } } diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 420b8d98dd7..a759c3cc685 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -1,134 +1,28 @@ -use futures::future; -use futures::Future; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; -use libp2p_core::transport::MemoryTransport; -use libp2p_core::upgrade::{EitherUpgrade, SelectUpgrade}; -use libp2p_core::{identity, Executor, Multiaddr, PeerId, Transport}; -use libp2p_mplex::MplexConfig; +use libp2p_core::{identity, transport, Multiaddr, PeerId, Transport}; use libp2p_noise as noise; -use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; -use libp2p_swarm::{NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; use libp2p_tcp::TcpConfig; use libp2p_yamux::YamuxConfig; -use log::debug; -use std::fmt::Debug; -use std::pin::Pin; -use std::time::Duration; - -/// An adaptor struct for libp2p that spawns futures into the current -/// thread-local runtime. -struct GlobalSpawnTokioExecutor; - -impl Executor for GlobalSpawnTokioExecutor { - fn exec(&self, future: Pin + Send>>) { - let _ = tokio::spawn(future); - } -} - -#[allow(missing_debug_implementations)] -pub struct Actor { - pub swarm: Swarm, - pub addr: Multiaddr, - pub peer_id: PeerId, -} - -pub fn new_swarm B>( - behaviour_fn: F, - id_keys: identity::Keypair, - listen_address: Multiaddr, -) -> (Swarm, Multiaddr, PeerId) -where - B: NetworkBehaviour, -{ - let peer_id = PeerId::from(id_keys.public()); +pub fn mk_transport(id_keys: identity::Keypair) -> transport::Boxed<(PeerId, StreamMuxerBox)> { let noise_keys = noise::Keypair::::new() .into_authentic(&id_keys) .unwrap(); - let transport = TcpConfig::new() + TcpConfig::new() .nodelay(true) .upgrade(Version::V1) .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) .multiplex(YamuxConfig::default()) - .boxed(); - - let mut swarm: Swarm = SwarmBuilder::new(transport, behaviour_fn(peer_id, id_keys), peer_id) - .executor(Box::new(GlobalSpawnTokioExecutor)) - .build(); - - Swarm::listen_on(&mut swarm, listen_address.clone()).unwrap(); - - (swarm, listen_address, peer_id) -} - -pub async fn await_events_or_timeout( - swarm_1_event: impl Future, - swarm_2_event: impl Future, -) -> (A, B) { - tokio::time::timeout( - Duration::from_secs(10), - future::join(swarm_1_event, swarm_2_event), - ) - .await - .expect("network behaviours to emit an event within 10 seconds") + .boxed() } -/// Connects two swarms with each other. -/// -/// This assumes the transport that is in use can be used by Bob to connect to -/// the listen address that is emitted by Alice. In other words, they have to be -/// on the same network. The memory transport used by the above `new_swarm` -/// function fulfills this. -/// -/// We also assume that the swarms don't emit any behaviour events during the -/// connection phase. Any event emitted is considered a bug from this functions -/// PoV because they would be lost. -pub async fn connect(receiver: &mut Swarm, dialer: &mut Swarm) -where - BA: NetworkBehaviour, - BB: NetworkBehaviour, - ::OutEvent: Debug, - ::OutEvent: Debug, -{ - let mut alice_connected = false; - let mut bob_connected = false; - - while !alice_connected && !bob_connected { - let (alice_event, bob_event) = - future::join(receiver.next_event(), dialer.next_event()).await; +pub fn get_rand_listen_addr() -> Multiaddr { + let address_port = rand::random::(); + let addr = format!("/ip4/127.0.0.1/tcp/{}", address_port) + .parse::() + .unwrap(); - match alice_event { - SwarmEvent::ConnectionEstablished { .. } => { - alice_connected = true; - } - SwarmEvent::NewListenAddr(addr) => { - dialer.dial_addr(addr).unwrap(); - } - SwarmEvent::Behaviour(event) => { - panic!( - "alice unexpectedly emitted a behaviour event during connection: {:?}", - event - ); - } - _ => { - debug!("{:?}", alice_event); - } - } - match &bob_event { - SwarmEvent::ConnectionEstablished { .. } => { - bob_connected = true; - } - SwarmEvent::Behaviour(event) => { - panic!( - "bob unexpectedly emitted a behaviour event during connection: {:?}", - event - ); - } - _ => { - debug!("{:?}", bob_event); - } - } - } + addr } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index f515ad7950a..5184f09897d 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,132 +1,89 @@ +use futures::{channel::mpsc, prelude::*}; use log::debug; pub mod harness; -use crate::harness::{await_events_or_timeout, connect, new_swarm}; -use libp2p_core::{Multiaddr, PeerId}; -use libp2p_rendezvous::behaviour::{Event, Rendezvous}; -use libp2p_swarm::Swarm; -use std::time::Duration; - +use crate::harness::{get_rand_listen_addr, mk_transport}; +use futures::{SinkExt, StreamExt}; use libp2p_core::identity::Keypair; +use libp2p_core::Multiaddr; +use libp2p_rendezvous::behaviour::Rendezvous; +use libp2p_swarm::{Swarm, SwarmEvent}; +use std::time::Duration; -#[tokio::test] -async fn given_successful_registration_then_successful_discovery() { - let mut test = RendezvousTest::setup().await; - - let namespace = "some-namespace".to_string(); - - println!("registring"); - - // register - test.registration_swarm - .behaviour_mut() - .register(namespace.clone(), test.rendezvous_peer_id); - - test.assert_successful_registration(namespace.clone()).await; - - // println!("Registration worked!"); - // - // // discover - // test.discovery_swarm - // .behaviour_mut() - // .discover(Some(namespace), test.rendezvous_peer_id); - // - // test.assert_successful_discovery().await; -} - -struct RendezvousTest { - pub registration_swarm: Swarm, - pub registration_peer_id: PeerId, - - pub discovery_swarm: Swarm, - pub discovery_peer_id: PeerId, - - pub rendezvous_swarm: Swarm, - pub rendezvous_peer_id: PeerId, -} - -fn get_rand_listen_addr() -> Multiaddr { - let address_port = rand::random::(); - let addr = format!("/ip4/127.0.0.1/tcp/{}", address_port) - .parse::() - .unwrap(); - - addr -} - -impl RendezvousTest { - pub async fn setup() -> Self { - let _ = env_logger::builder().is_test(true).try_init().unwrap(); - - let registration_keys = Keypair::generate_ed25519(); - let discovery_keys = Keypair::generate_ed25519(); - let rendezvous_keys = Keypair::generate_ed25519(); - - let registration_addr = get_rand_listen_addr(); - let discovery_addr = get_rand_listen_addr(); - let rendezvous_addr = get_rand_listen_addr(); - - let (mut registration_swarm, _, registration_peer_id) = new_swarm( - |_, _| Rendezvous::new(registration_keys.clone(), vec![registration_addr.clone()]), - registration_keys.clone(), - registration_addr.clone(), - ); - - let (mut discovery_swarm, _, discovery_peer_id) = new_swarm( - |_, _| Rendezvous::new(discovery_keys.clone(), vec![discovery_addr.clone()]), - discovery_keys.clone(), - discovery_addr.clone(), - ); - - let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm( - |_, _| Rendezvous::new(rendezvous_keys.clone(), vec![rendezvous_addr.clone()]), - rendezvous_keys.clone(), - rendezvous_addr.clone(), - ); - - connect(&mut rendezvous_swarm, &mut discovery_swarm).await; - connect(&mut rendezvous_swarm, &mut registration_swarm).await; - - Self { - registration_swarm, - registration_peer_id, - discovery_swarm, - discovery_peer_id, - rendezvous_swarm, - rendezvous_peer_id, +#[test] +fn given_successful_registration_then_successful_discovery() { + let _ = env_logger::builder().is_test(true).try_init().unwrap(); + + let registration_keys = Keypair::generate_ed25519(); + let registration_peer_id = registration_keys.public().into_peer_id(); + + let rendezvous_keys = Keypair::generate_ed25519(); + let rendezvoud_peer_id = rendezvous_keys.public().into_peer_id(); + let rendezvous_addr = get_rand_listen_addr(); + + let mut registration_swarm = Swarm::new( + mk_transport(registration_keys.clone()), + Rendezvous::new(registration_keys, vec![]), + registration_peer_id, + ); + + let mut rendezvous_swarm = Swarm::new( + mk_transport(rendezvous_keys.clone()), + Rendezvous::new(rendezvous_keys, vec![rendezvous_addr.clone()]), + rendezvoud_peer_id, + ); + + let (mut tx, mut rx) = mpsc::channel::(1); + + rendezvous_swarm.listen_on(rendezvous_addr).unwrap(); + + let rendezvous_future = async move { + loop { + let next = rendezvous_swarm.next_event().await; + debug!("rendezvous swarm event: {:?}", next); + match next { + SwarmEvent::NewListenAddr(listener) => tx.send(listener).await.unwrap(), + SwarmEvent::ConnectionClosed { + peer_id, + endpoint, + num_established, + cause, + } => { + debug!("connection closed: {:?}", peer_id); + return; + } + _ => {} + } } - } - - pub async fn assert_successful_registration(&mut self, namespace: String) { - match await_events_or_timeout(self.rendezvous_swarm.next(), self.registration_swarm.next()).await { - ( - rendezvous_swarm_event, - registration_swarm_event, - ) => { - - debug!("{:?}", &rendezvous_swarm_event); - debug!("{:?}", ®istration_swarm_event); - // TODO: Assertion against the actual peer record, pass the peer record in for assertion - - assert!(matches!(rendezvous_swarm_event, Event::PeerRegistered { .. })); - assert!(matches!(registration_swarm_event, Event::RegisteredWithRendezvousNode { .. })); + }; + + let registration_future = async move { + registration_swarm + .dial_addr(rx.next().await.unwrap()) + .unwrap(); + loop { + let next = registration_swarm.next_event().await; + debug!("registration swarm event: {:?}", next); + match next { + SwarmEvent::Behaviour( + libp2p_rendezvous::behaviour::Event::RegisteredWithRendezvousNode { .. }, + ) => { + debug!("registered with rendezvous node !!!!"); + return; + } + SwarmEvent::ConnectionEstablished { peer_id, .. } => { + registration_swarm + .behaviour_mut() + .register("xmr-btc".to_string(), peer_id); + } + _ => {} } - (rendezvous_swarm_event, registration_swarm_event) => panic!( - "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", - rendezvous_swarm_event, registration_swarm_event - ), } - } + }; - pub async fn assert_successful_discovery(&mut self) { - // TODO: Is it by design that there is no event emitted on the rendezvous side for discovery? + let future = future::join(registration_future, rendezvous_future); - match tokio::time::timeout(Duration::from_secs(10), self.discovery_swarm.next()) - .await - .expect("") - { - // TODO: Assert against the actual registration - Event::Discovered { .. } => {} - event => panic!("Discovery swarm emitted unexpected event {:?}", event), - } - } + let dur = Duration::from_secs(5); + let timeout = async_std::future::timeout(dur, future); + + let (a, b) = async_std::task::block_on(timeout).unwrap(); } From a00b3f8f3e145e53a568c87b048db1a0deb795ff Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 4 Jun 2021 22:44:01 +1000 Subject: [PATCH 048/242] Replace emission of dummy event with ResponseSent The register test is now passing. It seems that emitting a wrong/dummy event was resulting in a corrupted state. After making a ResponseSent event the specifically for the purpose notifying the swarm and handler that a response has been sent on an inbound stream fixed the hanging test. --- protocols/rendezvous/src/behaviour.rs | 135 ++++++++++++----------- protocols/rendezvous/src/handler.rs | 34 +++++- protocols/rendezvous/tests/rendezvous.rs | 7 +- 3 files changed, 102 insertions(+), 74 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 621f767c9fd..72b379461cb 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -103,6 +103,7 @@ pub enum Event { peer_id: PeerId, namespace: String, }, + ResponseSent, } impl NetworkBehaviour for Rendezvous { @@ -135,73 +136,83 @@ impl NetworkBehaviour for Rendezvous { event: crate::handler::HandlerEvent, ) { debug!("behaviour::inject_event {:?}", &event); - match event.0 { - Message::Register(new_registration) => { - let (namespace, ttl) = self.registrations.add(new_registration); + match event { + crate::handler::HandlerEvent::Message(msg) => { + match msg { + Message::Register(new_registration) => { + let (namespace, ttl) = self.registrations.add(new_registration); - // emit behaviour event - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::PeerRegistered { peer_id, namespace }, - )); + // emit behaviour event + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::PeerRegistered { peer_id, namespace }, + )); - // notify the handler that to send a response - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::RegisterResponse { ttl }, - }); - } - Message::RegisterResponse { ttl } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::RegisteredWithRendezvousNode { - rendezvous_node: peer_id, - ttl, - }), - ), - Message::FailedToRegister { error } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::FailedToRegisterWithRendezvousNode { - rendezvous_node: peer_id, - err_code: error, - }), - ), - Message::Unregister { namespace } => { - self.registrations.remove(namespace, peer_id); - // TODO: Should send unregister response? - } - Message::Discover { namespace } => { - let registrations = self.registrations.get(namespace); - - if let Some(registrations) = registrations { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::DiscoverResponse { - discovered: registrations, + // notify the handler that to send a response + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::RegisterResponse { ttl }, + }); + } + Message::RegisterResponse { ttl } => { + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::RegisteredWithRendezvousNode { + rendezvous_node: peer_id, + ttl, }, - }) - } else { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::DiscoverResponse { discovered: vec![] }, - }) + )) + } + Message::FailedToRegister { error } => { + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::FailedToRegisterWithRendezvousNode { + rendezvous_node: peer_id, + err_code: error, + }, + )) + } + Message::Unregister { namespace } => { + self.registrations.remove(namespace, peer_id); + // TODO: Should send unregister response? + } + Message::Discover { namespace } => { + let registrations = self.registrations.get(namespace); + + if let Some(registrations) = registrations { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::DiscoverResponse { + discovered: registrations, + }, + }) + } else { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::DiscoverResponse { discovered: vec![] }, + }) + } + } + Message::DiscoverResponse { registrations } => self.events.push_back( + NetworkBehaviourAction::GenerateEvent(Event::Discovered { + rendezvous_node: peer_id, + registrations, + }), + ), + Message::FailedToDiscover { error } => self.events.push_back( + NetworkBehaviourAction::GenerateEvent(Event::FailedToDiscover { + rendezvous_node: peer_id, + err_code: error, + }), + ), } } - Message::DiscoverResponse { registrations } => { - self.events - .push_back(NetworkBehaviourAction::GenerateEvent(Event::Discovered { - rendezvous_node: peer_id, - registrations, - })) - } - Message::FailedToDiscover { error } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::FailedToDiscover { - rendezvous_node: peer_id, - err_code: error, - }), - ), + crate::handler::HandlerEvent::ResponseSent => self + .events + .push_back(NetworkBehaviourAction::GenerateEvent(Event::ResponseSent)), } } diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 552319d8bec..4ca19a9f3f1 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -34,7 +34,10 @@ impl RendezvousHandler { } #[derive(Debug)] -pub struct HandlerEvent(pub Message); +pub enum HandlerEvent { + Message(Message), + ResponseSent, +} #[derive(Debug)] pub enum Input { @@ -231,7 +234,16 @@ impl ProtocolsHandler for RendezvousHandler { InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { self.inbound_substream = InboundState::WaitForBehaviour(substream); - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); + if let Message::Register(..) + | Message::Discover { .. } + | Message::Unregister { .. } = msg + { + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent::Message( + msg, + ))); + } else { + panic!("Invalid inbound message"); + } } Poll::Ready(Some(Err(e))) => { panic!("Error when sending outbound: {:?}", e); @@ -252,9 +264,9 @@ impl ProtocolsHandler for RendezvousHandler { Ok(()) => { self.inbound_substream = InboundState::PendingFlush(substream); // todo: remove this line/ figure out why i need to do this to keep it - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent( - message, - ))); + return Poll::Ready(ProtocolsHandlerEvent::Custom( + HandlerEvent::ResponseSent, + )); } Err(e) => { panic!("pending send from inbound error: {:?}", e); @@ -338,7 +350,17 @@ impl ProtocolsHandler for RendezvousHandler { OutboundState::WaitForRemote(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { self.outbound_substream = OutboundState::Closing(substream); - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); + if let Message::DiscoverResponse { .. } + | Message::RegisterResponse { .. } + | Message::FailedToDiscover { .. } + | Message::FailedToRegister { .. } = msg + { + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent::Message( + msg, + ))); + } else { + panic!("Invalid inbound message"); + } } Poll::Ready(Some(Err(e))) => { panic!("Error when receiving message from outbound: {:?}", e) diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 5184f09897d..c9c296cf347 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -42,12 +42,7 @@ fn given_successful_registration_then_successful_discovery() { debug!("rendezvous swarm event: {:?}", next); match next { SwarmEvent::NewListenAddr(listener) => tx.send(listener).await.unwrap(), - SwarmEvent::ConnectionClosed { - peer_id, - endpoint, - num_established, - cause, - } => { + SwarmEvent::ConnectionClosed { peer_id, .. } => { debug!("connection closed: {:?}", peer_id); return; } From 69a00cf6ce2202a422bcd9c49125a57011ef7c6c Mon Sep 17 00:00:00 2001 From: rishflab Date: Mon, 7 Jun 2021 12:43:34 +1000 Subject: [PATCH 049/242] Reverted back to daniel/thomas test architecture Same old bug --- protocols/rendezvous/Cargo.toml | 1 + protocols/rendezvous/src/behaviour.rs | 11 +- protocols/rendezvous/src/handler.rs | 5 +- protocols/rendezvous/tests/harness/mod.rs | 140 ++++++++++++-- protocols/rendezvous/tests/rendezvous.rs | 212 ++++++++++++++-------- 5 files changed, 274 insertions(+), 95 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 83254e0e2d3..11c0aee1512 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -28,6 +28,7 @@ libp2p-mplex = { path = "../../muxers/mplex" } rand = "0.8" async-std = "1.6.2" env_logger = "0.8" +tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } [build-dependencies] prost-build = "0.7" diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 72b379461cb..4f242c8b63c 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -13,6 +13,7 @@ use std::task::{Context, Poll}; pub const DOMAIN: &str = "libp2p-rendezvous"; pub struct Rendezvous { + role: String, events: VecDeque>, registrations: Registrations, key_pair: Keypair, @@ -20,7 +21,7 @@ pub struct Rendezvous { } impl Rendezvous { - pub fn new(key_pair: Keypair, listen_addresses: Vec) -> Self { + pub fn new(key_pair: Keypair, listen_addresses: Vec, role: String) -> Self { let peer_record = PeerRecord { peer_id: key_pair.public().into_peer_id(), seq: 0, @@ -28,6 +29,7 @@ impl Rendezvous { }; Self { + role, events: Default::default(), registrations: Registrations::new(), key_pair, @@ -135,7 +137,7 @@ impl NetworkBehaviour for Rendezvous { _connection: ConnectionId, event: crate::handler::HandlerEvent, ) { - debug!("behaviour::inject_event {:?}", &event); + debug!("{}: behaviour::inject_event {:?}", &self.role, &event); match event { crate::handler::HandlerEvent::Message(msg) => { match msg { @@ -226,7 +228,10 @@ impl NetworkBehaviour for Rendezvous { Self::OutEvent, >, > { - debug!("polling behaviour events: {:?}", &self.events); + debug!( + " {}: polling behaviour events: {:?}", + &self.role, &self.events + ); if let Some(event) = self.events.pop_front() { return Poll::Ready(event); } diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 4ca19a9f3f1..b1f89d75d43 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -207,7 +207,8 @@ impl ProtocolsHandler for RendezvousHandler { } fn connection_keep_alive(&self) -> KeepAlive { - self.keep_alive + //todo: fix this + KeepAlive::Yes } fn poll( @@ -233,6 +234,7 @@ impl ProtocolsHandler for RendezvousHandler { InboundState::None => self.inbound_substream = InboundState::None, InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { + debug!("read message from inbound {:?}", msg); self.inbound_substream = InboundState::WaitForBehaviour(substream); if let Message::Register(..) | Message::Discover { .. } @@ -264,6 +266,7 @@ impl ProtocolsHandler for RendezvousHandler { Ok(()) => { self.inbound_substream = InboundState::PendingFlush(substream); // todo: remove this line/ figure out why i need to do this to keep it + // todo: move the flush code into here as start_send_unpin does not send/does not trigger IO/wake return Poll::Ready(ProtocolsHandlerEvent::Custom( HandlerEvent::ResponseSent, )); diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index a759c3cc685..4e4378bc9f5 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -1,28 +1,134 @@ +use futures::future; +use futures::Future; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; -use libp2p_core::{identity, transport, Multiaddr, PeerId, Transport}; -use libp2p_noise as noise; -use libp2p_tcp::TcpConfig; +use libp2p_core::transport::MemoryTransport; +use libp2p_core::upgrade::SelectUpgrade; +use libp2p_core::{identity, Executor, Multiaddr, PeerId, Transport}; +use libp2p_mplex::MplexConfig; +use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; +use libp2p_swarm::{ + IntoProtocolsHandler, NetworkBehaviour, ProtocolsHandler, Swarm, SwarmBuilder, SwarmEvent, +}; use libp2p_yamux::YamuxConfig; +use std::fmt::Debug; +use std::pin::Pin; +use std::time::Duration; -pub fn mk_transport(id_keys: identity::Keypair) -> transport::Boxed<(PeerId, StreamMuxerBox)> { - let noise_keys = noise::Keypair::::new() +/// An adaptor struct for libp2p that spawns futures into the current +/// thread-local runtime. +struct GlobalSpawnTokioExecutor; + +impl Executor for GlobalSpawnTokioExecutor { + fn exec(&self, future: Pin + Send>>) { + let _ = tokio::spawn(future); + } +} + +#[allow(missing_debug_implementations)] +pub struct Actor { + pub swarm: Swarm, + pub addr: Multiaddr, + pub peer_id: PeerId, +} + +pub fn new_swarm B>( + behaviour_fn: F, + id_keys: identity::Keypair, + listen_address: Multiaddr, +) -> (Swarm, Multiaddr, PeerId) +where + B: NetworkBehaviour, +{ + let peer_id = PeerId::from(id_keys.public()); + + let dh_keys = Keypair::::new() .into_authentic(&id_keys) - .unwrap(); + .expect("failed to create dh_keys"); + let noise = NoiseConfig::xx(dh_keys).into_authenticated(); - TcpConfig::new() - .nodelay(true) + let transport = MemoryTransport::default() .upgrade(Version::V1) - .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) - .multiplex(YamuxConfig::default()) - .boxed() + .authenticate(noise) + .multiplex(SelectUpgrade::new( + YamuxConfig::default(), + MplexConfig::new(), + )) + .timeout(Duration::from_secs(5)) + .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) + .boxed(); + + let mut swarm: Swarm = SwarmBuilder::new(transport, behaviour_fn(peer_id, id_keys), peer_id) + .executor(Box::new(GlobalSpawnTokioExecutor)) + .build(); + + Swarm::listen_on(&mut swarm, listen_address.clone()).unwrap(); + + (swarm, listen_address, peer_id) } -pub fn get_rand_listen_addr() -> Multiaddr { - let address_port = rand::random::(); - let addr = format!("/ip4/127.0.0.1/tcp/{}", address_port) - .parse::() - .unwrap(); +pub async fn await_events_or_timeout( + swarm_1_event: impl Future, + swarm_2_event: impl Future, +) -> (A, B) { + tokio::time::timeout( + Duration::from_secs(10), + future::join(swarm_1_event, swarm_2_event), + ) + .await + .expect("network behaviours to emit an event within 10 seconds") +} + +/// Connects two swarms with each other. +/// +/// This assumes the transport that is in use can be used by Bob to connect to +/// the listen address that is emitted by Alice. In other words, they have to be +/// on the same network. The memory transport used by the above `new_swarm` +/// function fulfills this. +/// +/// We also assume that the swarms don't emit any behaviour events during the +/// connection phase. Any event emitted is considered a bug from this functions +/// PoV because they would be lost. +pub async fn connect(receiver: &mut Swarm, dialer: &mut Swarm) +where + BA: NetworkBehaviour, + BB: NetworkBehaviour, + ::OutEvent: Debug, + ::OutEvent: Debug, +{ + let mut alice_connected = false; + let mut bob_connected = false; + + while !alice_connected && !bob_connected { + let (recv_event, dial_event) = + future::join(receiver.next_event(), dialer.next_event()).await; - addr + match recv_event { + SwarmEvent::ConnectionEstablished { .. } => { + alice_connected = true; + } + SwarmEvent::NewListenAddr(addr) => { + dialer.dial_addr(addr).unwrap(); + } + SwarmEvent::Behaviour(event) => { + panic!( + "alice unexpectedly emitted a behaviour event during connection: {:?}", + event + ); + } + _ => {} + } + match dial_event { + SwarmEvent::ConnectionEstablished { .. } => { + bob_connected = true; + } + SwarmEvent::Behaviour(event) => { + panic!( + "bob unexpectedly emitted a behaviour event during connection: {:?}", + event + ); + } + _ => {} + } + } } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index c9c296cf347..6c01b5f1756 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,84 +1,148 @@ -use futures::{channel::mpsc, prelude::*}; -use log::debug; pub mod harness; -use crate::harness::{get_rand_listen_addr, mk_transport}; -use futures::{SinkExt, StreamExt}; +use crate::harness::{await_events_or_timeout, connect, new_swarm}; use libp2p_core::identity::Keypair; -use libp2p_core::Multiaddr; -use libp2p_rendezvous::behaviour::Rendezvous; -use libp2p_swarm::{Swarm, SwarmEvent}; +use libp2p_core::network::Peer; +use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId}; +use libp2p_rendezvous::behaviour::{Event, Rendezvous}; +use libp2p_swarm::Swarm; +use libp2p_swarm::SwarmEvent; +use std::str::FromStr; use std::time::Duration; -#[test] -fn given_successful_registration_then_successful_discovery() { - let _ = env_logger::builder().is_test(true).try_init().unwrap(); - - let registration_keys = Keypair::generate_ed25519(); - let registration_peer_id = registration_keys.public().into_peer_id(); - - let rendezvous_keys = Keypair::generate_ed25519(); - let rendezvoud_peer_id = rendezvous_keys.public().into_peer_id(); - let rendezvous_addr = get_rand_listen_addr(); - - let mut registration_swarm = Swarm::new( - mk_transport(registration_keys.clone()), - Rendezvous::new(registration_keys, vec![]), - registration_peer_id, - ); - - let mut rendezvous_swarm = Swarm::new( - mk_transport(rendezvous_keys.clone()), - Rendezvous::new(rendezvous_keys, vec![rendezvous_addr.clone()]), - rendezvoud_peer_id, - ); - - let (mut tx, mut rx) = mpsc::channel::(1); - - rendezvous_swarm.listen_on(rendezvous_addr).unwrap(); - - let rendezvous_future = async move { - loop { - let next = rendezvous_swarm.next_event().await; - debug!("rendezvous swarm event: {:?}", next); - match next { - SwarmEvent::NewListenAddr(listener) => tx.send(listener).await.unwrap(), - SwarmEvent::ConnectionClosed { peer_id, .. } => { - debug!("connection closed: {:?}", peer_id); - return; - } - _ => {} - } +#[tokio::test] +async fn given_successful_registration_then_successful_discovery() { + env_logger::init(); + let mut test = RendezvousTest::setup().await; + + let namespace = "some-namespace".to_string(); + + println!("registring"); + + // register + test.registration_swarm + .behaviour_mut() + .register(namespace.clone(), test.rendezvous_peer_id); + + test.assert_successful_registration(namespace.clone()).await; + + println!("Registration worked!"); + + // // discover + // test.discovery_swarm + // .behaviour_mut() + // .discover(Some(namespace), test.rendezvous_peer_id); + // + // test.assert_successful_discovery().await; +} + +struct RendezvousTest { + pub registration_swarm: Swarm, + pub registration_peer_id: PeerId, + + pub discovery_swarm: Swarm, + pub discovery_peer_id: PeerId, + + pub rendezvous_swarm: Swarm, + pub rendezvous_peer_id: PeerId, +} + +fn get_rand_listen_addr() -> Multiaddr { + let address_port = rand::random::(); + let addr = format!("/memory/{}", address_port) + .parse::() + .unwrap(); + + addr +} + +impl RendezvousTest { + pub async fn setup() -> Self { + let registration_keys = Keypair::generate_ed25519(); + let discovery_keys = Keypair::generate_ed25519(); + let rendezvous_keys = Keypair::generate_ed25519(); + + let registration_addr = get_rand_listen_addr(); + let discovery_addr = get_rand_listen_addr(); + let rendezvous_addr = get_rand_listen_addr(); + + let (mut registration_swarm, _, registration_peer_id) = new_swarm( + |_, _| { + Rendezvous::new( + registration_keys.clone(), + vec![registration_addr.clone()], + "Registration".to_string(), + ) + }, + registration_keys.clone(), + registration_addr.clone(), + ); + + let (mut discovery_swarm, _, discovery_peer_id) = new_swarm( + |_, _| { + Rendezvous::new( + discovery_keys.clone(), + vec![discovery_addr.clone()], + "Discovery".to_string(), + ) + }, + discovery_keys.clone(), + discovery_addr.clone(), + ); + + let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm( + |_, _| { + Rendezvous::new( + rendezvous_keys.clone(), + vec![rendezvous_addr.clone()], + "Rendezvous".to_string(), + ) + }, + rendezvous_keys.clone(), + rendezvous_addr.clone(), + ); + + //connect(&mut rendezvous_swarm, &mut discovery_swarm).await; + connect(&mut rendezvous_swarm, &mut registration_swarm).await; + + Self { + registration_swarm, + registration_peer_id, + discovery_swarm, + discovery_peer_id, + rendezvous_swarm, + rendezvous_peer_id, } - }; - - let registration_future = async move { - registration_swarm - .dial_addr(rx.next().await.unwrap()) - .unwrap(); - loop { - let next = registration_swarm.next_event().await; - debug!("registration swarm event: {:?}", next); - match next { - SwarmEvent::Behaviour( - libp2p_rendezvous::behaviour::Event::RegisteredWithRendezvousNode { .. }, - ) => { - debug!("registered with rendezvous node !!!!"); - return; - } - SwarmEvent::ConnectionEstablished { peer_id, .. } => { - registration_swarm - .behaviour_mut() - .register("xmr-btc".to_string(), peer_id); - } - _ => {} + } + + pub async fn assert_successful_registration(&mut self, namespace: String) { + match await_events_or_timeout(self.rendezvous_swarm.next_event(), self.registration_swarm.next_event()).await { + ( + rendezvous_swarm_event, + registration_swarm_event, + ) => { + + // TODO: Assertion against the actual peer record, pass the peer record in for assertion + + assert!(matches!(rendezvous_swarm_event, SwarmEvent::Behaviour(Event::PeerRegistered { .. }))); + assert!(matches!(registration_swarm_event, SwarmEvent::Behaviour(Event::RegisteredWithRendezvousNode { .. }))); } + (rendezvous_swarm_event, registration_swarm_event) => panic!( + "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", + rendezvous_swarm_event, registration_swarm_event + ), } - }; - - let future = future::join(registration_future, rendezvous_future); + } - let dur = Duration::from_secs(5); - let timeout = async_std::future::timeout(dur, future); + pub async fn assert_successful_discovery(&mut self) { + // TODO: Is it by design that there is no event emitted on the rendezvous side for discovery? - let (a, b) = async_std::task::block_on(timeout).unwrap(); + match tokio::time::timeout(Duration::from_secs(10), self.discovery_swarm.next()) + .await + .expect("") + { + // TODO: Assert against the actual registration + Event::Discovered { .. } => {} + event => panic!("Discovery swarm emitted unexpected event {:?}", event), + } + } } From c0599c828c566e2d6ff106184c213fec300c08ec Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 7 Jun 2021 18:19:37 +1000 Subject: [PATCH 050/242] Make initial test pass For this we had to fix several problems: 1. In the connection setup, we needed to wait for _both_ swarms to emit `ConnectionEstablished`. 2. In the inject_event implementation, we needed to emit the `NotifyHandler` event first because otherwise, the swarm would stop getting polled because we already have an event. --- protocols/rendezvous/src/behaviour.rs | 10 +- protocols/rendezvous/src/codec.rs | 54 ++-- protocols/rendezvous/src/handler.rs | 366 ++++++++++++---------- protocols/rendezvous/src/rpc.proto | 18 +- protocols/rendezvous/tests/harness/mod.rs | 63 ++-- protocols/rendezvous/tests/rendezvous.rs | 11 +- 6 files changed, 287 insertions(+), 235 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 4f242c8b63c..ec4370dd5a7 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -144,11 +144,6 @@ impl NetworkBehaviour for Rendezvous { Message::Register(new_registration) => { let (namespace, ttl) = self.registrations.add(new_registration); - // emit behaviour event - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::PeerRegistered { peer_id, namespace }, - )); - // notify the handler that to send a response self.events .push_back(NetworkBehaviourAction::NotifyHandler { @@ -156,6 +151,11 @@ impl NetworkBehaviour for Rendezvous { handler: NotifyHandler::Any, event: Input::RegisterResponse { ttl }, }); + + // emit behaviour event + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::PeerRegistered { peer_id, namespace }, + )); } Message::RegisterResponse { ttl } => { self.events.push_back(NetworkBehaviourAction::GenerateEvent( diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index f2cb5fbec4e..78d0ec937a7 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -146,10 +146,10 @@ impl From for wire::Message { match message { Message::Register(NewRegistration { - namespace, - record, - ttl, - }) => wire::Message { + namespace, + record, + ttl, + }) => wire::Message { r#type: Some(MessageType::Register.into()), register: Some(Register { ns: Some(namespace), @@ -257,11 +257,11 @@ impl TryFrom for Message { wire::Message { r#type: Some(0), register: - Some(Register { - ns, - ttl, - signed_peer_record: Some(signed_peer_record), - }), + Some(Register { + ns, + ttl, + signed_peer_record: Some(signed_peer_record), + }), .. } => Message::Register(NewRegistration { namespace: ns.ok_or(ConversionError::MissingNamespace)?, @@ -273,11 +273,11 @@ impl TryFrom for Message { wire::Message { r#type: Some(1), register_response: - Some(RegisterResponse { - status: Some(0), - ttl, - .. - }), + Some(RegisterResponse { + status: Some(0), + ttl, + .. + }), .. } => Message::RegisterResponse { ttl: ttl.ok_or(ConversionError::MissingTtl)?, @@ -290,11 +290,11 @@ impl TryFrom for Message { wire::Message { r#type: Some(4), discover_response: - Some(DiscoverResponse { - registrations, - status: Some(0), - .. - }), + Some(DiscoverResponse { + registrations, + status: Some(0), + .. + }), .. } => Message::DiscoverResponse { registrations: registrations @@ -317,10 +317,10 @@ impl TryFrom for Message { wire::Message { r#type: Some(1), register_response: - Some(RegisterResponse { - status: Some(error_code), - .. - }), + Some(RegisterResponse { + status: Some(error_code), + .. + }), .. } => Message::FailedToRegister { error: wire::message::ResponseStatus::from_i32(error_code) @@ -337,10 +337,10 @@ impl TryFrom for Message { wire::Message { r#type: Some(4), discover_response: - Some(DiscoverResponse { - status: Some(error_code), - .. - }), + Some(DiscoverResponse { + status: Some(error_code), + .. + }), .. } => Message::FailedToDiscover { error: wire::message::ResponseStatus::from_i32(error_code) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index b1f89d75d43..20c4ef265bc 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -76,6 +76,101 @@ enum InboundState { Poisoned, } +impl InboundState { + fn poll(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + match std::mem::replace(self, InboundState::Poisoned) { + InboundState::None => { + *self = InboundState::None; + return Poll::Pending; + } + InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(msg))) => { + debug!("read message from inbound {:?}", msg); + + *self = InboundState::WaitForBehaviour(substream); + + if let Message::Register(..) + | Message::Discover { .. } + | Message::Unregister { .. } = msg + { + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent::Message( + msg, + ))); + } else { + panic!("Invalid inbound message"); + } + } + Poll::Ready(Some(Err(e))) => { + panic!("Error when sending outbound: {:?}", e); + } + Poll::Ready(None) => { + panic!("Honestly no idea what to do if this happens"); + } + Poll::Pending => { + *self = InboundState::Reading(substream); + return Poll::Pending; + } + }, + InboundState::WaitForBehaviour(substream) => { + *self = InboundState::WaitForBehaviour(substream); + return Poll::Pending; + } + InboundState::PendingSend(mut substream, message) => { + match substream.poll_ready_unpin(cx) { + Poll::Ready(Ok(())) => match substream.start_send_unpin(message.clone()) { + Ok(()) => { + *self = InboundState::PendingFlush(substream); + // todo: remove this line/ figure out why i need to do this to keep it + // todo: move the flush code into here as start_send_unpin does not send/does not trigger IO/wake + return Poll::Ready(ProtocolsHandlerEvent::Custom( + HandlerEvent::ResponseSent, + )); + } + Err(e) => { + panic!("pending send from inbound error: {:?}", e); + } + }, + Poll::Ready(Err(e)) => { + panic!("pending send from inbound error: {:?}", e); + } + Poll::Pending => { + *self = InboundState::PendingSend(substream, message); + return Poll::Pending; + } + } + } + InboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { + Poll::Ready(Ok(())) => { + *self = InboundState::Closing(substream); + } + Poll::Ready(Err(e)) => panic!("pending send from inbound error: {:?}", e), + Poll::Pending => { + *self = InboundState::PendingFlush(substream); + return Poll::Pending; + } + }, + InboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { + Poll::Ready(..) => { + *self = InboundState::None; + return Poll::Pending; + } + Poll::Pending => { + *self = InboundState::Closing(substream); + return Poll::Pending; + } + }, + InboundState::Poisoned => panic!("inbound poisoned"), + }; + } + } +} + /// State of the outbound substream, opened either by us or by the remote. enum OutboundState { None, @@ -92,6 +187,104 @@ enum OutboundState { Poisoned, } +impl OutboundState { + fn poll(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + match std::mem::replace(self, OutboundState::Poisoned) { + OutboundState::None => { + *self = OutboundState::None; + return Poll::Pending; + } + OutboundState::Start(msg) => { + *self = OutboundState::WaitingUpgrade; + return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(Rendezvous, msg), + }); + } + OutboundState::WaitingUpgrade => { + *self = OutboundState::WaitingUpgrade; + return Poll::Pending; + } + OutboundState::PendingSend(mut substream, message) => { + match substream.poll_ready_unpin(cx) { + Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { + Ok(()) => { + *self = OutboundState::PendingFlush(substream); + } + Err(e) => { + panic!("Error when sending outbound: {:?}", e); + } + }, + Poll::Ready(Err(e)) => { + panic!("Error when sending outbound: {:?}", e); + } + Poll::Pending => { + *self = OutboundState::PendingSend(substream, message); + return Poll::Pending; + } + } + } + OutboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { + Poll::Ready(Ok(())) => { + *self = OutboundState::WaitForRemote(substream) + } + Poll::Ready(Err(e)) => { + panic!("Error when flushing outbound: {:?}", e); + } + Poll::Pending => { + *self = OutboundState::PendingFlush(substream); + return Poll::Pending; + } + }, + OutboundState::WaitForRemote(mut substream) => match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(msg))) => { + *self = OutboundState::Closing(substream); + if let Message::DiscoverResponse { .. } + | Message::RegisterResponse { .. } + | Message::FailedToDiscover { .. } + | Message::FailedToRegister { .. } = msg + { + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent::Message( + msg, + ))); + } else { + panic!("Invalid inbound message"); + } + } + Poll::Ready(Some(Err(e))) => { + panic!("Error when receiving message from outbound: {:?}", e) + } + Poll::Ready(None) => { + panic!("Honestly no idea what to do if this happens"); + } + Poll::Pending => { + *self = OutboundState::WaitForRemote(substream); + return Poll::Pending; + } + }, + OutboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { + Poll::Ready(..) => { + *self = OutboundState::None; + return Poll::Pending; + } + Poll::Pending => { + *self = OutboundState::Closing(substream); + return Poll::Pending; + } + }, + OutboundState::Poisoned => { + panic!("outbound poisoned"); + } + } + } + } +} + impl ProtocolsHandler for RendezvousHandler { type InEvent = Input; type OutEvent = HandlerEvent; @@ -143,11 +336,11 @@ impl ProtocolsHandler for RendezvousHandler { ( Input::RegisterRequest { request: - NewRegistration { - namespace, - record, - ttl, - }, + NewRegistration { + namespace, + record, + ttl, + }, }, inbound, OutboundState::None, @@ -230,166 +423,13 @@ impl ProtocolsHandler for RendezvousHandler { "polling handler: outbound_state {:?}", &self.outbound_substream ); - match std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned) { - InboundState::None => self.inbound_substream = InboundState::None, - InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(msg))) => { - debug!("read message from inbound {:?}", msg); - self.inbound_substream = InboundState::WaitForBehaviour(substream); - if let Message::Register(..) - | Message::Discover { .. } - | Message::Unregister { .. } = msg - { - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent::Message( - msg, - ))); - } else { - panic!("Invalid inbound message"); - } - } - Poll::Ready(Some(Err(e))) => { - panic!("Error when sending outbound: {:?}", e); - } - Poll::Ready(None) => { - panic!("Honestly no idea what to do if this happens"); - } - Poll::Pending => { - self.inbound_substream = InboundState::Reading(substream); - } - }, - InboundState::WaitForBehaviour(substream) => { - self.inbound_substream = InboundState::WaitForBehaviour(substream); - } - InboundState::PendingSend(mut substream, message) => { - match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => match substream.start_send_unpin(message.clone()) { - Ok(()) => { - self.inbound_substream = InboundState::PendingFlush(substream); - // todo: remove this line/ figure out why i need to do this to keep it - // todo: move the flush code into here as start_send_unpin does not send/does not trigger IO/wake - return Poll::Ready(ProtocolsHandlerEvent::Custom( - HandlerEvent::ResponseSent, - )); - } - Err(e) => { - panic!("pending send from inbound error: {:?}", e); - } - }, - Poll::Ready(Err(e)) => { - panic!("pending send from inbound error: {:?}", e); - } - Poll::Pending => { - self.keep_alive = KeepAlive::Yes; - self.inbound_substream = InboundState::PendingSend(substream, message); - } - } - } - InboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => { - self.inbound_substream = InboundState::Closing(substream); - } - Poll::Ready(Err(e)) => panic!("pending send from inbound error: {:?}", e), - Poll::Pending => { - self.keep_alive = KeepAlive::Yes; - self.inbound_substream = InboundState::PendingFlush(substream); - } - }, - InboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => { - if let OutboundState::None | OutboundState::Poisoned = self.outbound_substream { - self.keep_alive = KeepAlive::No; - } - self.inbound_substream = InboundState::None; - } - Poll::Pending => { - self.inbound_substream = InboundState::Closing(substream); - } - }, - InboundState::Poisoned => panic!("inbound poisoned"), + + if let Poll::Ready(event) = self.inbound_substream.poll(cx) { + return Poll::Ready(event); } - match std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned) { - OutboundState::None => self.outbound_substream = OutboundState::None, - OutboundState::Start(msg) => { - self.outbound_substream = OutboundState::WaitingUpgrade; - return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(Rendezvous, msg), - }); - } - OutboundState::WaitingUpgrade => { - self.outbound_substream = OutboundState::WaitingUpgrade; - } - OutboundState::PendingSend(mut substream, message) => { - match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { - Ok(()) => { - self.outbound_substream = OutboundState::PendingFlush(substream); - } - Err(e) => { - panic!("Error when sending outbound: {:?}", e); - } - }, - Poll::Ready(Err(e)) => { - panic!("Error when sending outbound: {:?}", e); - } - Poll::Pending => { - self.keep_alive = KeepAlive::Yes; - self.outbound_substream = OutboundState::PendingSend(substream, message); - } - } - } - OutboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => { - self.outbound_substream = OutboundState::WaitForRemote(substream) - } - Poll::Ready(Err(e)) => { - panic!("Error when flushing outbound: {:?}", e); - } - Poll::Pending => { - self.keep_alive = KeepAlive::Yes; - self.outbound_substream = OutboundState::PendingFlush(substream); - } - }, - OutboundState::WaitForRemote(mut substream) => match substream.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(msg))) => { - self.outbound_substream = OutboundState::Closing(substream); - if let Message::DiscoverResponse { .. } - | Message::RegisterResponse { .. } - | Message::FailedToDiscover { .. } - | Message::FailedToRegister { .. } = msg - { - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent::Message( - msg, - ))); - } else { - panic!("Invalid inbound message"); - } - } - Poll::Ready(Some(Err(e))) => { - panic!("Error when receiving message from outbound: {:?}", e) - } - Poll::Ready(None) => { - panic!("Honestly no idea what to do if this happens"); - } - Poll::Pending => { - self.keep_alive = KeepAlive::Yes; - self.outbound_substream = OutboundState::WaitForRemote(substream); - } - }, - OutboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => { - if let InboundState::None | InboundState::Poisoned = self.inbound_substream { - self.keep_alive = KeepAlive::No; - } - self.outbound_substream = OutboundState::None; - } - Poll::Pending => { - self.outbound_substream = OutboundState::Closing(substream); - } - }, - OutboundState::Poisoned => { - panic!("outbound poisoned"); - } + if let Poll::Ready(event) = self.outbound_substream.poll(cx) { + return Poll::Ready(event); } Poll::Pending diff --git a/protocols/rendezvous/src/rpc.proto b/protocols/rendezvous/src/rpc.proto index 1123d8a42b6..f3cbd9505ff 100644 --- a/protocols/rendezvous/src/rpc.proto +++ b/protocols/rendezvous/src/rpc.proto @@ -12,14 +12,14 @@ message Message { } enum ResponseStatus { - OK = 0; - E_INVALID_NAMESPACE = 100; - E_INVALID_SIGNED_PEER_RECORD = 101; - E_INVALID_TTL = 102; - E_INVALID_COOKIE = 103; - E_NOT_AUTHORIZED = 200; - E_INTERNAL_ERROR = 300; - E_UNAVAILABLE = 400; + OK = 0; + E_INVALID_NAMESPACE = 100; + E_INVALID_SIGNED_PEER_RECORD = 101; + E_INVALID_TTL = 102; + E_INVALID_COOKIE = 103; + E_NOT_AUTHORIZED = 200; + E_INTERNAL_ERROR = 300; + E_UNAVAILABLE = 400; } message Register { @@ -58,4 +58,4 @@ message Message { optional Unregister unregister = 4; optional Discover discover = 5; optional DiscoverResponse discoverResponse = 6; -} \ No newline at end of file +} diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 4e4378bc9f5..2451548d8d1 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -96,39 +96,42 @@ where ::OutEvent: Debug, ::OutEvent: Debug, { - let mut alice_connected = false; - let mut bob_connected = false; - - while !alice_connected && !bob_connected { - let (recv_event, dial_event) = - future::join(receiver.next_event(), dialer.next_event()).await; + loop { + if let SwarmEvent::NewListenAddr(addr) = receiver.next_event().await { + dialer.dial_addr(addr).unwrap(); + break; + } + } - match recv_event { - SwarmEvent::ConnectionEstablished { .. } => { - alice_connected = true; - } - SwarmEvent::NewListenAddr(addr) => { - dialer.dial_addr(addr).unwrap(); + future::join(async move { + loop { + match receiver.next_event().await { + SwarmEvent::ConnectionEstablished { .. } => { + break; + } + SwarmEvent::Behaviour(event) => { + panic!( + "receiver unexpectedly emitted a behaviour event during connection: {:?}", + event + ); + } + _ => {} } - SwarmEvent::Behaviour(event) => { - panic!( - "alice unexpectedly emitted a behaviour event during connection: {:?}", - event - ); - } - _ => {} } - match dial_event { - SwarmEvent::ConnectionEstablished { .. } => { - bob_connected = true; - } - SwarmEvent::Behaviour(event) => { - panic!( - "bob unexpectedly emitted a behaviour event during connection: {:?}", - event - ); + }, async move { + loop { + match dialer.next_event().await { + SwarmEvent::ConnectionEstablished { .. } => { + break; + } + SwarmEvent::Behaviour(event) => { + panic!( + "dialer unexpectedly emitted a behaviour event during connection: {:?}", + event + ); + } + _ => {} } - _ => {} } - } + }).await; } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 6c01b5f1756..b1fb6ff89cd 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -115,7 +115,16 @@ impl RendezvousTest { } pub async fn assert_successful_registration(&mut self, namespace: String) { - match await_events_or_timeout(self.rendezvous_swarm.next_event(), self.registration_swarm.next_event()).await { + let rendezvous_swarm = &mut self.rendezvous_swarm; + let reggo_swarm = &mut self.registration_swarm; + + match await_events_or_timeout(async { + let event = rendezvous_swarm.next_event().await; + dbg!(event) + }, async { + let event = reggo_swarm.next_event().await; + dbg!(event) + }).await { ( rendezvous_swarm_event, registration_swarm_event, From 57d471dbd9da32edc865a71ad29c804e79623c35 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 7 Jun 2021 18:22:40 +1000 Subject: [PATCH 051/242] Add TODO --- protocols/rendezvous/src/handler.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 20c4ef265bc..daf18d28b78 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -77,6 +77,7 @@ enum InboundState { } impl InboundState { + // TODO: See if we can refactor this such that we don't need to assign `self` in so many branches fn poll(&mut self, cx: &mut Context<'_>) -> Poll Date: Mon, 7 Jun 2021 22:15:14 +1000 Subject: [PATCH 052/242] Remove hack fix to trigger Swarm::poll() The ResponseSent dummy message hack is no longer required after the changes introduced in c0599c82 --- protocols/rendezvous/src/behaviour.rs | 135 ++++++++++++-------------- protocols/rendezvous/src/handler.rs | 119 +++++++++++------------ 2 files changed, 119 insertions(+), 135 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index ec4370dd5a7..b75b4afbe51 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -105,7 +105,6 @@ pub enum Event { peer_id: PeerId, namespace: String, }, - ResponseSent, } impl NetworkBehaviour for Rendezvous { @@ -138,83 +137,73 @@ impl NetworkBehaviour for Rendezvous { event: crate::handler::HandlerEvent, ) { debug!("{}: behaviour::inject_event {:?}", &self.role, &event); - match event { - crate::handler::HandlerEvent::Message(msg) => { - match msg { - Message::Register(new_registration) => { - let (namespace, ttl) = self.registrations.add(new_registration); + match event.0 { + Message::Register(new_registration) => { + let (namespace, ttl) = self.registrations.add(new_registration); - // notify the handler that to send a response - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::RegisterResponse { ttl }, - }); + // notify the handler that to send a response + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::RegisterResponse { ttl }, + }); - // emit behaviour event - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::PeerRegistered { peer_id, namespace }, - )); - } - Message::RegisterResponse { ttl } => { - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::RegisteredWithRendezvousNode { - rendezvous_node: peer_id, - ttl, - }, - )) - } - Message::FailedToRegister { error } => { - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::FailedToRegisterWithRendezvousNode { - rendezvous_node: peer_id, - err_code: error, - }, - )) - } - Message::Unregister { namespace } => { - self.registrations.remove(namespace, peer_id); - // TODO: Should send unregister response? - } - Message::Discover { namespace } => { - let registrations = self.registrations.get(namespace); + // emit behaviour event + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::PeerRegistered { peer_id, namespace }, + )); + } + Message::RegisterResponse { ttl } => self.events.push_back( + NetworkBehaviourAction::GenerateEvent(Event::RegisteredWithRendezvousNode { + rendezvous_node: peer_id, + ttl, + }), + ), + Message::FailedToRegister { error } => self.events.push_back( + NetworkBehaviourAction::GenerateEvent(Event::FailedToRegisterWithRendezvousNode { + rendezvous_node: peer_id, + err_code: error, + }), + ), + Message::Unregister { namespace } => { + self.registrations.remove(namespace, peer_id); + // TODO: Should send unregister response? + } + Message::Discover { namespace } => { + let registrations = self.registrations.get(namespace); - if let Some(registrations) = registrations { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::DiscoverResponse { - discovered: registrations, - }, - }) - } else { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::DiscoverResponse { discovered: vec![] }, - }) - } - } - Message::DiscoverResponse { registrations } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::Discovered { - rendezvous_node: peer_id, - registrations, - }), - ), - Message::FailedToDiscover { error } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::FailedToDiscover { - rendezvous_node: peer_id, - err_code: error, - }), - ), + if let Some(registrations) = registrations { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::DiscoverResponse { + discovered: registrations, + }, + }) + } else { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::DiscoverResponse { discovered: vec![] }, + }) } } - crate::handler::HandlerEvent::ResponseSent => self - .events - .push_back(NetworkBehaviourAction::GenerateEvent(Event::ResponseSent)), + Message::DiscoverResponse { registrations } => { + self.events + .push_back(NetworkBehaviourAction::GenerateEvent(Event::Discovered { + rendezvous_node: peer_id, + registrations, + })) + } + Message::FailedToDiscover { error } => self.events.push_back( + NetworkBehaviourAction::GenerateEvent(Event::FailedToDiscover { + rendezvous_node: peer_id, + err_code: error, + }), + ), } } diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index daf18d28b78..3215d7aec81 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -34,10 +34,7 @@ impl RendezvousHandler { } #[derive(Debug)] -pub enum HandlerEvent { - Message(Message), - ResponseSent, -} +pub struct HandlerEvent(pub Message); #[derive(Debug)] pub enum Input { @@ -78,12 +75,11 @@ enum InboundState { impl InboundState { // TODO: See if we can refactor this such that we don't need to assign `self` in so many branches - fn poll(&mut self, cx: &mut Context<'_>) -> Poll> { + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll> + { loop { match std::mem::replace(self, InboundState::Poisoned) { InboundState::None => { @@ -100,9 +96,7 @@ impl InboundState { | Message::Discover { .. } | Message::Unregister { .. } = msg { - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent::Message( - msg, - ))); + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); } else { panic!("Invalid inbound message"); } @@ -129,9 +123,9 @@ impl InboundState { *self = InboundState::PendingFlush(substream); // todo: remove this line/ figure out why i need to do this to keep it // todo: move the flush code into here as start_send_unpin does not send/does not trigger IO/wake - return Poll::Ready(ProtocolsHandlerEvent::Custom( - HandlerEvent::ResponseSent, - )); + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent( + message, + ))); } Err(e) => { panic!("pending send from inbound error: {:?}", e); @@ -189,12 +183,11 @@ enum OutboundState { } impl OutboundState { - fn poll(&mut self, cx: &mut Context<'_>) -> Poll> { + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll> + { loop { match std::mem::replace(self, OutboundState::Poisoned) { OutboundState::None => { @@ -230,44 +223,46 @@ impl OutboundState { } } } - OutboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => { - *self = OutboundState::WaitForRemote(substream) - } - Poll::Ready(Err(e)) => { - panic!("Error when flushing outbound: {:?}", e); - } - Poll::Pending => { - *self = OutboundState::PendingFlush(substream); - return Poll::Pending; - } - }, - OutboundState::WaitForRemote(mut substream) => match substream.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(msg))) => { - *self = OutboundState::Closing(substream); - if let Message::DiscoverResponse { .. } - | Message::RegisterResponse { .. } - | Message::FailedToDiscover { .. } - | Message::FailedToRegister { .. } = msg - { - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent::Message( - msg, - ))); - } else { - panic!("Invalid inbound message"); + OutboundState::PendingFlush(mut substream) => { + match substream.poll_flush_unpin(cx) { + Poll::Ready(Ok(())) => *self = OutboundState::WaitForRemote(substream), + Poll::Ready(Err(e)) => { + panic!("Error when flushing outbound: {:?}", e); + } + Poll::Pending => { + *self = OutboundState::PendingFlush(substream); + return Poll::Pending; } } - Poll::Ready(Some(Err(e))) => { - panic!("Error when receiving message from outbound: {:?}", e) - } - Poll::Ready(None) => { - panic!("Honestly no idea what to do if this happens"); - } - Poll::Pending => { - *self = OutboundState::WaitForRemote(substream); - return Poll::Pending; + } + OutboundState::WaitForRemote(mut substream) => { + match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(msg))) => { + *self = OutboundState::Closing(substream); + if let Message::DiscoverResponse { .. } + | Message::RegisterResponse { .. } + | Message::FailedToDiscover { .. } + | Message::FailedToRegister { .. } = msg + { + return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent( + msg, + ))); + } else { + panic!("Invalid inbound message"); + } + } + Poll::Ready(Some(Err(e))) => { + panic!("Error when receiving message from outbound: {:?}", e) + } + Poll::Ready(None) => { + panic!("Honestly no idea what to do if this happens"); + } + Poll::Pending => { + *self = OutboundState::WaitForRemote(substream); + return Poll::Pending; + } } - }, + } OutboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { Poll::Ready(..) => { *self = OutboundState::None; @@ -337,11 +332,11 @@ impl ProtocolsHandler for RendezvousHandler { ( Input::RegisterRequest { request: - NewRegistration { - namespace, - record, - ttl, - }, + NewRegistration { + namespace, + record, + ttl, + }, }, inbound, OutboundState::None, From 1f5251a1258c8bc05ade050b2f0406c9bffbe58f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 10:40:39 +1000 Subject: [PATCH 053/242] Reduce code duplication by generating identity inside `new_swarm` --- protocols/rendezvous/tests/harness/mod.rs | 65 ++++++++++++----------- protocols/rendezvous/tests/rendezvous.rs | 20 +++---- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 2451548d8d1..9cdcdda9d0e 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -34,16 +34,16 @@ pub struct Actor { pub fn new_swarm B>( behaviour_fn: F, - id_keys: identity::Keypair, listen_address: Multiaddr, ) -> (Swarm, Multiaddr, PeerId) where B: NetworkBehaviour, { - let peer_id = PeerId::from(id_keys.public()); + let identity = identity::Keypair::generate_ed25519(); + let peer_id = PeerId::from(identity.public()); let dh_keys = Keypair::::new() - .into_authentic(&id_keys) + .into_authentic(&identity) .expect("failed to create dh_keys"); let noise = NoiseConfig::xx(dh_keys).into_authenticated(); @@ -58,9 +58,10 @@ where .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let mut swarm: Swarm = SwarmBuilder::new(transport, behaviour_fn(peer_id, id_keys), peer_id) - .executor(Box::new(GlobalSpawnTokioExecutor)) - .build(); + let mut swarm: Swarm = + SwarmBuilder::new(transport, behaviour_fn(peer_id, identity), peer_id) + .executor(Box::new(GlobalSpawnTokioExecutor)) + .build(); Swarm::listen_on(&mut swarm, listen_address.clone()).unwrap(); @@ -103,35 +104,39 @@ where } } - future::join(async move { - loop { - match receiver.next_event().await { - SwarmEvent::ConnectionEstablished { .. } => { - break; - } - SwarmEvent::Behaviour(event) => { - panic!( + future::join( + async move { + loop { + match receiver.next_event().await { + SwarmEvent::ConnectionEstablished { .. } => { + break; + } + SwarmEvent::Behaviour(event) => { + panic!( "receiver unexpectedly emitted a behaviour event during connection: {:?}", event ); + } + _ => {} } - _ => {} } - } - }, async move { - loop { - match dialer.next_event().await { - SwarmEvent::ConnectionEstablished { .. } => { - break; + }, + async move { + loop { + match dialer.next_event().await { + SwarmEvent::ConnectionEstablished { .. } => { + break; + } + SwarmEvent::Behaviour(event) => { + panic!( + "dialer unexpectedly emitted a behaviour event during connection: {:?}", + event + ); + } + _ => {} } - SwarmEvent::Behaviour(event) => { - panic!( - "dialer unexpectedly emitted a behaviour event during connection: {:?}", - event - ); - } - _ => {} } - } - }).await; + }, + ) + .await; } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index b1fb6ff89cd..f8b782e1885 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,4 +1,5 @@ pub mod harness; + use crate::harness::{await_events_or_timeout, connect, new_swarm}; use libp2p_core::identity::Keypair; use libp2p_core::network::Peer; @@ -57,47 +58,40 @@ fn get_rand_listen_addr() -> Multiaddr { impl RendezvousTest { pub async fn setup() -> Self { - let registration_keys = Keypair::generate_ed25519(); - let discovery_keys = Keypair::generate_ed25519(); - let rendezvous_keys = Keypair::generate_ed25519(); - let registration_addr = get_rand_listen_addr(); let discovery_addr = get_rand_listen_addr(); let rendezvous_addr = get_rand_listen_addr(); let (mut registration_swarm, _, registration_peer_id) = new_swarm( - |_, _| { + |_, identity| { Rendezvous::new( - registration_keys.clone(), + identity, vec![registration_addr.clone()], "Registration".to_string(), ) }, - registration_keys.clone(), registration_addr.clone(), ); let (mut discovery_swarm, _, discovery_peer_id) = new_swarm( - |_, _| { + |_, identity| { Rendezvous::new( - discovery_keys.clone(), + identity, vec![discovery_addr.clone()], "Discovery".to_string(), ) }, - discovery_keys.clone(), discovery_addr.clone(), ); let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm( - |_, _| { + |_, identity| { Rendezvous::new( - rendezvous_keys.clone(), + identity, vec![rendezvous_addr.clone()], "Rendezvous".to_string(), ) }, - rendezvous_keys.clone(), rendezvous_addr.clone(), ); From 94b5dbe332cb423e37c136937fc82570ac7d46e2 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 10:49:44 +1000 Subject: [PATCH 054/242] Format with rustfmt --- protocols/rendezvous/build.rs | 3 +- protocols/rendezvous/src/codec.rs | 54 +++++++++++++++---------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/protocols/rendezvous/build.rs b/protocols/rendezvous/build.rs index 2c596bd8249..fa982fa3d90 100644 --- a/protocols/rendezvous/build.rs +++ b/protocols/rendezvous/build.rs @@ -1,4 +1,3 @@ fn main() { - prost_build::compile_protos(&["src/rpc.proto"], &["src"]).unwrap(); + prost_build::compile_protos(&["src/rpc.proto"], &["src"]).unwrap(); } - diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 78d0ec937a7..f2cb5fbec4e 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -146,10 +146,10 @@ impl From for wire::Message { match message { Message::Register(NewRegistration { - namespace, - record, - ttl, - }) => wire::Message { + namespace, + record, + ttl, + }) => wire::Message { r#type: Some(MessageType::Register.into()), register: Some(Register { ns: Some(namespace), @@ -257,11 +257,11 @@ impl TryFrom for Message { wire::Message { r#type: Some(0), register: - Some(Register { - ns, - ttl, - signed_peer_record: Some(signed_peer_record), - }), + Some(Register { + ns, + ttl, + signed_peer_record: Some(signed_peer_record), + }), .. } => Message::Register(NewRegistration { namespace: ns.ok_or(ConversionError::MissingNamespace)?, @@ -273,11 +273,11 @@ impl TryFrom for Message { wire::Message { r#type: Some(1), register_response: - Some(RegisterResponse { - status: Some(0), - ttl, - .. - }), + Some(RegisterResponse { + status: Some(0), + ttl, + .. + }), .. } => Message::RegisterResponse { ttl: ttl.ok_or(ConversionError::MissingTtl)?, @@ -290,11 +290,11 @@ impl TryFrom for Message { wire::Message { r#type: Some(4), discover_response: - Some(DiscoverResponse { - registrations, - status: Some(0), - .. - }), + Some(DiscoverResponse { + registrations, + status: Some(0), + .. + }), .. } => Message::DiscoverResponse { registrations: registrations @@ -317,10 +317,10 @@ impl TryFrom for Message { wire::Message { r#type: Some(1), register_response: - Some(RegisterResponse { - status: Some(error_code), - .. - }), + Some(RegisterResponse { + status: Some(error_code), + .. + }), .. } => Message::FailedToRegister { error: wire::message::ResponseStatus::from_i32(error_code) @@ -337,10 +337,10 @@ impl TryFrom for Message { wire::Message { r#type: Some(4), discover_response: - Some(DiscoverResponse { - status: Some(error_code), - .. - }), + Some(DiscoverResponse { + status: Some(error_code), + .. + }), .. } => Message::FailedToDiscover { error: wire::message::ResponseStatus::from_i32(error_code) From 10ee7e5d0c5a364a8fb8d8a41541ac18b1a8d197 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 10:50:02 +1000 Subject: [PATCH 055/242] Infer addresses to register from external addresses To actually register an address now, we need to tell the Swarm about our external address. --- protocols/rendezvous/src/behaviour.rs | 29 ++++++++++++++---------- protocols/rendezvous/tests/rendezvous.rs | 29 ++++++------------------ 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index b75b4afbe51..4082e2f63d1 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -17,29 +17,30 @@ pub struct Rendezvous { events: VecDeque>, registrations: Registrations, key_pair: Keypair, - peer_record: PeerRecord, + external_addresses: Vec, } impl Rendezvous { - pub fn new(key_pair: Keypair, listen_addresses: Vec, role: String) -> Self { - let peer_record = PeerRecord { - peer_id: key_pair.public().into_peer_id(), - seq: 0, - addresses: listen_addresses, - }; - + pub fn new(key_pair: Keypair, role: String) -> Self { Self { role, events: Default::default(), registrations: Registrations::new(), key_pair, - peer_record, + external_addresses: vec![], } } + // TODO: Make it possible to filter for specific external-addresses (like onion addresses-only f.e.) pub fn register(&mut self, namespace: String, rendezvous_node: PeerId) { - let authenticated_peer_record = - AuthenticatedPeerRecord::from_record(self.key_pair.clone(), self.peer_record.clone()); + let authenticated_peer_record = AuthenticatedPeerRecord::from_record( + self.key_pair.clone(), + PeerRecord { + peer_id: self.key_pair.public().into_peer_id(), + seq: 0, // TODO: should be current unix timestamp + addresses: self.external_addresses.clone(), + }, + ); self.events .push_back(NetworkBehaviourAction::NotifyHandler { @@ -210,13 +211,17 @@ impl NetworkBehaviour for Rendezvous { fn poll( &mut self, _cx: &mut Context<'_>, - _: &mut impl PollParameters, + poll_params: &mut impl PollParameters, ) -> Poll< NetworkBehaviourAction< ::InEvent, Self::OutEvent, >, > { + // 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. + self.external_addresses = poll_params.external_addresses().map(|r| r.addr).collect(); + debug!( " {}: polling behaviour events: {:?}", &self.role, &self.events diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index f8b782e1885..e97a4c62413 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -5,8 +5,8 @@ use libp2p_core::identity::Keypair; use libp2p_core::network::Peer; use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId}; use libp2p_rendezvous::behaviour::{Event, Rendezvous}; -use libp2p_swarm::Swarm; use libp2p_swarm::SwarmEvent; +use libp2p_swarm::{AddressScore, Swarm}; use std::str::FromStr; use std::time::Duration; @@ -63,37 +63,22 @@ impl RendezvousTest { let rendezvous_addr = get_rand_listen_addr(); let (mut registration_swarm, _, registration_peer_id) = new_swarm( - |_, identity| { - Rendezvous::new( - identity, - vec![registration_addr.clone()], - "Registration".to_string(), - ) - }, + |_, identity| Rendezvous::new(identity, "Registration".to_string()), registration_addr.clone(), ); + registration_swarm.add_external_address(registration_addr, AddressScore::Infinite); let (mut discovery_swarm, _, discovery_peer_id) = new_swarm( - |_, identity| { - Rendezvous::new( - identity, - vec![discovery_addr.clone()], - "Discovery".to_string(), - ) - }, + |_, identity| Rendezvous::new(identity, "Discovery".to_string()), discovery_addr.clone(), ); + discovery_swarm.add_external_address(discovery_addr, AddressScore::Infinite); let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm( - |_, identity| { - Rendezvous::new( - identity, - vec![rendezvous_addr.clone()], - "Rendezvous".to_string(), - ) - }, + |_, identity| Rendezvous::new(identity, "Rendezvous".to_string()), rendezvous_addr.clone(), ); + rendezvous_swarm.add_external_address(rendezvous_addr, AddressScore::Infinite); //connect(&mut rendezvous_swarm, &mut discovery_swarm).await; connect(&mut rendezvous_swarm, &mut registration_swarm).await; From 26d1ac61465dd9af5f31a439a60241449491e382 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 10:54:30 +1000 Subject: [PATCH 056/242] Grab PeerId from Swarm when necessary instead of passing it around --- protocols/rendezvous/tests/harness/mod.rs | 4 ++-- protocols/rendezvous/tests/rendezvous.rs | 21 +++++++-------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 9cdcdda9d0e..49989ca32fe 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -35,7 +35,7 @@ pub struct Actor { pub fn new_swarm B>( behaviour_fn: F, listen_address: Multiaddr, -) -> (Swarm, Multiaddr, PeerId) +) -> (Swarm, Multiaddr) where B: NetworkBehaviour, { @@ -65,7 +65,7 @@ where Swarm::listen_on(&mut swarm, listen_address.clone()).unwrap(); - (swarm, listen_address, peer_id) + (swarm, listen_address) } pub async fn await_events_or_timeout( diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index e97a4c62413..585d8ecd2f3 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -20,9 +20,10 @@ async fn given_successful_registration_then_successful_discovery() { println!("registring"); // register - test.registration_swarm - .behaviour_mut() - .register(namespace.clone(), test.rendezvous_peer_id); + test.registration_swarm.behaviour_mut().register( + namespace.clone(), + test.rendezvous_swarm.local_peer_id().clone(), + ); test.assert_successful_registration(namespace.clone()).await; @@ -38,13 +39,8 @@ async fn given_successful_registration_then_successful_discovery() { struct RendezvousTest { pub registration_swarm: Swarm, - pub registration_peer_id: PeerId, - pub discovery_swarm: Swarm, - pub discovery_peer_id: PeerId, - pub rendezvous_swarm: Swarm, - pub rendezvous_peer_id: PeerId, } fn get_rand_listen_addr() -> Multiaddr { @@ -62,19 +58,19 @@ impl RendezvousTest { let discovery_addr = get_rand_listen_addr(); let rendezvous_addr = get_rand_listen_addr(); - let (mut registration_swarm, _, registration_peer_id) = new_swarm( + let (mut registration_swarm, _) = new_swarm( |_, identity| Rendezvous::new(identity, "Registration".to_string()), registration_addr.clone(), ); registration_swarm.add_external_address(registration_addr, AddressScore::Infinite); - let (mut discovery_swarm, _, discovery_peer_id) = new_swarm( + let (mut discovery_swarm, _) = new_swarm( |_, identity| Rendezvous::new(identity, "Discovery".to_string()), discovery_addr.clone(), ); discovery_swarm.add_external_address(discovery_addr, AddressScore::Infinite); - let (mut rendezvous_swarm, _, rendezvous_peer_id) = new_swarm( + let (mut rendezvous_swarm, _) = new_swarm( |_, identity| Rendezvous::new(identity, "Rendezvous".to_string()), rendezvous_addr.clone(), ); @@ -85,11 +81,8 @@ impl RendezvousTest { Self { registration_swarm, - registration_peer_id, discovery_swarm, - discovery_peer_id, rendezvous_swarm, - rendezvous_peer_id, } } From 32b05a1816f2a32c8e1f018f039ddd73c3dbc89c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 10:56:32 +1000 Subject: [PATCH 057/242] Remove dead code --- protocols/rendezvous/tests/harness/mod.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 49989ca32fe..342f01a6739 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -25,13 +25,6 @@ impl Executor for GlobalSpawnTokioExecutor { } } -#[allow(missing_debug_implementations)] -pub struct Actor { - pub swarm: Swarm, - pub addr: Multiaddr, - pub peer_id: PeerId, -} - pub fn new_swarm B>( behaviour_fn: F, listen_address: Multiaddr, From 5fc85835eafa5d0fc5a16d34c6dc72f6b9a4cfcf Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 12:10:11 +1000 Subject: [PATCH 058/242] Replace buggy `connect` functionality with composable extension trait --- protocols/rendezvous/Cargo.toml | 1 + protocols/rendezvous/tests/harness/mod.rs | 159 ++++++++++++++-------- protocols/rendezvous/tests/rendezvous.rs | 53 +++----- 3 files changed, 120 insertions(+), 93 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 11c0aee1512..a1c29dca961 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -29,6 +29,7 @@ rand = "0.8" async-std = "1.6.2" env_logger = "0.8" tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } +async-trait = "0.1" [build-dependencies] prost-build = "0.7" diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 342f01a6739..396dcc8eea7 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -1,3 +1,4 @@ +use async_trait::async_trait; use futures::future; use futures::Future; use libp2p_core::muxing::StreamMuxerBox; @@ -8,7 +9,8 @@ use libp2p_core::{identity, Executor, Multiaddr, PeerId, Transport}; use libp2p_mplex::MplexConfig; use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; use libp2p_swarm::{ - IntoProtocolsHandler, NetworkBehaviour, ProtocolsHandler, Swarm, SwarmBuilder, SwarmEvent, + AddressScore, ExpandedSwarm, IntoProtocolsHandler, NetworkBehaviour, ProtocolsHandler, Swarm, + SwarmBuilder, SwarmEvent, }; use libp2p_yamux::YamuxConfig; use std::fmt::Debug; @@ -27,10 +29,10 @@ impl Executor for GlobalSpawnTokioExecutor { pub fn new_swarm B>( behaviour_fn: F, - listen_address: Multiaddr, -) -> (Swarm, Multiaddr) +) -> Swarm where B: NetworkBehaviour, + ::OutEvent: Debug, { let identity = identity::Keypair::generate_ed25519(); let peer_id = PeerId::from(identity.public()); @@ -51,14 +53,18 @@ where .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let mut swarm: Swarm = - SwarmBuilder::new(transport, behaviour_fn(peer_id, identity), peer_id) - .executor(Box::new(GlobalSpawnTokioExecutor)) - .build(); + SwarmBuilder::new(transport, behaviour_fn(peer_id, identity), peer_id) + .executor(Box::new(GlobalSpawnTokioExecutor)) + .build() +} - Swarm::listen_on(&mut swarm, listen_address.clone()).unwrap(); +fn get_rand_memory_address() -> Multiaddr { + let address_port = rand::random::(); + let addr = format!("/memory/{}", address_port) + .parse::() + .unwrap(); - (swarm, listen_address) + addr } pub async fn await_events_or_timeout( @@ -73,63 +79,98 @@ pub async fn await_events_or_timeout( .expect("network behaviours to emit an event within 10 seconds") } -/// Connects two swarms with each other. -/// -/// This assumes the transport that is in use can be used by Bob to connect to -/// the listen address that is emitted by Alice. In other words, they have to be -/// on the same network. The memory transport used by the above `new_swarm` -/// function fulfills this. -/// -/// We also assume that the swarms don't emit any behaviour events during the -/// connection phase. Any event emitted is considered a bug from this functions -/// PoV because they would be lost. -pub async fn connect(receiver: &mut Swarm, dialer: &mut Swarm) +/// An extension trait for [`Swarm`] that makes it easier to set up a network of [`Swarm`]s for tests. +#[async_trait] +pub trait SwarmExt { + /// Establishes a connection to the given [`Swarm`], polling both of them until the connection is established. + async fn block_on_connection(&mut self, other: &mut Swarm) + where + T: NetworkBehaviour, + ::OutEvent: Debug; + + /// Listens on a random memory address, polling the [`Swarm`] until the transport is ready to accept connections. + async fn listen_on_random_memory_address(&mut self) -> Multiaddr; +} + +#[async_trait] +impl SwarmExt for Swarm where - BA: NetworkBehaviour, - BB: NetworkBehaviour, - ::OutEvent: Debug, - ::OutEvent: Debug, + B: NetworkBehaviour, + ::OutEvent: Debug, { - loop { - if let SwarmEvent::NewListenAddr(addr) = receiver.next_event().await { - dialer.dial_addr(addr).unwrap(); - break; - } - } + async fn block_on_connection(&mut self, other: &mut Swarm) + where + T: NetworkBehaviour, + ::OutEvent: Debug, + { + let addr_to_dial = other.external_addresses().next().unwrap().addr.clone(); - future::join( - async move { - loop { - match receiver.next_event().await { - SwarmEvent::ConnectionEstablished { .. } => { - break; + self.dial_addr(addr_to_dial.clone()).unwrap(); + + let mut dialer_done = false; + let mut listener_done = false; + + loop { + let dialer_event_fut = self.next_event(); + + tokio::select! { + dialer_event = dialer_event_fut => { + match dialer_event { + SwarmEvent::ConnectionEstablished { .. } => { + dialer_done = true; + } + SwarmEvent::UnknownPeerUnreachableAddr { address, error } if address == addr_to_dial => { + panic!("Failed to dial address {}: {}", addr_to_dial, error) + } + other => { + log::debug!("Ignoring {:?}", other); + } } - SwarmEvent::Behaviour(event) => { - panic!( - "receiver unexpectedly emitted a behaviour event during connection: {:?}", - event - ); + }, + listener_event = other.next_event() => { + match listener_event { + SwarmEvent::ConnectionEstablished { .. } => { + listener_done = true; + } + SwarmEvent::IncomingConnectionError { error, .. } => { + panic!("Failure in incoming connection {}", error); + } + other => { + log::debug!("Ignoring {:?}", other); + } } - _ => {} } } - }, - async move { - loop { - match dialer.next_event().await { - SwarmEvent::ConnectionEstablished { .. } => { - break; - } - SwarmEvent::Behaviour(event) => { - panic!( - "dialer unexpectedly emitted a behaviour event during connection: {:?}", - event - ); - } - _ => {} + + if dialer_done && listener_done { + return; + } + } + } + + async fn listen_on_random_memory_address(&mut self) -> Multiaddr { + let multiaddr = get_rand_memory_address(); + + self.listen_on(multiaddr.clone()).unwrap(); + + // block until we are actually listening + loop { + match self.next_event().await { + SwarmEvent::NewListenAddr(addr) if addr == multiaddr => { + break; + } + other => { + log::debug!( + "Ignoring {:?} while waiting for listening to succeed", + other + ); } } - }, - ) - .await; + } + + // Memory addresses are externally reachable because they all share the same memory-space. + self.add_external_address(multiaddr.clone(), AddressScore::Infinite); + + multiaddr + } } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 585d8ecd2f3..c69fcda891b 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,6 +1,6 @@ pub mod harness; -use crate::harness::{await_events_or_timeout, connect, new_swarm}; +use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use libp2p_core::identity::Keypair; use libp2p_core::network::Peer; use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId}; @@ -43,41 +43,26 @@ struct RendezvousTest { pub rendezvous_swarm: Swarm, } -fn get_rand_listen_addr() -> Multiaddr { - let address_port = rand::random::(); - let addr = format!("/memory/{}", address_port) - .parse::() - .unwrap(); - - addr -} - impl RendezvousTest { pub async fn setup() -> Self { - let registration_addr = get_rand_listen_addr(); - let discovery_addr = get_rand_listen_addr(); - let rendezvous_addr = get_rand_listen_addr(); - - let (mut registration_swarm, _) = new_swarm( - |_, identity| Rendezvous::new(identity, "Registration".to_string()), - registration_addr.clone(), - ); - registration_swarm.add_external_address(registration_addr, AddressScore::Infinite); - - let (mut discovery_swarm, _) = new_swarm( - |_, identity| Rendezvous::new(identity, "Discovery".to_string()), - discovery_addr.clone(), - ); - discovery_swarm.add_external_address(discovery_addr, AddressScore::Infinite); - - let (mut rendezvous_swarm, _) = new_swarm( - |_, identity| Rendezvous::new(identity, "Rendezvous".to_string()), - rendezvous_addr.clone(), - ); - rendezvous_swarm.add_external_address(rendezvous_addr, AddressScore::Infinite); - - //connect(&mut rendezvous_swarm, &mut discovery_swarm).await; - connect(&mut rendezvous_swarm, &mut registration_swarm).await; + let mut registration_swarm = + new_swarm(|_, identity| Rendezvous::new(identity, "Registration".to_string())); + registration_swarm.listen_on_random_memory_address().await; + + let mut discovery_swarm = + new_swarm(|_, identity| Rendezvous::new(identity, "Discovery".to_string())); + discovery_swarm.listen_on_random_memory_address().await; + + let mut rendezvous_swarm = + new_swarm(|_, identity| Rendezvous::new(identity, "Rendezvous".to_string())); + rendezvous_swarm.listen_on_random_memory_address().await; + + registration_swarm + .block_on_connection(&mut rendezvous_swarm) + .await; + discovery_swarm + .block_on_connection(&mut rendezvous_swarm) + .await; Self { registration_swarm, From 24a0c0db0b4a1afe93199d29aa202d6c96077cf6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 12:16:03 +1000 Subject: [PATCH 059/242] Use unwrap_or_default to avoid branching --- protocols/rendezvous/src/behaviour.rs | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 4082e2f63d1..f22c29bf78a 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -172,25 +172,16 @@ impl NetworkBehaviour for Rendezvous { // TODO: Should send unregister response? } Message::Discover { namespace } => { - let registrations = self.registrations.get(namespace); + let registrations = self.registrations.get(namespace).unwrap_or_default(); - if let Some(registrations) = registrations { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::DiscoverResponse { - discovered: registrations, - }, - }) - } else { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: Input::DiscoverResponse { discovered: vec![] }, - }) - } + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: Input::DiscoverResponse { + discovered: registrations, + }, + }) } Message::DiscoverResponse { registrations } => { self.events From dec5a43ee63e2073c66324d35064cf7af29c88c3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 12:33:47 +1000 Subject: [PATCH 060/242] Log events as we get them --- protocols/rendezvous/tests/harness/mod.rs | 25 +++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 396dcc8eea7..f162ff87594 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -1,6 +1,8 @@ use async_trait::async_trait; use futures::future; +use futures::future::Either; use futures::Future; +use futures::FutureExt; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; use libp2p_core::transport::MemoryTransport; @@ -70,10 +72,29 @@ fn get_rand_memory_address() -> Multiaddr { pub async fn await_events_or_timeout( swarm_1_event: impl Future, swarm_2_event: impl Future, -) -> (A, B) { +) -> (A, B) +where + A: Debug, + B: Debug, +{ tokio::time::timeout( Duration::from_secs(10), - future::join(swarm_1_event, swarm_2_event), + future::join( + async { + let e1 = swarm_1_event.await; + + log::debug!("Got event1: {:?}", e1); + + e1 + }, + async { + let e2 = swarm_2_event.await; + + log::debug!("Got event2: {:?}", e2); + + e2 + }, + ), ) .await .expect("network behaviours to emit an event within 10 seconds") From 0c3bbaabc5bb1ef77d74b9fcc868f6b934a4eb1b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 12:35:27 +1000 Subject: [PATCH 061/242] Simplify assertion of successful registration --- protocols/rendezvous/tests/rendezvous.rs | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index c69fcda891b..b48378dbfa0 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -72,25 +72,11 @@ impl RendezvousTest { } pub async fn assert_successful_registration(&mut self, namespace: String) { - let rendezvous_swarm = &mut self.rendezvous_swarm; - let reggo_swarm = &mut self.registration_swarm; - - match await_events_or_timeout(async { - let event = rendezvous_swarm.next_event().await; - dbg!(event) - }, async { - let event = reggo_swarm.next_event().await; - dbg!(event) - }).await { + match await_events_or_timeout(self.rendezvous_swarm.next(), self.registration_swarm.next()).await { ( - rendezvous_swarm_event, - registration_swarm_event, + Event::PeerRegistered { .. }, + Event::RegisteredWithRendezvousNode { .. }, ) => { - - // TODO: Assertion against the actual peer record, pass the peer record in for assertion - - assert!(matches!(rendezvous_swarm_event, SwarmEvent::Behaviour(Event::PeerRegistered { .. }))); - assert!(matches!(registration_swarm_event, SwarmEvent::Behaviour(Event::RegisteredWithRendezvousNode { .. }))); } (rendezvous_swarm_event, registration_swarm_event) => panic!( "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", From 5c9c83e97a5e66e0e2f3046ddd96c7e069086490 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 14:24:59 +1000 Subject: [PATCH 062/242] Deal with warnings --- protocols/rendezvous/tests/harness/mod.rs | 7 +------ protocols/rendezvous/tests/rendezvous.rs | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index f162ff87594..b887fad260a 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -1,8 +1,6 @@ use async_trait::async_trait; use futures::future; -use futures::future::Either; use futures::Future; -use futures::FutureExt; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; use libp2p_core::transport::MemoryTransport; @@ -10,10 +8,7 @@ use libp2p_core::upgrade::SelectUpgrade; use libp2p_core::{identity, Executor, Multiaddr, PeerId, Transport}; use libp2p_mplex::MplexConfig; use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; -use libp2p_swarm::{ - AddressScore, ExpandedSwarm, IntoProtocolsHandler, NetworkBehaviour, ProtocolsHandler, Swarm, - SwarmBuilder, SwarmEvent, -}; +use libp2p_swarm::{AddressScore, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; use libp2p_yamux::YamuxConfig; use std::fmt::Debug; use std::pin::Pin; diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index b48378dbfa0..9e23a8ee837 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,13 +1,8 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; -use libp2p_core::identity::Keypair; -use libp2p_core::network::Peer; -use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId}; use libp2p_rendezvous::behaviour::{Event, Rendezvous}; -use libp2p_swarm::SwarmEvent; -use libp2p_swarm::{AddressScore, Swarm}; -use std::str::FromStr; +use libp2p_swarm::Swarm; use std::time::Duration; #[tokio::test] From b766951d34584f6c4f1fe330a21136cb81dff7df Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 14:45:55 +1000 Subject: [PATCH 063/242] Avoid emitting events twice / erroneously --- protocols/rendezvous/src/handler.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 3215d7aec81..7ab0c773a0f 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -121,11 +121,6 @@ impl InboundState { Poll::Ready(Ok(())) => match substream.start_send_unpin(message.clone()) { Ok(()) => { *self = InboundState::PendingFlush(substream); - // todo: remove this line/ figure out why i need to do this to keep it - // todo: move the flush code into here as start_send_unpin does not send/does not trigger IO/wake - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent( - message, - ))); } Err(e) => { panic!("pending send from inbound error: {:?}", e); From a89b28d4848d3a56bc32cf1882511619c8123e5b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 14:52:31 +1000 Subject: [PATCH 064/242] Make discover test working --- protocols/rendezvous/src/behaviour.rs | 23 +++++++++++---- protocols/rendezvous/src/codec.rs | 6 ++-- protocols/rendezvous/src/handler.rs | 2 +- protocols/rendezvous/tests/rendezvous.rs | 36 +++++++++--------------- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index f22c29bf78a..5a61b48630a 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -80,6 +80,10 @@ pub enum Event { rendezvous_node: PeerId, registrations: Vec, }, + AnsweredDiscoverRequest { + enquirer: PeerId, + registrations: Vec, + }, FailedToDiscover { rendezvous_node: PeerId, err_code: ErrorCode, @@ -99,11 +103,11 @@ pub enum Event { // TODO: get the namespace in as well, needs association between the registration request and the response }, PeerRegistered { - peer_id: PeerId, + peer: PeerId, namespace: String, }, PeerUnregistered { - peer_id: PeerId, + peer: PeerId, namespace: String, }, } @@ -152,7 +156,10 @@ impl NetworkBehaviour for Rendezvous { // emit behaviour event self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::PeerRegistered { peer_id, namespace }, + Event::PeerRegistered { + peer: peer_id, + namespace, + }, )); } Message::RegisterResponse { ttl } => self.events.push_back( @@ -179,9 +186,15 @@ impl NetworkBehaviour for Rendezvous { peer_id, handler: NotifyHandler::Any, event: Input::DiscoverResponse { - discovered: registrations, + discovered: registrations.clone(), }, - }) + }); + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::AnsweredDiscoverRequest { + enquirer: peer_id, + registrations, + }, + )); } Message::DiscoverResponse { registrations } => { self.events diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index f2cb5fbec4e..b1c547ce1eb 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -217,8 +217,10 @@ impl From for wire::Message { .into_iter() .map(|reggo| Register { ns: Some(reggo.namespace), - ttl: None, - signed_peer_record: None, + ttl: Some(reggo.ttl), + signed_peer_record: Some( + reggo.record.into_signed_envelope().into_protobuf_encoding(), + ), }) .collect(), status: Some(ResponseStatus::Ok.into()), diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 7ab0c773a0f..4afadc43e97 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -118,7 +118,7 @@ impl InboundState { } InboundState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => match substream.start_send_unpin(message.clone()) { + Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { Ok(()) => { *self = InboundState::PendingFlush(substream); } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 9e23a8ee837..b165227970b 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -3,7 +3,6 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use libp2p_rendezvous::behaviour::{Event, Rendezvous}; use libp2p_swarm::Swarm; -use std::time::Duration; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { @@ -12,24 +11,18 @@ async fn given_successful_registration_then_successful_discovery() { let namespace = "some-namespace".to_string(); - println!("registring"); - - // register test.registration_swarm.behaviour_mut().register( namespace.clone(), test.rendezvous_swarm.local_peer_id().clone(), ); - test.assert_successful_registration(namespace.clone()).await; - println!("Registration worked!"); + test.discovery_swarm.behaviour_mut().discover( + Some(namespace), + test.rendezvous_swarm.local_peer_id().clone(), + ); - // // discover - // test.discovery_swarm - // .behaviour_mut() - // .discover(Some(namespace), test.rendezvous_peer_id); - // - // test.assert_successful_discovery().await; + test.assert_successful_discovery().await; } struct RendezvousTest { @@ -66,12 +59,15 @@ impl RendezvousTest { } } - pub async fn assert_successful_registration(&mut self, namespace: String) { + pub async fn assert_successful_registration(&mut self, expected_namespace: String) { match await_events_or_timeout(self.rendezvous_swarm.next(), self.registration_swarm.next()).await { ( - Event::PeerRegistered { .. }, - Event::RegisteredWithRendezvousNode { .. }, + Event::PeerRegistered { peer, namespace }, + Event::RegisteredWithRendezvousNode { rendezvous_node, .. }, ) => { + assert_eq!(&peer, self.registration_swarm.local_peer_id()); + assert_eq!(&rendezvous_node, self.rendezvous_swarm.local_peer_id()); + assert_eq!(namespace, expected_namespace); } (rendezvous_swarm_event, registration_swarm_event) => panic!( "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", @@ -81,15 +77,11 @@ impl RendezvousTest { } pub async fn assert_successful_discovery(&mut self) { - // TODO: Is it by design that there is no event emitted on the rendezvous side for discovery? - - match tokio::time::timeout(Duration::from_secs(10), self.discovery_swarm.next()) + match await_events_or_timeout(self.rendezvous_swarm.next(), self.discovery_swarm.next()) .await - .expect("") { - // TODO: Assert against the actual registration - Event::Discovered { .. } => {} - event => panic!("Discovery swarm emitted unexpected event {:?}", event), + (Event::AnsweredDiscoverRequest { .. }, Event::Discovered { .. }) => {} + (e1, e2) => panic!("Unexpected events {:?} {:?}", e1, e2), } } } From acc65830dda072e5132e1a7318eb403aa1b9beb2 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 14:54:16 +1000 Subject: [PATCH 065/242] Remove role name that was only there for debugging --- protocols/rendezvous/src/behaviour.rs | 11 +---------- protocols/rendezvous/tests/rendezvous.rs | 9 +++------ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 5a61b48630a..3034b0394a4 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -10,10 +10,7 @@ use log::debug; use std::collections::{HashMap, VecDeque}; use std::task::{Context, Poll}; -pub const DOMAIN: &str = "libp2p-rendezvous"; - pub struct Rendezvous { - role: String, events: VecDeque>, registrations: Registrations, key_pair: Keypair, @@ -21,9 +18,8 @@ pub struct Rendezvous { } impl Rendezvous { - pub fn new(key_pair: Keypair, role: String) -> Self { + pub fn new(key_pair: Keypair) -> Self { Self { - role, events: Default::default(), registrations: Registrations::new(), key_pair, @@ -141,7 +137,6 @@ impl NetworkBehaviour for Rendezvous { _connection: ConnectionId, event: crate::handler::HandlerEvent, ) { - debug!("{}: behaviour::inject_event {:?}", &self.role, &event); match event.0 { Message::Register(new_registration) => { let (namespace, ttl) = self.registrations.add(new_registration); @@ -226,10 +221,6 @@ impl NetworkBehaviour for Rendezvous { // It doesn't make sense to register addresses on which we are not reachable, hence this should not be configurable from the outside. self.external_addresses = poll_params.external_addresses().map(|r| r.addr).collect(); - debug!( - " {}: polling behaviour events: {:?}", - &self.role, &self.events - ); if let Some(event) = self.events.pop_front() { return Poll::Ready(event); } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index b165227970b..7e5b9b293e7 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -33,16 +33,13 @@ struct RendezvousTest { impl RendezvousTest { pub async fn setup() -> Self { - let mut registration_swarm = - new_swarm(|_, identity| Rendezvous::new(identity, "Registration".to_string())); + let mut registration_swarm = new_swarm(|_, identity| Rendezvous::new(identity)); registration_swarm.listen_on_random_memory_address().await; - let mut discovery_swarm = - new_swarm(|_, identity| Rendezvous::new(identity, "Discovery".to_string())); + let mut discovery_swarm = new_swarm(|_, identity| Rendezvous::new(identity)); discovery_swarm.listen_on_random_memory_address().await; - let mut rendezvous_swarm = - new_swarm(|_, identity| Rendezvous::new(identity, "Rendezvous".to_string())); + let mut rendezvous_swarm = new_swarm(|_, identity| Rendezvous::new(identity)); rendezvous_swarm.listen_on_random_memory_address().await; registration_swarm From 1ef3539bcf9dea4ba5a469b373b4b2ce36236fab Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 14:56:09 +1000 Subject: [PATCH 066/242] Add empty line for better formatting --- protocols/rendezvous/src/behaviour.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 3034b0394a4..765a6b54e43 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -60,6 +60,7 @@ impl Rendezvous { handler: NotifyHandler::Any, }); } + pub fn discover(&mut self, ns: Option, rendezvous_node: PeerId) { self.events .push_back(NetworkBehaviourAction::NotifyHandler { From f42ac1866d67ac1267f6d5baaeb1637aeb8b1711 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 14:56:48 +1000 Subject: [PATCH 067/242] Rename `handler::Input` to `handler::InEvent` --- protocols/rendezvous/src/behaviour.rs | 14 +++++++------- protocols/rendezvous/src/handler.rs | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 765a6b54e43..7e45b22679f 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,5 +1,5 @@ use crate::codec::{ErrorCode, Message, NewRegistration, Registration}; -use crate::handler::{Input, RendezvousHandler}; +use crate::handler::{InEvent, RendezvousHandler}; use libp2p_core::connection::ConnectionId; use libp2p_core::identity::Keypair; use libp2p_core::{AuthenticatedPeerRecord, Multiaddr, PeerId, PeerRecord}; @@ -11,7 +11,7 @@ use std::collections::{HashMap, VecDeque}; use std::task::{Context, Poll}; pub struct Rendezvous { - events: VecDeque>, + events: VecDeque>, registrations: Registrations, key_pair: Keypair, external_addresses: Vec, @@ -41,7 +41,7 @@ impl Rendezvous { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: Input::RegisterRequest { + event: InEvent::RegisterRequest { request: NewRegistration { namespace, record: authenticated_peer_record, @@ -56,7 +56,7 @@ impl Rendezvous { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: Input::UnregisterRequest { namespace }, + event: InEvent::UnregisterRequest { namespace }, handler: NotifyHandler::Any, }); } @@ -65,7 +65,7 @@ impl Rendezvous { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: Input::DiscoverRequest { namespace: ns }, + event: InEvent::DiscoverRequest { namespace: ns }, handler: NotifyHandler::Any, }); } @@ -147,7 +147,7 @@ impl NetworkBehaviour for Rendezvous { .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::RegisterResponse { ttl }, + event: InEvent::RegisterResponse { ttl }, }); // emit behaviour event @@ -181,7 +181,7 @@ impl NetworkBehaviour for Rendezvous { .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: Input::DiscoverResponse { + event: InEvent::DiscoverResponse { discovered: registrations.clone(), }, }); diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 4afadc43e97..af4f77453c3 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -37,7 +37,7 @@ impl RendezvousHandler { pub struct HandlerEvent(pub Message); #[derive(Debug)] -pub enum Input { +pub enum InEvent { RegisterRequest { request: NewRegistration, }, @@ -277,7 +277,7 @@ impl OutboundState { } impl ProtocolsHandler for RendezvousHandler { - type InEvent = Input; + type InEvent = InEvent; type OutEvent = HandlerEvent; type Error = crate::codec::Error; type InboundOpenInfo = (); @@ -317,7 +317,7 @@ impl ProtocolsHandler for RendezvousHandler { } // event injected from NotifyHandler - fn inject_event(&mut self, req: Input) { + fn inject_event(&mut self, req: InEvent) { debug!("injecting event into handler from behaviour: {:?}", &req); let (inbound_substream, outbound_substream) = match ( req, @@ -325,7 +325,7 @@ impl ProtocolsHandler for RendezvousHandler { std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned), ) { ( - Input::RegisterRequest { + InEvent::RegisterRequest { request: NewRegistration { namespace, @@ -343,20 +343,20 @@ impl ProtocolsHandler for RendezvousHandler { ttl, ))), ), - (Input::UnregisterRequest { namespace }, inbound, OutboundState::None) => ( + (InEvent::UnregisterRequest { namespace }, inbound, OutboundState::None) => ( inbound, OutboundState::Start(Message::Unregister { namespace: namespace.clone(), }), ), - (Input::DiscoverRequest { namespace }, inbound, OutboundState::None) => ( + (InEvent::DiscoverRequest { namespace }, inbound, OutboundState::None) => ( inbound, OutboundState::Start(Message::Discover { namespace: namespace.clone(), }), ), ( - Input::RegisterResponse { ttl }, + InEvent::RegisterResponse { ttl }, InboundState::WaitForBehaviour(substream), outbound, ) => ( @@ -364,7 +364,7 @@ impl ProtocolsHandler for RendezvousHandler { outbound, ), ( - Input::DiscoverResponse { discovered }, + InEvent::DiscoverResponse { discovered }, InboundState::WaitForBehaviour(substream), outbound, ) => { From 8bb25b97e4fe156bd26bac0933873f38a4987779 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 15:04:05 +1000 Subject: [PATCH 068/242] Don't wrap Message on the way up from the handler --- protocols/rendezvous/src/behaviour.rs | 5 +++-- protocols/rendezvous/src/handler.rs | 15 ++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 7e45b22679f..ff6b5f1f2b2 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,4 +1,5 @@ use crate::codec::{ErrorCode, Message, NewRegistration, Registration}; +use crate::handler; use crate::handler::{InEvent, RendezvousHandler}; use libp2p_core::connection::ConnectionId; use libp2p_core::identity::Keypair; @@ -136,9 +137,9 @@ impl NetworkBehaviour for Rendezvous { &mut self, peer_id: PeerId, _connection: ConnectionId, - event: crate::handler::HandlerEvent, + message: handler::OutEvent, ) { - match event.0 { + match message { Message::Register(new_registration) => { let (namespace, ttl) = self.registrations.add(new_registration); diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index af4f77453c3..aef41873f81 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -33,8 +33,7 @@ impl RendezvousHandler { } } -#[derive(Debug)] -pub struct HandlerEvent(pub Message); +pub type OutEvent = Message; #[derive(Debug)] pub enum InEvent { @@ -78,7 +77,7 @@ impl InboundState { fn poll( &mut self, cx: &mut Context<'_>, - ) -> Poll> + ) -> Poll> { loop { match std::mem::replace(self, InboundState::Poisoned) { @@ -96,7 +95,7 @@ impl InboundState { | Message::Discover { .. } | Message::Unregister { .. } = msg { - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent(msg))); + return Poll::Ready(ProtocolsHandlerEvent::Custom(msg)); } else { panic!("Invalid inbound message"); } @@ -181,7 +180,7 @@ impl OutboundState { fn poll( &mut self, cx: &mut Context<'_>, - ) -> Poll> + ) -> Poll> { loop { match std::mem::replace(self, OutboundState::Poisoned) { @@ -239,9 +238,7 @@ impl OutboundState { | Message::FailedToDiscover { .. } | Message::FailedToRegister { .. } = msg { - return Poll::Ready(ProtocolsHandlerEvent::Custom(HandlerEvent( - msg, - ))); + return Poll::Ready(ProtocolsHandlerEvent::Custom(msg)); } else { panic!("Invalid inbound message"); } @@ -278,7 +275,7 @@ impl OutboundState { impl ProtocolsHandler for RendezvousHandler { type InEvent = InEvent; - type OutEvent = HandlerEvent; + type OutEvent = OutEvent; type Error = crate::codec::Error; type InboundOpenInfo = (); type InboundProtocol = protocol::Rendezvous; From e337024f9ef6ea4769a059f59abc7048c71dad10 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 15:25:21 +1000 Subject: [PATCH 069/242] Introduce `Next` enum for making `State::poll` fns declarative Modelling the `poll` functions as a pure function of `State` -> `Next` makes it hopefully easier to understand what is going on here. --- protocols/rendezvous/src/handler.rs | 200 ++++++++++++++++------------ 1 file changed, 115 insertions(+), 85 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index aef41873f81..cc52df13a2f 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,7 +1,7 @@ use crate::codec::{Message, Registration}; use crate::codec::{NewRegistration, RendezvousCodec}; -use crate::protocol; use crate::protocol::Rendezvous; +use crate::{codec, protocol}; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; use libp2p_core::{InboundUpgrade, OutboundUpgrade}; @@ -57,6 +57,14 @@ pub enum InEvent { }, } +/// Defines what should happen next after polling an [`InboundState`] or [`OutboundState`]. +enum Next { + /// Return from the `poll` function, either because are `Ready` or there is no more work to do (`Pending`). + Return { poll: Poll, next_state: S }, + /// Continue with polling the state. + Continue { next_state: S }, +} + /// State of the inbound substream, opened either by us or by the remote. enum InboundState { None, @@ -73,29 +81,28 @@ enum InboundState { } impl InboundState { - // TODO: See if we can refactor this such that we don't need to assign `self` in so many branches fn poll( &mut self, cx: &mut Context<'_>, - ) -> Poll> - { + ) -> Poll> { loop { - match std::mem::replace(self, InboundState::Poisoned) { - InboundState::None => { - *self = InboundState::None; - return Poll::Pending; - } + let next = match std::mem::replace(self, InboundState::Poisoned) { + InboundState::None => Next::Return { + poll: Poll::Pending, + next_state: InboundState::None, + }, InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { debug!("read message from inbound {:?}", msg); - *self = InboundState::WaitForBehaviour(substream); - if let Message::Register(..) | Message::Discover { .. } | Message::Unregister { .. } = msg { - return Poll::Ready(ProtocolsHandlerEvent::Custom(msg)); + Next::Return { + poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), + next_state: InboundState::WaitForBehaviour(substream), + } } else { panic!("Invalid inbound message"); } @@ -106,21 +113,21 @@ impl InboundState { Poll::Ready(None) => { panic!("Honestly no idea what to do if this happens"); } - Poll::Pending => { - *self = InboundState::Reading(substream); - return Poll::Pending; - } + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: InboundState::Reading(substream), + }, + }, + InboundState::WaitForBehaviour(substream) => Next::Return { + poll: Poll::Pending, + next_state: InboundState::WaitForBehaviour(substream), }, - InboundState::WaitForBehaviour(substream) => { - *self = InboundState::WaitForBehaviour(substream); - return Poll::Pending; - } InboundState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { - Ok(()) => { - *self = InboundState::PendingFlush(substream); - } + Ok(()) => Next::Continue { + next_state: InboundState::PendingFlush(substream), + }, Err(e) => { panic!("pending send from inbound error: {:?}", e); } @@ -128,34 +135,44 @@ impl InboundState { Poll::Ready(Err(e)) => { panic!("pending send from inbound error: {:?}", e); } - Poll::Pending => { - *self = InboundState::PendingSend(substream, message); - return Poll::Pending; - } + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: InboundState::PendingSend(substream, message), + }, } } InboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => { - *self = InboundState::Closing(substream); - } + Poll::Ready(Ok(())) => Next::Continue { + next_state: InboundState::Closing(substream), + }, Poll::Ready(Err(e)) => panic!("pending send from inbound error: {:?}", e), - Poll::Pending => { - *self = InboundState::PendingFlush(substream); - return Poll::Pending; - } + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: InboundState::PendingFlush(substream), + }, }, InboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => { - *self = InboundState::None; - return Poll::Pending; - } - Poll::Pending => { - *self = InboundState::Closing(substream); - return Poll::Pending; - } + Poll::Ready(..) => Next::Return { + poll: Poll::Pending, + next_state: InboundState::None, + }, + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: InboundState::Closing(substream), + }, }, InboundState::Poisoned => panic!("inbound poisoned"), }; + + match next { + Next::Return { poll, next_state } => { + *self = next_state; + return poll; + } + Next::Continue { next_state } => { + *self = next_state; + } + } } } } @@ -180,30 +197,29 @@ impl OutboundState { fn poll( &mut self, cx: &mut Context<'_>, - ) -> Poll> - { + ) -> Poll> { loop { - match std::mem::replace(self, OutboundState::Poisoned) { - OutboundState::None => { - *self = OutboundState::None; - return Poll::Pending; - } - OutboundState::Start(msg) => { - *self = OutboundState::WaitingUpgrade; - return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { + let next = match std::mem::replace(self, OutboundState::Poisoned) { + OutboundState::None => Next::Return { + poll: Poll::Pending, + next_state: OutboundState::None, + }, + OutboundState::Start(msg) => Next::Return { + poll: Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol: SubstreamProtocol::new(Rendezvous, msg), - }); - } - OutboundState::WaitingUpgrade => { - *self = OutboundState::WaitingUpgrade; - return Poll::Pending; - } + }), + next_state: OutboundState::WaitingUpgrade, + }, + OutboundState::WaitingUpgrade => Next::Return { + poll: Poll::Pending, + next_state: OutboundState::WaitingUpgrade, + }, OutboundState::PendingSend(mut substream, message) => { match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { - Ok(()) => { - *self = OutboundState::PendingFlush(substream); - } + Ok(()) => Next::Continue { + next_state: OutboundState::PendingFlush(substream), + }, Err(e) => { panic!("Error when sending outbound: {:?}", e); } @@ -211,34 +227,38 @@ impl OutboundState { Poll::Ready(Err(e)) => { panic!("Error when sending outbound: {:?}", e); } - Poll::Pending => { - *self = OutboundState::PendingSend(substream, message); - return Poll::Pending; - } + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: OutboundState::PendingSend(substream, message), + }, } } OutboundState::PendingFlush(mut substream) => { match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => *self = OutboundState::WaitForRemote(substream), + Poll::Ready(Ok(())) => Next::Continue { + next_state: OutboundState::WaitForRemote(substream), + }, Poll::Ready(Err(e)) => { panic!("Error when flushing outbound: {:?}", e); } - Poll::Pending => { - *self = OutboundState::PendingFlush(substream); - return Poll::Pending; - } + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: OutboundState::PendingFlush(substream), + }, } } OutboundState::WaitForRemote(mut substream) => { match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { - *self = OutboundState::Closing(substream); if let Message::DiscoverResponse { .. } | Message::RegisterResponse { .. } | Message::FailedToDiscover { .. } | Message::FailedToRegister { .. } = msg { - return Poll::Ready(ProtocolsHandlerEvent::Custom(msg)); + Next::Return { + poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), + next_state: OutboundState::Closing(substream), + } } else { panic!("Invalid inbound message"); } @@ -249,25 +269,35 @@ impl OutboundState { Poll::Ready(None) => { panic!("Honestly no idea what to do if this happens"); } - Poll::Pending => { - *self = OutboundState::WaitForRemote(substream); - return Poll::Pending; - } + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: OutboundState::WaitForRemote(substream), + }, } } OutboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => { - *self = OutboundState::None; - return Poll::Pending; - } - Poll::Pending => { - *self = OutboundState::Closing(substream); - return Poll::Pending; - } + Poll::Ready(..) => Next::Return { + poll: Poll::Pending, + next_state: OutboundState::None, + }, + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: OutboundState::Closing(substream), + }, }, OutboundState::Poisoned => { panic!("outbound poisoned"); } + }; + + match next { + Next::Return { poll, next_state } => { + *self = next_state; + return poll; + } + Next::Continue { next_state } => { + *self = next_state; + } } } } @@ -276,7 +306,7 @@ impl OutboundState { impl ProtocolsHandler for RendezvousHandler { type InEvent = InEvent; type OutEvent = OutEvent; - type Error = crate::codec::Error; + type Error = codec::Error; type InboundOpenInfo = (); type InboundProtocol = protocol::Rendezvous; type OutboundOpenInfo = Message; From 6128354fb9c2681e4e6c4498c28d05cf535cd3be Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 15:44:24 +1000 Subject: [PATCH 070/242] Fix documentation of {Inbound,Outbound}State --- protocols/rendezvous/src/handler.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index cc52df13a2f..275cd8017df 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -65,18 +65,21 @@ enum Next { Continue { next_state: S }, } -/// State of the inbound substream, opened either by us or by the remote. +/// The state of an inbound substream (i.e. the remote node opened it). enum InboundState { + /// There is no substream. None, - /// Waiting for behaviour to respond to the inbound substream + /// We are in the process of reading a message from the substream. Reading(Framed), - /// Waiting for behaviour to respond to the inbound substream + /// We read a message, dispatched it to the behaviour and are waiting for the response. WaitForBehaviour(Framed), - /// Waiting to send response to remote + /// We are in the process of sending a response. PendingSend(Framed, Message), + /// We started sending and are currently flushing the data out. PendingFlush(Framed), + /// We've sent the message and are now closing down the substream. Closing(Framed), - /// An error occurred during processing. + /// Something went seriously wrong. Poisoned, } @@ -177,19 +180,23 @@ impl InboundState { } } -/// State of the outbound substream, opened either by us or by the remote. +/// The state of an outbound substream (i.e. we opened it). enum OutboundState { + /// There is no substream. None, + /// We got a message to send from the behaviour. Start(Message), + /// We've requested a substream and are waiting for it to be set up. WaitingUpgrade, - /// Waiting to send a message to the remote. + /// We got the substream, now we need to send the message. PendingSend(Framed, Message), - /// Waiting to flush the substream so that the data arrives to the remote. + /// We sent the message, now we need to flush the data out. PendingFlush(Framed), - /// Waiting for remote to respond on the outbound substream + /// We are waiting for the response from the remote. WaitForRemote(Framed), + /// We got a message from the remote and dispatched it to the behaviour, now we are closing down the substream. Closing(Framed), - /// An error occurred during processing. + /// Something went seriously wrong. Poisoned, } From 4806c965df503df81ba3989eb0c4f24571befe40 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 15:45:27 +1000 Subject: [PATCH 071/242] Align order of members as per trait definition --- protocols/rendezvous/src/handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 275cd8017df..95b2bad973f 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -314,10 +314,10 @@ impl ProtocolsHandler for RendezvousHandler { type InEvent = InEvent; type OutEvent = OutEvent; type Error = codec::Error; - type InboundOpenInfo = (); type InboundProtocol = protocol::Rendezvous; - type OutboundOpenInfo = Message; type OutboundProtocol = protocol::Rendezvous; + type InboundOpenInfo = (); + type OutboundOpenInfo = Message; fn listen_protocol(&self) -> SubstreamProtocol { debug!("creating substream protocol"); From 7e0d9da7925152ae312923ff4531936d948e661a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 15:48:10 +1000 Subject: [PATCH 072/242] Remove noisy logs --- protocols/rendezvous/src/handler.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 95b2bad973f..fcda336bf72 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -410,8 +410,6 @@ impl ProtocolsHandler for RendezvousHandler { _ => unreachable!("Handler in invalid state"), }; - debug!("inbound: {:?}", inbound_substream); - debug!("outbound: {:?}", outbound_substream); self.inbound_substream = inbound_substream; self.outbound_substream = outbound_substream; } From 939faa95d9e1e915a82971ec24ccc793b7ccacaa Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 15:49:19 +1000 Subject: [PATCH 073/242] Simplify handling of `InEvent` --- protocols/rendezvous/src/handler.rs | 45 +++++++++-------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index fcda336bf72..fc34183b483 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -358,36 +358,16 @@ impl ProtocolsHandler for RendezvousHandler { std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned), std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned), ) { - ( - InEvent::RegisterRequest { - request: - NewRegistration { - namespace, - record, - ttl, - }, - }, - inbound, - OutboundState::None, - ) => ( - inbound, - OutboundState::Start(Message::Register(NewRegistration::new( - namespace.clone(), - record, - ttl, - ))), - ), + (InEvent::RegisterRequest { request: reggo }, inbound, OutboundState::None) => { + (inbound, OutboundState::Start(Message::Register(reggo))) + } (InEvent::UnregisterRequest { namespace }, inbound, OutboundState::None) => ( inbound, - OutboundState::Start(Message::Unregister { - namespace: namespace.clone(), - }), + OutboundState::Start(Message::Unregister { namespace }), ), (InEvent::DiscoverRequest { namespace }, inbound, OutboundState::None) => ( inbound, - OutboundState::Start(Message::Discover { - namespace: namespace.clone(), - }), + OutboundState::Start(Message::Discover { namespace }), ), ( InEvent::RegisterResponse { ttl }, @@ -401,12 +381,15 @@ impl ProtocolsHandler for RendezvousHandler { InEvent::DiscoverResponse { discovered }, InboundState::WaitForBehaviour(substream), outbound, - ) => { - let msg = Message::DiscoverResponse { - registrations: discovered, - }; - (InboundState::PendingSend(substream, msg), outbound) - } + ) => ( + InboundState::PendingSend( + substream, + Message::DiscoverResponse { + registrations: discovered, + }, + ), + outbound, + ), _ => unreachable!("Handler in invalid state"), }; From e9707585f37e380235722e25b1327a446e1e7a3c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 15:49:58 +1000 Subject: [PATCH 074/242] Import std::mem --- protocols/rendezvous/src/handler.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index fc34183b483..6ad74b03160 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -11,9 +11,9 @@ use libp2p_swarm::{ }; use log::debug; use log::error; -use std::fmt; use std::fmt::{Debug, Formatter}; use std::task::{Context, Poll}; +use std::{fmt, mem}; use void::Void; #[derive(Debug)] @@ -89,7 +89,7 @@ impl InboundState { cx: &mut Context<'_>, ) -> Poll> { loop { - let next = match std::mem::replace(self, InboundState::Poisoned) { + let next = match mem::replace(self, InboundState::Poisoned) { InboundState::None => Next::Return { poll: Poll::Pending, next_state: InboundState::None, @@ -206,7 +206,7 @@ impl OutboundState { cx: &mut Context<'_>, ) -> Poll> { loop { - let next = match std::mem::replace(self, OutboundState::Poisoned) { + let next = match mem::replace(self, OutboundState::Poisoned) { OutboundState::None => Next::Return { poll: Poll::Pending, next_state: OutboundState::None, @@ -355,8 +355,8 @@ impl ProtocolsHandler for RendezvousHandler { debug!("injecting event into handler from behaviour: {:?}", &req); let (inbound_substream, outbound_substream) = match ( req, - std::mem::replace(&mut self.inbound_substream, InboundState::Poisoned), - std::mem::replace(&mut self.outbound_substream, OutboundState::Poisoned), + mem::replace(&mut self.inbound_substream, InboundState::Poisoned), + mem::replace(&mut self.outbound_substream, OutboundState::Poisoned), ) { (InEvent::RegisterRequest { request: reggo }, inbound, OutboundState::None) => { (inbound, OutboundState::Start(Message::Register(reggo))) From 35c91abe3830c1c043c9bf943e08a9fc46d86689 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 15:51:44 +1000 Subject: [PATCH 075/242] Shorten variable and fieldnames --- protocols/rendezvous/src/handler.rs | 40 ++++++++++++----------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 6ad74b03160..78bbd00f374 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -18,16 +18,16 @@ use void::Void; #[derive(Debug)] pub struct RendezvousHandler { - outbound_substream: OutboundState, - inbound_substream: InboundState, + outbound: OutboundState, + inbound: InboundState, keep_alive: KeepAlive, } impl RendezvousHandler { pub fn new() -> Self { Self { - outbound_substream: OutboundState::None, - inbound_substream: InboundState::None, + outbound: OutboundState::None, + inbound: InboundState::None, keep_alive: KeepAlive::Yes, } } @@ -330,8 +330,8 @@ impl ProtocolsHandler for RendezvousHandler { _msg: Self::InboundOpenInfo, ) { debug!("injected inbound"); - if let InboundState::None = self.inbound_substream { - self.inbound_substream = InboundState::Reading(substream); + if let InboundState::None = self.inbound { + self.inbound = InboundState::Reading(substream); } else { unreachable!("Invalid inbound state") } @@ -343,8 +343,8 @@ impl ProtocolsHandler for RendezvousHandler { msg: Self::OutboundOpenInfo, ) { debug!("injected outbound"); - if let OutboundState::WaitingUpgrade = self.outbound_substream { - self.outbound_substream = OutboundState::PendingSend(substream, msg); + if let OutboundState::WaitingUpgrade = self.outbound { + self.outbound = OutboundState::PendingSend(substream, msg); } else { unreachable!("Invalid outbound state") } @@ -353,10 +353,10 @@ impl ProtocolsHandler for RendezvousHandler { // event injected from NotifyHandler fn inject_event(&mut self, req: InEvent) { debug!("injecting event into handler from behaviour: {:?}", &req); - let (inbound_substream, outbound_substream) = match ( + let (inbound, outbound) = match ( req, - mem::replace(&mut self.inbound_substream, InboundState::Poisoned), - mem::replace(&mut self.outbound_substream, OutboundState::Poisoned), + mem::replace(&mut self.inbound, InboundState::Poisoned), + mem::replace(&mut self.outbound, OutboundState::Poisoned), ) { (InEvent::RegisterRequest { request: reggo }, inbound, OutboundState::None) => { (inbound, OutboundState::Start(Message::Register(reggo))) @@ -393,8 +393,8 @@ impl ProtocolsHandler for RendezvousHandler { _ => unreachable!("Handler in invalid state"), }; - self.inbound_substream = inbound_substream; - self.outbound_substream = outbound_substream; + self.inbound = inbound; + self.outbound = outbound; } fn inject_dial_upgrade_error( @@ -421,20 +421,14 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { - debug!( - "polling handler: inbound_state: {:?}", - &self.inbound_substream - ); - debug!( - "polling handler: outbound_state {:?}", - &self.outbound_substream - ); + debug!("polling handler: inbound_state: {:?}", &self.inbound); + debug!("polling handler: outbound_state {:?}", &self.outbound); - if let Poll::Ready(event) = self.inbound_substream.poll(cx) { + if let Poll::Ready(event) = self.inbound.poll(cx) { return Poll::Ready(event); } - if let Poll::Ready(event) = self.outbound_substream.poll(cx) { + if let Poll::Ready(event) = self.outbound.poll(cx) { return Poll::Ready(event); } From 7610d3d60dd071611cf133123f4527fb2b8c71b3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 16:22:24 +1000 Subject: [PATCH 076/242] Have some fun with Rust --- protocols/rendezvous/src/handler.rs | 478 ++++++++++++++-------------- 1 file changed, 240 insertions(+), 238 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 78bbd00f374..ef1e0913850 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -18,16 +18,16 @@ use void::Void; #[derive(Debug)] pub struct RendezvousHandler { - outbound: OutboundState, - inbound: InboundState, + outbound: SubstreamState, + inbound: SubstreamState, keep_alive: KeepAlive, } impl RendezvousHandler { pub fn new() -> Self { Self { - outbound: OutboundState::None, - inbound: InboundState::None, + outbound: SubstreamState::None, + inbound: SubstreamState::None, keep_alive: KeepAlive::Yes, } } @@ -57,133 +57,84 @@ pub enum InEvent { }, } -/// Defines what should happen next after polling an [`InboundState`] or [`OutboundState`]. +#[derive(Debug)] +enum SubstreamState { + /// There is no substream. + None, + /// The substream is in an active state. + Active(S), + /// Something went seriously wrong. + Poisoned, +} + +/// Advances a state machine. +trait Advance: Sized { + type Event; + + fn advance(self, cx: &mut Context<'_>) -> Next; +} + +/// Defines the results of advancing a state machine. enum Next { /// Return from the `poll` function, either because are `Ready` or there is no more work to do (`Pending`). Return { poll: Poll, next_state: S }, /// Continue with polling the state. Continue { next_state: S }, + /// The state machine finished. + Done, } -/// The state of an inbound substream (i.e. the remote node opened it). -enum InboundState { - /// There is no substream. - None, - /// We are in the process of reading a message from the substream. - Reading(Framed), - /// We read a message, dispatched it to the behaviour and are waiting for the response. - WaitForBehaviour(Framed), - /// We are in the process of sending a response. - PendingSend(Framed, Message), - /// We started sending and are currently flushing the data out. - PendingFlush(Framed), - /// We've sent the message and are now closing down the substream. - Closing(Framed), - /// Something went seriously wrong. - Poisoned, -} - -impl InboundState { - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll> { +impl SubstreamState +where + S: Advance, +{ + fn poll(&mut self, cx: &mut Context<'_>) -> Poll { loop { - let next = match mem::replace(self, InboundState::Poisoned) { - InboundState::None => Next::Return { - poll: Poll::Pending, - next_state: InboundState::None, - }, - InboundState::Reading(mut substream) => match substream.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(msg))) => { - debug!("read message from inbound {:?}", msg); - - if let Message::Register(..) - | Message::Discover { .. } - | Message::Unregister { .. } = msg - { - Next::Return { - poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), - next_state: InboundState::WaitForBehaviour(substream), - } - } else { - panic!("Invalid inbound message"); - } - } - Poll::Ready(Some(Err(e))) => { - panic!("Error when sending outbound: {:?}", e); - } - Poll::Ready(None) => { - panic!("Honestly no idea what to do if this happens"); - } - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: InboundState::Reading(substream), - }, - }, - InboundState::WaitForBehaviour(substream) => Next::Return { - poll: Poll::Pending, - next_state: InboundState::WaitForBehaviour(substream), - }, - InboundState::PendingSend(mut substream, message) => { - match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { - Ok(()) => Next::Continue { - next_state: InboundState::PendingFlush(substream), - }, - Err(e) => { - panic!("pending send from inbound error: {:?}", e); - } - }, - Poll::Ready(Err(e)) => { - panic!("pending send from inbound error: {:?}", e); - } - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: InboundState::PendingSend(substream, message), - }, - } + let next = match mem::replace(self, SubstreamState::Poisoned) { + SubstreamState::None => { + *self = SubstreamState::None; + return Poll::Pending; + } + SubstreamState::Active(state) => state.advance(cx), + SubstreamState::Poisoned => { + unreachable!("reached poisoned state") } - InboundState::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => Next::Continue { - next_state: InboundState::Closing(substream), - }, - Poll::Ready(Err(e)) => panic!("pending send from inbound error: {:?}", e), - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: InboundState::PendingFlush(substream), - }, - }, - InboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => Next::Return { - poll: Poll::Pending, - next_state: InboundState::None, - }, - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: InboundState::Closing(substream), - }, - }, - InboundState::Poisoned => panic!("inbound poisoned"), }; match next { + Next::Continue { next_state } => { + *self = SubstreamState::Active(next_state); + continue; + } Next::Return { poll, next_state } => { - *self = next_state; + *self = SubstreamState::Active(next_state); return poll; } - Next::Continue { next_state } => { - *self = next_state; + Next::Done => { + *self = SubstreamState::None; + return Poll::Pending; } } } } } +/// The state of an inbound substream (i.e. the remote node opened it). +enum Inbound { + /// We are in the process of reading a message from the substream. + Reading(Framed), + /// We read a message, dispatched it to the behaviour and are waiting for the response. + WaitForBehaviour(Framed), + /// We are in the process of sending a response. + PendingSend(Framed, Message), + /// We started sending and are currently flushing the data out. + PendingFlush(Framed), + /// We've sent the message and are now closing down the substream. + Closing(Framed), +} + /// The state of an outbound substream (i.e. we opened it). -enum OutboundState { - /// There is no substream. - None, +enum Outbound { /// We got a message to send from the behaviour. Start(Message), /// We've requested a substream and are waiting for it to be set up. @@ -196,116 +147,167 @@ enum OutboundState { WaitForRemote(Framed), /// We got a message from the remote and dispatched it to the behaviour, now we are closing down the substream. Closing(Framed), - /// Something went seriously wrong. - Poisoned, } -impl OutboundState { - fn poll( - &mut self, +impl Advance for Inbound { + type Event = ProtocolsHandlerEvent; + + fn advance( + self, cx: &mut Context<'_>, - ) -> Poll> { - loop { - let next = match mem::replace(self, OutboundState::Poisoned) { - OutboundState::None => Next::Return { + ) -> Next> + { + match self { + Inbound::Reading(mut substream) => match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(msg))) => { + debug!("read message from inbound {:?}", msg); + + if let Message::Register(..) + | Message::Discover { .. } + | Message::Unregister { .. } = msg + { + Next::Return { + poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), + next_state: Inbound::WaitForBehaviour(substream), + } + } else { + panic!("Invalid inbound message"); + } + } + Poll::Ready(Some(Err(e))) => { + panic!("Error when sending outbound: {:?}", e); + } + Poll::Ready(None) => { + panic!("Honestly no idea what to do if this happens"); + } + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: Inbound::Reading(substream), + }, + }, + Inbound::WaitForBehaviour(substream) => Next::Return { + poll: Poll::Pending, + next_state: Inbound::WaitForBehaviour(substream), + }, + Inbound::PendingSend(mut substream, message) => match substream.poll_ready_unpin(cx) { + Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { + Ok(()) => Next::Continue { + next_state: Inbound::PendingFlush(substream), + }, + Err(e) => { + panic!("pending send from inbound error: {:?}", e); + } + }, + Poll::Ready(Err(e)) => { + panic!("pending send from inbound error: {:?}", e); + } + Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: OutboundState::None, + next_state: Inbound::PendingSend(substream, message), }, - OutboundState::Start(msg) => Next::Return { - poll: Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(Rendezvous, msg), - }), - next_state: OutboundState::WaitingUpgrade, + }, + Inbound::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { + Poll::Ready(Ok(())) => Next::Continue { + next_state: Inbound::Closing(substream), }, - OutboundState::WaitingUpgrade => Next::Return { + Poll::Ready(Err(e)) => panic!("pending send from inbound error: {:?}", e), + Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: OutboundState::WaitingUpgrade, + next_state: Inbound::PendingFlush(substream), }, - OutboundState::PendingSend(mut substream, message) => { - match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { - Ok(()) => Next::Continue { - next_state: OutboundState::PendingFlush(substream), - }, - Err(e) => { - panic!("Error when sending outbound: {:?}", e); - } - }, - Poll::Ready(Err(e)) => { - panic!("Error when sending outbound: {:?}", e); - } - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: OutboundState::PendingSend(substream, message), - }, + }, + Inbound::Closing(mut substream) => match substream.poll_close_unpin(cx) { + Poll::Ready(..) => Next::Done, + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: Inbound::Closing(substream), + }, + }, + } + } +} + +impl Advance for Outbound { + type Event = ProtocolsHandlerEvent; + + fn advance( + self, + cx: &mut Context<'_>, + ) -> Next> + { + match self { + Outbound::Start(msg) => Next::Return { + poll: Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(Rendezvous, msg), + }), + next_state: Outbound::WaitingUpgrade, + }, + Outbound::WaitingUpgrade => Next::Return { + poll: Poll::Pending, + next_state: Outbound::WaitingUpgrade, + }, + Outbound::PendingSend(mut substream, message) => match substream.poll_ready_unpin(cx) { + Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { + Ok(()) => Next::Continue { + next_state: Outbound::PendingFlush(substream), + }, + Err(e) => { + panic!("Error when sending outbound: {:?}", e); } + }, + Poll::Ready(Err(e)) => { + panic!("Error when sending outbound: {:?}", e); } - OutboundState::PendingFlush(mut substream) => { - match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => Next::Continue { - next_state: OutboundState::WaitForRemote(substream), - }, - Poll::Ready(Err(e)) => { - panic!("Error when flushing outbound: {:?}", e); - } - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: OutboundState::PendingFlush(substream), - }, - } + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: Outbound::PendingSend(substream, message), + }, + }, + Outbound::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { + Poll::Ready(Ok(())) => Next::Continue { + next_state: Outbound::WaitForRemote(substream), + }, + Poll::Ready(Err(e)) => { + panic!("Error when flushing outbound: {:?}", e); } - OutboundState::WaitForRemote(mut substream) => { - match substream.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(msg))) => { - if let Message::DiscoverResponse { .. } - | Message::RegisterResponse { .. } - | Message::FailedToDiscover { .. } - | Message::FailedToRegister { .. } = msg - { - Next::Return { - poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), - next_state: OutboundState::Closing(substream), - } - } else { - panic!("Invalid inbound message"); - } - } - Poll::Ready(Some(Err(e))) => { - panic!("Error when receiving message from outbound: {:?}", e) - } - Poll::Ready(None) => { - panic!("Honestly no idea what to do if this happens"); + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: Outbound::PendingFlush(substream), + }, + }, + Outbound::WaitForRemote(mut substream) => match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(msg))) => { + if let Message::DiscoverResponse { .. } + | Message::RegisterResponse { .. } + | Message::FailedToDiscover { .. } + | Message::FailedToRegister { .. } = msg + { + Next::Return { + poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), + next_state: Outbound::Closing(substream), } - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: OutboundState::WaitForRemote(substream), - }, + } else { + panic!("Invalid inbound message"); } } - OutboundState::Closing(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => Next::Return { - poll: Poll::Pending, - next_state: OutboundState::None, - }, - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: OutboundState::Closing(substream), - }, - }, - OutboundState::Poisoned => { - panic!("outbound poisoned"); - } - }; - - match next { - Next::Return { poll, next_state } => { - *self = next_state; - return poll; + Poll::Ready(Some(Err(e))) => { + panic!("Error when receiving message from outbound: {:?}", e) } - Next::Continue { next_state } => { - *self = next_state; + Poll::Ready(None) => { + panic!("Honestly no idea what to do if this happens"); } - } + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: Outbound::WaitForRemote(substream), + }, + }, + Outbound::Closing(mut substream) => match substream.poll_close_unpin(cx) { + Poll::Ready(..) => Next::Done, + Poll::Pending => Next::Return { + poll: Poll::Pending, + next_state: Outbound::Closing(substream), + }, + }, } } } @@ -330,8 +332,8 @@ impl ProtocolsHandler for RendezvousHandler { _msg: Self::InboundOpenInfo, ) { debug!("injected inbound"); - if let InboundState::None = self.inbound { - self.inbound = InboundState::Reading(substream); + if let SubstreamState::None = self.inbound { + self.inbound = SubstreamState::Active(Inbound::Reading(substream)); } else { unreachable!("Invalid inbound state") } @@ -343,8 +345,8 @@ impl ProtocolsHandler for RendezvousHandler { msg: Self::OutboundOpenInfo, ) { debug!("injected outbound"); - if let OutboundState::WaitingUpgrade = self.outbound { - self.outbound = OutboundState::PendingSend(substream, msg); + if let SubstreamState::Active(Outbound::WaitingUpgrade) = self.outbound { + self.outbound = SubstreamState::Active(Outbound::PendingSend(substream, msg)); } else { unreachable!("Invalid outbound state") } @@ -355,39 +357,43 @@ impl ProtocolsHandler for RendezvousHandler { debug!("injecting event into handler from behaviour: {:?}", &req); let (inbound, outbound) = match ( req, - mem::replace(&mut self.inbound, InboundState::Poisoned), - mem::replace(&mut self.outbound, OutboundState::Poisoned), + mem::replace(&mut self.inbound, SubstreamState::Poisoned), + mem::replace(&mut self.outbound, SubstreamState::Poisoned), ) { - (InEvent::RegisterRequest { request: reggo }, inbound, OutboundState::None) => { - (inbound, OutboundState::Start(Message::Register(reggo))) - } - (InEvent::UnregisterRequest { namespace }, inbound, OutboundState::None) => ( + (InEvent::RegisterRequest { request: reggo }, inbound, SubstreamState::None) => ( + inbound, + SubstreamState::Active(Outbound::Start(Message::Register(reggo))), + ), + (InEvent::UnregisterRequest { namespace }, inbound, SubstreamState::None) => ( inbound, - OutboundState::Start(Message::Unregister { namespace }), + SubstreamState::Active(Outbound::Start(Message::Unregister { namespace })), ), - (InEvent::DiscoverRequest { namespace }, inbound, OutboundState::None) => ( + (InEvent::DiscoverRequest { namespace }, inbound, SubstreamState::None) => ( inbound, - OutboundState::Start(Message::Discover { namespace }), + SubstreamState::Active(Outbound::Start(Message::Discover { namespace })), ), ( InEvent::RegisterResponse { ttl }, - InboundState::WaitForBehaviour(substream), + SubstreamState::Active(Inbound::WaitForBehaviour(substream)), outbound, ) => ( - InboundState::PendingSend(substream, Message::RegisterResponse { ttl }), + SubstreamState::Active(Inbound::PendingSend( + substream, + Message::RegisterResponse { ttl }, + )), outbound, ), ( InEvent::DiscoverResponse { discovered }, - InboundState::WaitForBehaviour(substream), + SubstreamState::Active(Inbound::WaitForBehaviour(substream)), outbound, ) => ( - InboundState::PendingSend( + SubstreamState::Active(Inbound::PendingSend( substream, Message::DiscoverResponse { registrations: discovered, }, - ), + )), outbound, ), _ => unreachable!("Handler in invalid state"), @@ -436,31 +442,27 @@ impl ProtocolsHandler for RendezvousHandler { } } -impl Debug for OutboundState { +impl Debug for Outbound { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - OutboundState::None => f.write_str("none"), - OutboundState::Start(_) => f.write_str("start"), - OutboundState::WaitingUpgrade => f.write_str("waiting_upgrade"), - OutboundState::PendingSend(_, _) => f.write_str("pending_send"), - OutboundState::PendingFlush(_) => f.write_str("pending_flush"), - OutboundState::WaitForRemote(_) => f.write_str("waiting_for_remote"), - OutboundState::Closing(_) => f.write_str("closing"), - OutboundState::Poisoned => f.write_str("poisoned"), + Outbound::Start(_) => f.write_str("start"), + Outbound::WaitingUpgrade => f.write_str("waiting_upgrade"), + Outbound::PendingSend(_, _) => f.write_str("pending_send"), + Outbound::PendingFlush(_) => f.write_str("pending_flush"), + Outbound::WaitForRemote(_) => f.write_str("waiting_for_remote"), + Outbound::Closing(_) => f.write_str("closing"), } } } -impl Debug for InboundState { +impl Debug for Inbound { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - InboundState::None => f.write_str("none"), - InboundState::Reading(_) => f.write_str("reading"), - InboundState::PendingSend(_, _) => f.write_str("pending_send"), - InboundState::PendingFlush(_) => f.write_str("pending_flush"), - InboundState::WaitForBehaviour(_) => f.write_str("waiting_for_behaviour"), - InboundState::Closing(_) => f.write_str("closing"), - InboundState::Poisoned => f.write_str("poisoned"), + Inbound::Reading(_) => f.write_str("reading"), + Inbound::PendingSend(_, _) => f.write_str("pending_send"), + Inbound::PendingFlush(_) => f.write_str("pending_flush"), + Inbound::WaitForBehaviour(_) => f.write_str("waiting_for_behaviour"), + Inbound::Closing(_) => f.write_str("closing"), } } } From 5af486ed5a9b508fa40ee02b754df852f14ca81a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 17:43:24 +1000 Subject: [PATCH 077/242] Use module-level public constructor for making new protocol --- protocols/rendezvous/src/handler.rs | 5 ++--- protocols/rendezvous/src/protocol.rs | 6 +++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index ef1e0913850..413e9cb159a 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,6 +1,5 @@ use crate::codec::{Message, Registration}; use crate::codec::{NewRegistration, RendezvousCodec}; -use crate::protocol::Rendezvous; use crate::{codec, protocol}; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; @@ -238,7 +237,7 @@ impl Advance for Outbound { match self { Outbound::Start(msg) => Next::Return { poll: Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(Rendezvous, msg), + protocol: SubstreamProtocol::new(protocol::new(), msg), }), next_state: Outbound::WaitingUpgrade, }, @@ -323,7 +322,7 @@ impl ProtocolsHandler for RendezvousHandler { fn listen_protocol(&self) -> SubstreamProtocol { debug!("creating substream protocol"); - SubstreamProtocol::new(protocol::Rendezvous::new(), ()) + SubstreamProtocol::new(protocol::new(), ()) } fn inject_fully_negotiated_inbound( diff --git a/protocols/rendezvous/src/protocol.rs b/protocols/rendezvous/src/protocol.rs index 6b596a5464d..e88262570ef 100644 --- a/protocols/rendezvous/src/protocol.rs +++ b/protocols/rendezvous/src/protocol.rs @@ -7,11 +7,15 @@ use log::debug; use std::{future, iter}; use void::Void; +pub fn new() -> Rendezvous { + Rendezvous::new() +} + #[derive(Default, Debug, Copy, Clone)] pub struct Rendezvous; impl Rendezvous { - pub fn new() -> Rendezvous { + fn new() -> Rendezvous { Rendezvous } } From 581cee0c8fb77e26ac885b047abf9e7e801a17dc Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 8 Jun 2021 17:48:58 +1000 Subject: [PATCH 078/242] Implement rendezvous protocol using `upgrade::from_fn` --- protocols/rendezvous/src/protocol.rs | 66 +++++++++------------------- 1 file changed, 21 insertions(+), 45 deletions(-) diff --git a/protocols/rendezvous/src/protocol.rs b/protocols/rendezvous/src/protocol.rs index e88262570ef..c3b4c6b60bc 100644 --- a/protocols/rendezvous/src/protocol.rs +++ b/protocols/rendezvous/src/protocol.rs @@ -1,52 +1,28 @@ use crate::codec::RendezvousCodec; use asynchronous_codec::Framed; -use futures::AsyncRead; -use futures::AsyncWrite; -use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; -use log::debug; -use std::{future, iter}; +use libp2p_core::upgrade::FromFnUpgrade; +use libp2p_core::{upgrade, Endpoint}; +use libp2p_swarm::NegotiatedSubstream; +use std::future; +use upgrade::from_fn; use void::Void; pub fn new() -> Rendezvous { - Rendezvous::new() + from_fn( + b"/rendezvous/1.0.0", + Box::new(|socket, _| future::ready(Ok(Framed::new(socket, RendezvousCodec::default())))), + ) } -#[derive(Default, Debug, Copy, Clone)] -pub struct Rendezvous; - -impl Rendezvous { - fn new() -> Rendezvous { - Rendezvous - } -} - -impl UpgradeInfo for Rendezvous { - type Info = &'static [u8]; - type InfoIter = iter::Once; - - fn protocol_info(&self) -> Self::InfoIter { - iter::once(b"/rendezvous/1.0.0") - } -} - -impl InboundUpgrade for Rendezvous { - type Output = Framed; - type Error = Void; - type Future = future::Ready>; - - fn upgrade_inbound(self, socket: TSocket, _: Self::Info) -> Self::Future { - debug!("upgrading inbound"); - future::ready(Ok(Framed::new(socket, RendezvousCodec::default()))) - } -} - -impl OutboundUpgrade for Rendezvous { - type Output = Framed; - type Error = Void; - type Future = future::Ready>; - - fn upgrade_outbound(self, socket: TSocket, _: Self::Info) -> Self::Future { - debug!("upgrading outbound"); - future::ready(Ok(Framed::new(socket, RendezvousCodec::default()))) - } -} +pub type Rendezvous = FromFnUpgrade< + &'static [u8], + Box< + dyn Fn( + NegotiatedSubstream, + Endpoint, + ) + -> future::Ready, Void>> + + Send + + 'static, + >, +>; From 43c8753d26af9a406c30aed2489cba49e6e4d4a0 Mon Sep 17 00:00:00 2001 From: rishflab Date: Wed, 9 Jun 2021 16:32:21 +1000 Subject: [PATCH 079/242] Return a hashmap of registrations instead of a vector A hashmap of (namespace, peer_id) ensures there will be no duplicate registration records. It does not make sense for there to be duplicate registration records. A Hashset would of been nice but then we would have to implement Hash etc on various types. --- protocols/rendezvous/src/behaviour.rs | 37 ++++++++++++++------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index ff6b5f1f2b2..0a0478c36d4 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -76,11 +76,11 @@ impl Rendezvous { pub enum Event { Discovered { rendezvous_node: PeerId, - registrations: Vec, + registrations: HashMap<(String, PeerId), Registration>, }, AnsweredDiscoverRequest { enquirer: PeerId, - registrations: Vec, + registrations: HashMap<(String, PeerId), Registration>, }, FailedToDiscover { rendezvous_node: PeerId, @@ -176,14 +176,14 @@ impl NetworkBehaviour for Rendezvous { // TODO: Should send unregister response? } Message::Discover { namespace } => { - let registrations = self.registrations.get(namespace).unwrap_or_default(); + let registrations = self.registrations.get(namespace); self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, event: InEvent::DiscoverResponse { - discovered: registrations.clone(), + discovered: registrations.values().cloned().collect(), }, }); self.events.push_back(NetworkBehaviourAction::GenerateEvent( @@ -197,7 +197,10 @@ impl NetworkBehaviour for Rendezvous { self.events .push_back(NetworkBehaviourAction::GenerateEvent(Event::Discovered { rendezvous_node: peer_id, - registrations, + registrations: registrations + .iter() + .map(|r| ((r.namespace.clone(), r.record.peer_id()), r.clone())) + .collect(), })) } Message::FailedToDiscover { error } => self.events.push_back( @@ -268,21 +271,19 @@ impl Registrations { } } - pub fn get(&mut self, namespace: Option) -> Option> { + pub fn get(&mut self, namespace: Option) -> HashMap<(String, PeerId), Registration> { if self.registrations_for_namespace.is_empty() { - return None; + return HashMap::new(); } if let Some(namespace) = namespace { if let Some(registrations) = self.registrations_for_namespace.get(&namespace) { - Some( - registrations - .values() - .cloned() - .collect::>(), - ) + registrations + .values() + .map(|r| ((r.namespace.clone(), r.record.peer_id()), r.clone())) + .collect::>() } else { - None + HashMap::new() } } else { let discovered = self @@ -291,13 +292,13 @@ impl Registrations { .map(|(_, registrations)| { registrations .values() - .cloned() - .collect::>() + .map(|r| ((r.namespace.clone(), r.record.peer_id()), r.clone())) + .collect::>() }) .flatten() - .collect::>(); + .collect::>(); - Some(discovered) + discovered } } } From 344123f53b9f3e04e312cc1c9c19c1c1605f1838 Mon Sep 17 00:00:00 2001 From: rishflab Date: Wed, 9 Jun 2021 17:19:37 +1000 Subject: [PATCH 080/242] Allow registration swarm to specify TTL in registration request Assert that the TTL is correct in the tests. --- protocols/rendezvous/src/behaviour.rs | 4 +- protocols/rendezvous/src/codec.rs | 2 +- protocols/rendezvous/tests/rendezvous.rs | 47 ++++++++++++++++++++---- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 0a0478c36d4..33c9638773a 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -29,7 +29,7 @@ impl Rendezvous { } // TODO: Make it possible to filter for specific external-addresses (like onion addresses-only f.e.) - pub fn register(&mut self, namespace: String, rendezvous_node: PeerId) { + pub fn register(&mut self, namespace: String, rendezvous_node: PeerId, ttl: Option) { let authenticated_peer_record = AuthenticatedPeerRecord::from_record( self.key_pair.clone(), PeerRecord { @@ -46,7 +46,7 @@ impl Rendezvous { request: NewRegistration { namespace, record: authenticated_peer_record, - ttl: None, + ttl, }, }, handler: NotifyHandler::Any, diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index b1c547ce1eb..e92a622adb0 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -40,7 +40,7 @@ pub struct NewRegistration { /// If unspecified, rendezvous nodes should assume a TTL of 2h. /// /// See https://github.com/libp2p/specs/blob/d21418638d5f09f2a4e5a1ceca17058df134a300/rendezvous/README.md#L116-L117. -const DEFAULT_TTL: i64 = 60 * 60 * 2; +pub const DEFAULT_TTL: i64 = 60 * 60 * 2; impl NewRegistration { pub fn new(namespace: String, record: AuthenticatedPeerRecord, ttl: Option) -> Self { diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 7e5b9b293e7..377371c3813 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,7 +1,9 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; +use libp2p_core::PeerId; use libp2p_rendezvous::behaviour::{Event, Rendezvous}; +use libp2p_rendezvous::codec::DEFAULT_TTL; use libp2p_swarm::Swarm; #[tokio::test] @@ -14,15 +16,23 @@ async fn given_successful_registration_then_successful_discovery() { test.registration_swarm.behaviour_mut().register( namespace.clone(), test.rendezvous_swarm.local_peer_id().clone(), + None, ); - test.assert_successful_registration(namespace.clone()).await; + + test.assert_successful_registration(namespace.clone(), DEFAULT_TTL) + .await; test.discovery_swarm.behaviour_mut().discover( - Some(namespace), + Some(namespace.clone()), test.rendezvous_swarm.local_peer_id().clone(), ); - test.assert_successful_discovery().await; + test.assert_successful_discovery( + namespace.clone(), + DEFAULT_TTL, + test.registration_swarm.local_peer_id().clone(), + ) + .await; } struct RendezvousTest { @@ -56,15 +66,20 @@ impl RendezvousTest { } } - pub async fn assert_successful_registration(&mut self, expected_namespace: String) { + pub async fn assert_successful_registration( + &mut self, + expected_namespace: String, + expected_ttl: i64, + ) { match await_events_or_timeout(self.rendezvous_swarm.next(), self.registration_swarm.next()).await { ( Event::PeerRegistered { peer, namespace }, - Event::RegisteredWithRendezvousNode { rendezvous_node, .. }, + Event::RegisteredWithRendezvousNode { rendezvous_node, ttl}, ) => { assert_eq!(&peer, self.registration_swarm.local_peer_id()); assert_eq!(&rendezvous_node, self.rendezvous_swarm.local_peer_id()); assert_eq!(namespace, expected_namespace); + assert_eq!(ttl, expected_ttl); } (rendezvous_swarm_event, registration_swarm_event) => panic!( "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", @@ -73,11 +88,29 @@ impl RendezvousTest { } } - pub async fn assert_successful_discovery(&mut self) { + pub async fn assert_successful_discovery( + &mut self, + expected_namespace: String, + expected_ttl: i64, + expected_peer_id: PeerId, + ) { match await_events_or_timeout(self.rendezvous_swarm.next(), self.discovery_swarm.next()) .await { - (Event::AnsweredDiscoverRequest { .. }, Event::Discovered { .. }) => {} + (Event::AnsweredDiscoverRequest { .. }, Event::Discovered { registrations, .. }) => { + if let Some(reg) = + registrations.get(&(expected_namespace.clone(), expected_peer_id)) + { + assert_eq!(reg.ttl, expected_ttl) + } else { + { + panic!( + "Registration with namespace {} and peer id {} not found", + expected_namespace, expected_peer_id + ) + } + } + } (e1, e2) => panic!("Unexpected events {:?} {:?}", e1, e2), } } From faa066c55d41e38c3fe2eb208d6ddbacf16edafa Mon Sep 17 00:00:00 2001 From: rishflab Date: Wed, 9 Jun 2021 17:34:47 +1000 Subject: [PATCH 081/242] Set unix timestamp in peer record --- protocols/rendezvous/src/behaviour.rs | 12 ++++++++++-- protocols/rendezvous/tests/rendezvous.rs | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 33c9638773a..c20679fc2cb 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -10,6 +10,7 @@ use libp2p_swarm::{ use log::debug; use std::collections::{HashMap, VecDeque}; use std::task::{Context, Poll}; +use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH}; pub struct Rendezvous { events: VecDeque>, @@ -29,12 +30,17 @@ impl Rendezvous { } // TODO: Make it possible to filter for specific external-addresses (like onion addresses-only f.e.) - pub fn register(&mut self, namespace: String, rendezvous_node: PeerId, ttl: Option) { + pub fn register( + &mut self, + namespace: String, + rendezvous_node: PeerId, + ttl: Option, + ) -> Result<(), SystemTimeError> { let authenticated_peer_record = AuthenticatedPeerRecord::from_record( self.key_pair.clone(), PeerRecord { peer_id: self.key_pair.public().into_peer_id(), - seq: 0, // TODO: should be current unix timestamp + seq: SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(), addresses: self.external_addresses.clone(), }, ); @@ -51,6 +57,8 @@ impl Rendezvous { }, handler: NotifyHandler::Any, }); + + Ok(()) } pub fn unregister(&mut self, namespace: String, rendezvous_node: PeerId) { diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 377371c3813..ee1ee7a8e54 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -13,7 +13,7 @@ async fn given_successful_registration_then_successful_discovery() { let namespace = "some-namespace".to_string(); - test.registration_swarm.behaviour_mut().register( + let _ = test.registration_swarm.behaviour_mut().register( namespace.clone(), test.rendezvous_swarm.local_peer_id().clone(), None, From 6908659f05d08869a6c0836a805d065cb9093923 Mon Sep 17 00:00:00 2001 From: rishflab Date: Wed, 9 Jun 2021 19:37:57 +1000 Subject: [PATCH 082/242] Attach timestamp to the registration the record The TTL alone is useless to the user. The timestamp allows the user calculate the absolute expiry time without breaking the tests. --- protocols/rendezvous/src/behaviour.rs | 1 + protocols/rendezvous/src/codec.rs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index c20679fc2cb..701f84eacd7 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -267,6 +267,7 @@ impl Registrations { namespace: namespace.clone(), record: new_registration.record, ttl, + timestamp: SystemTime::now(), }, ); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index e92a622adb0..1871f233de2 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -1,6 +1,7 @@ use asynchronous_codec::{Bytes, BytesMut, Decoder, Encoder}; use libp2p_core::{peer_record, signed_envelope, AuthenticatedPeerRecord, SignedEnvelope}; use std::convert::{TryFrom, TryInto}; +use std::time::SystemTime; use unsigned_varint::codec::UviBytes; #[derive(Debug, Clone)] @@ -60,7 +61,8 @@ impl NewRegistration { pub struct Registration { pub namespace: String, pub record: AuthenticatedPeerRecord, - pub ttl: i64, // TODO THEZ: This is useless as a relative value, need registration timestamp, this needs to be a unix timestamp or this is relative in remaining seconds + pub ttl: i64, + pub timestamp: SystemTime, } #[derive(Debug, Clone)] @@ -312,6 +314,7 @@ impl TryFrom for Message { )?, )?, ttl: reggo.ttl.ok_or(ConversionError::MissingTtl)?, + timestamp: SystemTime::now(), }) }) .collect::, ConversionError>>()?, From 5ebb7342d9a784f64ca520f4a05a8c7d862097e4 Mon Sep 17 00:00:00 2001 From: rishflab Date: Wed, 9 Jun 2021 21:55:48 +1000 Subject: [PATCH 083/242] Test refreshing of the TTL by sending a new registration request --- protocols/rendezvous/tests/rendezvous.rs | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index ee1ee7a8e54..09720d5043b 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -35,6 +35,58 @@ async fn given_successful_registration_then_successful_discovery() { .await; } +#[tokio::test] +async fn given_successful_registration_then_refresh_ttl() { + env_logger::init(); + let mut test = RendezvousTest::setup().await; + + let namespace = "some-namespace".to_string(); + + let refesh_ttl = 10_000; + + let _ = test.registration_swarm.behaviour_mut().register( + namespace.clone(), + test.rendezvous_swarm.local_peer_id().clone(), + None, + ); + + test.assert_successful_registration(namespace.clone(), DEFAULT_TTL) + .await; + + test.discovery_swarm.behaviour_mut().discover( + Some(namespace.clone()), + test.rendezvous_swarm.local_peer_id().clone(), + ); + + test.assert_successful_discovery( + namespace.clone(), + DEFAULT_TTL, + test.registration_swarm.local_peer_id().clone(), + ) + .await; + + let _ = test.registration_swarm.behaviour_mut().register( + namespace.clone(), + test.rendezvous_swarm.local_peer_id().clone(), + Some(refesh_ttl), + ); + + test.assert_successful_registration(namespace.clone(), refesh_ttl) + .await; + + test.discovery_swarm.behaviour_mut().discover( + Some(namespace.clone()), + test.rendezvous_swarm.local_peer_id().clone(), + ); + + test.assert_successful_discovery( + namespace.clone(), + refesh_ttl, + test.registration_swarm.local_peer_id().clone(), + ) + .await; +} + struct RendezvousTest { pub registration_swarm: Swarm, pub discovery_swarm: Swarm, From b9dd7d32d78e953dc364fab0c36dba13969c1027 Mon Sep 17 00:00:00 2001 From: rishflab Date: Wed, 9 Jun 2021 22:51:42 +1000 Subject: [PATCH 084/242] Reject register request if specified TTL is too high --- protocols/rendezvous/src/behaviour.rs | 99 +++++++++++++++--------- protocols/rendezvous/src/codec.rs | 2 +- protocols/rendezvous/src/handler.rs | 16 +++- protocols/rendezvous/tests/rendezvous.rs | 40 +++++++++- 4 files changed, 116 insertions(+), 41 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 701f84eacd7..f8ca26c2a44 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -20,10 +20,10 @@ pub struct Rendezvous { } impl Rendezvous { - pub fn new(key_pair: Keypair) -> Self { + pub fn new(key_pair: Keypair, ttl_upper_board: i64) -> Self { Self { events: Default::default(), - registrations: Registrations::new(), + registrations: Registrations::new(ttl_upper_board), key_pair, external_addresses: vec![], } @@ -149,23 +149,36 @@ impl NetworkBehaviour for Rendezvous { ) { match message { Message::Register(new_registration) => { - let (namespace, ttl) = self.registrations.add(new_registration); + if let Ok((namespace, ttl)) = self.registrations.add(new_registration) { + // notify the handler that to send a response + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: InEvent::RegisterResponse { ttl }, + }); - // notify the handler that to send a response - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: InEvent::RegisterResponse { ttl }, - }); + // emit behaviour event + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::PeerRegistered { + peer: peer_id, + namespace, + }, + )); + } else { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: InEvent::DeclineRegisterRequest { + error: ErrorCode::InvalidTtl, + }, + }); - // emit behaviour event - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::PeerRegistered { - peer: peer_id, - namespace, - }, - )); + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::DeclinedRegisterRequest { peer: peer_id }, + )); + } } Message::RegisterResponse { ttl } => self.events.push_back( NetworkBehaviourAction::GenerateEvent(Event::RegisteredWithRendezvousNode { @@ -245,33 +258,49 @@ impl NetworkBehaviour for Rendezvous { // TODO: Unit Tests pub struct Registrations { registrations_for_namespace: HashMap>, + ttl_upper_bound: i64, +} + +#[derive(Debug, thiserror::Error)] +pub enum RegistrationError { + #[error("Requested TTL: {requested} is greater than upper bound: {upper_bound} ")] + TTLGreaterThanUpperBound { upper_bound: i64, requested: i64 }, } impl Registrations { - pub fn new() -> Self { + pub fn new(ttl_upper_bound: i64) -> Self { Self { registrations_for_namespace: Default::default(), + ttl_upper_bound, } } - pub fn add(&mut self, new_registration: NewRegistration) -> (String, i64) { + pub fn add( + &mut self, + new_registration: NewRegistration, + ) -> Result<(String, i64), RegistrationError> { let ttl = new_registration.effective_ttl(); - let namespace = new_registration.namespace; - - self.registrations_for_namespace - .entry(namespace.clone()) - .or_insert_with(|| HashMap::new()) - .insert( - new_registration.record.peer_id(), - Registration { - namespace: namespace.clone(), - record: new_registration.record, - ttl, - timestamp: SystemTime::now(), - }, - ); - - (namespace, ttl) + if ttl < self.ttl_upper_bound { + let namespace = new_registration.namespace; + self.registrations_for_namespace + .entry(namespace.clone()) + .or_insert_with(|| HashMap::new()) + .insert( + new_registration.record.peer_id(), + Registration { + namespace: namespace.clone(), + record: new_registration.record, + ttl, + timestamp: SystemTime::now(), + }, + ); + Ok((namespace, ttl)) + } else { + Err(RegistrationError::TTLGreaterThanUpperBound { + upper_bound: self.ttl_upper_bound, + requested: ttl, + }) + } } pub fn remove(&mut self, namespace: String, peer_id: PeerId) { diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 1871f233de2..462ebcd4804 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -65,7 +65,7 @@ pub struct Registration { pub timestamp: SystemTime, } -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ErrorCode { InvalidNamespace, InvalidSignedPeerRecord, diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 413e9cb159a..1a63b48fb8f 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,4 +1,4 @@ -use crate::codec::{Message, Registration}; +use crate::codec::{ErrorCode, Message, Registration}; use crate::codec::{NewRegistration, RendezvousCodec}; use crate::{codec, protocol}; use asynchronous_codec::Framed; @@ -39,6 +39,9 @@ pub enum InEvent { RegisterRequest { request: NewRegistration, }, + DeclineRegisterRequest { + error: ErrorCode, + }, UnregisterRequest { namespace: String, // TODO: what is the `id` field here in the PB message @@ -382,6 +385,17 @@ impl ProtocolsHandler for RendezvousHandler { )), outbound, ), + ( + InEvent::DeclineRegisterRequest { error }, + SubstreamState::Active(Inbound::WaitForBehaviour(substream)), + outbound, + ) => ( + SubstreamState::Active(Inbound::PendingSend( + substream, + Message::FailedToRegister { error }, + )), + outbound, + ), ( InEvent::DiscoverResponse { discovered }, SubstreamState::Active(Inbound::WaitForBehaviour(substream)), diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 09720d5043b..7a1c8192c51 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -3,7 +3,7 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use libp2p_core::PeerId; use libp2p_rendezvous::behaviour::{Event, Rendezvous}; -use libp2p_rendezvous::codec::DEFAULT_TTL; +use libp2p_rendezvous::codec::{ErrorCode, DEFAULT_TTL}; use libp2p_swarm::Swarm; #[tokio::test] @@ -87,21 +87,53 @@ async fn given_successful_registration_then_refresh_ttl() { .await; } +#[tokio::test] +async fn given_invalid_ttl_then_unsuccessful_registration() { + env_logger::init(); + let mut test = RendezvousTest::setup().await; + + let namespace = "some-namespace".to_string(); + + let _ = test.registration_swarm.behaviour_mut().register( + namespace.clone(), + test.rendezvous_swarm.local_peer_id().clone(), + Some(100_000), + ); + + match await_events_or_timeout(test.rendezvous_swarm.next(), test.registration_swarm.next()).await { + ( + Event::DeclinedRegisterRequest { .. }, + Event::FailedToRegisterWithRendezvousNode { err_code, .. }, + ) => { + assert_eq!(err_code, ErrorCode::InvalidTtl); + } + (rendezvous_swarm_event, registration_swarm_event) => panic!( + "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", + rendezvous_swarm_event, registration_swarm_event + ), + } +} + struct RendezvousTest { pub registration_swarm: Swarm, pub discovery_swarm: Swarm, pub rendezvous_swarm: Swarm, } +const DEFAULT_TTL_UPPER_BOUND: i64 = 56_000; + impl RendezvousTest { pub async fn setup() -> Self { - let mut registration_swarm = new_swarm(|_, identity| Rendezvous::new(identity)); + let mut registration_swarm = + new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); registration_swarm.listen_on_random_memory_address().await; - let mut discovery_swarm = new_swarm(|_, identity| Rendezvous::new(identity)); + let mut discovery_swarm = + new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); discovery_swarm.listen_on_random_memory_address().await; - let mut rendezvous_swarm = new_swarm(|_, identity| Rendezvous::new(identity)); + let mut rendezvous_swarm = + new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); rendezvous_swarm.listen_on_random_memory_address().await; registration_swarm From 0d4bd9fb00db789ade9109638737b288b67908da Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 10:06:42 +1000 Subject: [PATCH 085/242] Add failing test for cookie behaviour --- protocols/rendezvous/src/behaviour.rs | 52 ++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index f8ca26c2a44..65c81d43b5f 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -197,7 +197,7 @@ impl NetworkBehaviour for Rendezvous { // TODO: Should send unregister response? } Message::Discover { namespace } => { - let registrations = self.registrations.get(namespace); + let (registrations, _) = self.registrations.get(namespace, None); self.events .push_back(NetworkBehaviourAction::NotifyHandler { @@ -255,6 +255,8 @@ impl NetworkBehaviour for Rendezvous { } } +pub struct Cookie {} + // TODO: Unit Tests pub struct Registrations { registrations_for_namespace: HashMap>, @@ -309,12 +311,16 @@ impl Registrations { } } - pub fn get(&mut self, namespace: Option) -> HashMap<(String, PeerId), Registration> { + pub fn get( + &mut self, + namespace: Option, + _: Option, + ) -> (HashMap<(String, PeerId), Registration>, Cookie) { if self.registrations_for_namespace.is_empty() { - return HashMap::new(); + return (HashMap::new(), Cookie {}); } - if let Some(namespace) = namespace { + let discovered = if let Some(namespace) = namespace { if let Some(registrations) = self.registrations_for_namespace.get(&namespace) { registrations .values() @@ -337,6 +343,42 @@ impl Registrations { .collect::>(); discovered - } + }; + + (discovered, Cookie {}) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use libp2p_core::identity; + + #[test] + fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { + let mut registrations = Registrations::new(); + registrations.add(new_dummy_registration("foo")); + registrations.add(new_dummy_registration("foo")); + + let (initial_discover, cookie) = registrations.get(None, None); + let (subsequent_discover, _) = registrations.get(None, Some(cookie)); + + assert_eq!(initial_discover.len(), 2); + assert_eq!(subsequent_discover.len(), 0); + } + + fn new_dummy_registration(namespace: &str) -> NewRegistration { + let identity = identity::Keypair::generate_ed25519(); + let record = PeerRecord { + peer_id: identity.public().into_peer_id(), + seq: 0, + addresses: vec!["/ip4/127.0.0.1/tcp/1234".parse().unwrap()], + }; + + NewRegistration::new( + namespace.to_owned(), + AuthenticatedPeerRecord::from_record(identity, record), + None, + ) } } From e1fcdd12dcda023d9039dcc11a7cabdded3e7bf3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 10:12:36 +1000 Subject: [PATCH 086/242] Add basic tests for Registrations behaviour --- protocols/rendezvous/src/behaviour.rs | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 65c81d43b5f..516231b6f22 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -367,6 +367,36 @@ mod tests { assert_eq!(subsequent_discover.len(), 0); } + #[test] + fn given_registrations_when_discover_all_then_all_are_returned() { + let mut registrations = Registrations::new(); + registrations.add(new_dummy_registration("foo")); + registrations.add(new_dummy_registration("foo")); + + let (discover, _) = registrations.get(None, None); + + assert_eq!(discover.len(), 2); + } + + #[test] + fn given_registrations_when_discover_only_for_specific_namespace_then_only_those_are_returned() + { + let mut registrations = Registrations::new(); + registrations.add(new_dummy_registration("foo")); + registrations.add(new_dummy_registration("bar")); + + let (discover, _) = registrations.get(Some("foo".to_owned()), None); + + assert_eq!(discover.len(), 1); + assert_eq!( + discover + .values() + .map(|r| r.namespace.as_str()) + .collect::>(), + vec!["foo"] + ); + } + fn new_dummy_registration(namespace: &str) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); let record = PeerRecord { From a92be312e1226f53554bf3d81b43f4304738d6a6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 10:22:26 +1000 Subject: [PATCH 087/242] Refactor `Registrations::get` to return Iterator --- protocols/rendezvous/src/behaviour.rs | 68 ++++++++++----------------- 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 516231b6f22..8612dd744b6 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -88,7 +88,7 @@ pub enum Event { }, AnsweredDiscoverRequest { enquirer: PeerId, - registrations: HashMap<(String, PeerId), Registration>, + registrations: Vec, }, FailedToDiscover { rendezvous_node: PeerId, @@ -199,18 +199,20 @@ impl NetworkBehaviour for Rendezvous { Message::Discover { namespace } => { let (registrations, _) = self.registrations.get(namespace, None); + let discovered = registrations.collect::>(); + self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, event: InEvent::DiscoverResponse { - discovered: registrations.values().cloned().collect(), + discovered: discovered.clone(), }, }); self.events.push_back(NetworkBehaviourAction::GenerateEvent( Event::AnsweredDiscoverRequest { enquirer: peer_id, - registrations, + registrations: discovered, }, )); } @@ -313,37 +315,23 @@ impl Registrations { pub fn get( &mut self, - namespace: Option, + discover_namespace: Option, _: Option, - ) -> (HashMap<(String, PeerId), Registration>, Cookie) { - if self.registrations_for_namespace.is_empty() { - return (HashMap::new(), Cookie {}); - } - - let discovered = if let Some(namespace) = namespace { - if let Some(registrations) = self.registrations_for_namespace.get(&namespace) { - registrations - .values() - .map(|r| ((r.namespace.clone(), r.record.peer_id()), r.clone())) - .collect::>() - } else { - HashMap::new() - } - } else { - let discovered = self - .registrations_for_namespace - .iter() - .map(|(_, registrations)| { - registrations - .values() - .map(|r| ((r.namespace.clone(), r.record.peer_id()), r.clone())) - .collect::>() - }) - .flatten() - .collect::>(); - - discovered - }; + ) -> (impl Iterator + '_, Cookie) { + let discovered = self + .registrations_for_namespace + .iter() + .filter_map( + move |(namespace, registrations)| match discover_namespace.as_ref() { + Some(discover_namespace) if discover_namespace == namespace => { + Some(registrations.values()) + } + Some(_) => None, + None => Some(registrations.values()), + }, + ) + .flatten() + .cloned(); (discovered, Cookie {}) } @@ -361,10 +349,10 @@ mod tests { registrations.add(new_dummy_registration("foo")); let (initial_discover, cookie) = registrations.get(None, None); - let (subsequent_discover, _) = registrations.get(None, Some(cookie)); + assert_eq!(initial_discover.collect::>().len(), 2); - assert_eq!(initial_discover.len(), 2); - assert_eq!(subsequent_discover.len(), 0); + let (subsequent_discover, _) = registrations.get(None, Some(cookie)); + assert_eq!(subsequent_discover.collect::>().len(), 0); } #[test] @@ -375,7 +363,7 @@ mod tests { let (discover, _) = registrations.get(None, None); - assert_eq!(discover.len(), 2); + assert_eq!(discover.collect::>().len(), 2); } #[test] @@ -387,12 +375,8 @@ mod tests { let (discover, _) = registrations.get(Some("foo".to_owned()), None); - assert_eq!(discover.len(), 1); assert_eq!( - discover - .values() - .map(|r| r.namespace.as_str()) - .collect::>(), + discover.map(|r| r.namespace).collect::>(), vec!["foo"] ); } From 89348dcd51373f9ba1a17d72752bb0008f391acc Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 10:35:23 +1000 Subject: [PATCH 088/242] Add test for overwriting existing registration --- protocols/rendezvous/src/behaviour.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 8612dd744b6..a99efebe9d1 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -381,8 +381,28 @@ mod tests { ); } + #[test] + fn given_reregistration_old_registration_is_discarded() { + let alice = identity::Keypair::generate_ed25519(); + let mut registrations = Registrations::new(); + registrations.add(new_registration("foo", alice.clone())); + registrations.add(new_registration("foo", alice)); + + let (discover, _) = registrations.get(Some("foo".to_owned()), None); + + assert_eq!( + discover.map(|r| r.namespace).collect::>(), + vec!["foo"] + ); + } + fn new_dummy_registration(namespace: &str) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); + + new_registration(namespace, identity) + } + + fn new_registration(namespace: &str, identity: identity::Keypair) -> NewRegistration { let record = PeerRecord { peer_id: identity.public().into_peer_id(), seq: 0, From edeb5b2c2d5e2c046629612a76848ec46f7da7e7 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 11:23:56 +1000 Subject: [PATCH 089/242] Store registrations by ID --- protocols/rendezvous/Cargo.toml | 1 + protocols/rendezvous/src/behaviour.rs | 104 ++++++++++++++++---------- 2 files changed, 67 insertions(+), 38 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index a1c29dca961..43f0d294f7c 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -19,6 +19,7 @@ log = "0.4" futures = { version = "0.3", default-features = false, features = ["std"] } thiserror = "1" # TODO: REMOVE FOR PRODUCTION USE unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } +uuid = { version = "0.8", features = ["v4"] } [dev-dependencies] libp2p-tcp = { path = "../../transports/tcp" } diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index a99efebe9d1..1e7544df9de 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -8,9 +8,11 @@ use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; use log::debug; +use std::collections::hash_map::Entry; use std::collections::{HashMap, VecDeque}; use std::task::{Context, Poll}; use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH}; +use uuid::Uuid; pub struct Rendezvous { events: VecDeque>, @@ -261,7 +263,8 @@ pub struct Cookie {} // TODO: Unit Tests pub struct Registrations { - registrations_for_namespace: HashMap>, + registrations_for_peer: HashMap<(PeerId, String), Uuid>, + registrations: HashMap, ttl_upper_bound: i64, } @@ -274,7 +277,8 @@ pub enum RegistrationError { impl Registrations { pub fn new(ttl_upper_bound: i64) -> Self { Self { - registrations_for_namespace: Default::default(), + registrations_for_peer: Default::default(), + registrations: Default::default(), ttl_upper_bound, } } @@ -286,18 +290,31 @@ impl Registrations { let ttl = new_registration.effective_ttl(); if ttl < self.ttl_upper_bound { let namespace = new_registration.namespace; - self.registrations_for_namespace - .entry(namespace.clone()) - .or_insert_with(|| HashMap::new()) - .insert( - new_registration.record.peer_id(), - Registration { - namespace: namespace.clone(), - record: new_registration.record, - ttl, - timestamp: SystemTime::now(), - }, - ); + let registration_id = Uuid::new_v4(); + + match self + .registrations_for_peer + .entry((new_registration.record.peer_id(), namespace.clone())) + { + Entry::Occupied(mut occupied) => { + let old_registration = occupied.insert(registration_id); + + self.registrations.remove(&old_registration); + } + Entry::Vacant(vacant) => { + vacant.insert(registration_id); + } + } + + self.registrations.insert( + registration_id, + Registration { + namespace: namespace.clone(), + record: new_registration.record, + ttl, + timestamp: SystemTime::now(), + }, + ); Ok((namespace, ttl)) } else { Err(RegistrationError::TTLGreaterThanUpperBound { @@ -308,8 +325,10 @@ impl Registrations { } pub fn remove(&mut self, namespace: String, peer_id: PeerId) { - if let Some(registrations) = self.registrations_for_namespace.get_mut(&namespace) { - registrations.remove(&peer_id); + let reggo_to_remove = self.registrations_for_peer.remove(&(peer_id, namespace)); + + if let Some(reggo_to_remove) = reggo_to_remove { + self.registrations.remove(®go_to_remove); } } @@ -319,18 +338,25 @@ impl Registrations { _: Option, ) -> (impl Iterator + '_, Cookie) { let discovered = self - .registrations_for_namespace + .registrations_for_peer .iter() - .filter_map( - move |(namespace, registrations)| match discover_namespace.as_ref() { - Some(discover_namespace) if discover_namespace == namespace => { - Some(registrations.values()) + .filter_map({ + let reggos = &self.registrations; + + move |((_, namespace), registration_id)| { + let registration = reggos + .get(registration_id) + .expect("bad internal data structure"); + + match discover_namespace.as_ref() { + Some(discover_namespace) if discover_namespace == namespace => { + Some(registration) + } + Some(_) => None, + None => Some(registration), } - Some(_) => None, - None => Some(registrations.values()), - }, - ) - .flatten() + } + }) .cloned(); (discovered, Cookie {}) @@ -344,9 +370,9 @@ mod tests { #[test] fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { - let mut registrations = Registrations::new(); - registrations.add(new_dummy_registration("foo")); - registrations.add(new_dummy_registration("foo")); + let mut registrations = Registrations::new(7201); + registrations.add(new_dummy_registration("foo")).unwrap(); + registrations.add(new_dummy_registration("foo")).unwrap(); let (initial_discover, cookie) = registrations.get(None, None); assert_eq!(initial_discover.collect::>().len(), 2); @@ -357,9 +383,9 @@ mod tests { #[test] fn given_registrations_when_discover_all_then_all_are_returned() { - let mut registrations = Registrations::new(); - registrations.add(new_dummy_registration("foo")); - registrations.add(new_dummy_registration("foo")); + let mut registrations = Registrations::new(7201); + registrations.add(new_dummy_registration("foo")).unwrap(); + registrations.add(new_dummy_registration("foo")).unwrap(); let (discover, _) = registrations.get(None, None); @@ -369,9 +395,9 @@ mod tests { #[test] fn given_registrations_when_discover_only_for_specific_namespace_then_only_those_are_returned() { - let mut registrations = Registrations::new(); - registrations.add(new_dummy_registration("foo")); - registrations.add(new_dummy_registration("bar")); + let mut registrations = Registrations::new(7201); + registrations.add(new_dummy_registration("foo")).unwrap(); + registrations.add(new_dummy_registration("bar")).unwrap(); let (discover, _) = registrations.get(Some("foo".to_owned()), None); @@ -384,9 +410,11 @@ mod tests { #[test] fn given_reregistration_old_registration_is_discarded() { let alice = identity::Keypair::generate_ed25519(); - let mut registrations = Registrations::new(); - registrations.add(new_registration("foo", alice.clone())); - registrations.add(new_registration("foo", alice)); + let mut registrations = Registrations::new(7201); + registrations + .add(new_registration("foo", alice.clone())) + .unwrap(); + registrations.add(new_registration("foo", alice)).unwrap(); let (discover, _) = registrations.get(Some("foo".to_owned()), None); From 3a62bee6793ca7fa23737dbead65f01f9db1ab5b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 11:26:05 +1000 Subject: [PATCH 090/242] Don't return namespace from `Registrations::add` We pass the namespace in, there is no need to return it. --- protocols/rendezvous/src/behaviour.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 1e7544df9de..19dcc025112 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -151,13 +151,15 @@ impl NetworkBehaviour for Rendezvous { ) { match message { Message::Register(new_registration) => { - if let Ok((namespace, ttl)) = self.registrations.add(new_registration) { + let namespace = new_registration.namespace.clone(); + + if let Ok(effective_ttl) = self.registrations.add(new_registration) { // notify the handler that to send a response self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: InEvent::RegisterResponse { ttl }, + event: InEvent::RegisterResponse { ttl: effective_ttl }, }); // emit behaviour event @@ -283,10 +285,7 @@ impl Registrations { } } - pub fn add( - &mut self, - new_registration: NewRegistration, - ) -> Result<(String, i64), RegistrationError> { + pub fn add(&mut self, new_registration: NewRegistration) -> Result { let ttl = new_registration.effective_ttl(); if ttl < self.ttl_upper_bound { let namespace = new_registration.namespace; @@ -315,7 +314,7 @@ impl Registrations { timestamp: SystemTime::now(), }, ); - Ok((namespace, ttl)) + Ok(ttl) } else { Err(RegistrationError::TTLGreaterThanUpperBound { upper_bound: self.ttl_upper_bound, From 5349d9383542149bf58e1bb7c2833d55966d4826 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 11:31:44 +1000 Subject: [PATCH 091/242] Create newtype for registration id --- protocols/rendezvous/src/behaviour.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 19dcc025112..31942aad42d 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -263,10 +263,19 @@ impl NetworkBehaviour for Rendezvous { pub struct Cookie {} +#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] +struct RegistrationId(Uuid); + +impl RegistrationId { + fn new() -> Self { + Self(Uuid::new_v4()) + } +} + // TODO: Unit Tests pub struct Registrations { - registrations_for_peer: HashMap<(PeerId, String), Uuid>, - registrations: HashMap, + registrations_for_peer: HashMap<(PeerId, String), RegistrationId>, + registrations: HashMap, ttl_upper_bound: i64, } @@ -289,7 +298,7 @@ impl Registrations { let ttl = new_registration.effective_ttl(); if ttl < self.ttl_upper_bound { let namespace = new_registration.namespace; - let registration_id = Uuid::new_v4(); + let registration_id = RegistrationId::new(); match self .registrations_for_peer From 4c28f11b4b0689f0a7df79bf41b70bbe829815a1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 11:34:35 +1000 Subject: [PATCH 092/242] Don't clone registrations unless necessary --- protocols/rendezvous/src/behaviour.rs | 42 ++++++++++++--------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 31942aad42d..c03f391a652 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -203,7 +203,7 @@ impl NetworkBehaviour for Rendezvous { Message::Discover { namespace } => { let (registrations, _) = self.registrations.get(namespace, None); - let discovered = registrations.collect::>(); + let discovered = registrations.cloned().collect::>(); self.events .push_back(NetworkBehaviourAction::NotifyHandler { @@ -344,28 +344,24 @@ impl Registrations { &mut self, discover_namespace: Option, _: Option, - ) -> (impl Iterator + '_, Cookie) { - let discovered = self - .registrations_for_peer - .iter() - .filter_map({ - let reggos = &self.registrations; - - move |((_, namespace), registration_id)| { - let registration = reggos - .get(registration_id) - .expect("bad internal data structure"); - - match discover_namespace.as_ref() { - Some(discover_namespace) if discover_namespace == namespace => { - Some(registration) - } - Some(_) => None, - None => Some(registration), + ) -> (impl Iterator + '_, Cookie) { + let discovered = self.registrations_for_peer.iter().filter_map({ + let reggos = &self.registrations; + + move |((_, namespace), registration_id)| { + let registration = reggos + .get(registration_id) + .expect("bad internal data structure"); + + match discover_namespace.as_ref() { + Some(discover_namespace) if discover_namespace == namespace => { + Some(registration) } + Some(_) => None, + None => Some(registration), } - }) - .cloned(); + } + }); (discovered, Cookie {}) } @@ -410,7 +406,7 @@ mod tests { let (discover, _) = registrations.get(Some("foo".to_owned()), None); assert_eq!( - discover.map(|r| r.namespace).collect::>(), + discover.map(|r| r.namespace.as_str()).collect::>(), vec!["foo"] ); } @@ -427,7 +423,7 @@ mod tests { let (discover, _) = registrations.get(Some("foo".to_owned()), None); assert_eq!( - discover.map(|r| r.namespace).collect::>(), + discover.map(|r| r.namespace.as_str()).collect::>(), vec!["foo"] ); } From 4f03f9057e09e47cf3156dadc7c2e2f143617757 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 11:38:50 +1000 Subject: [PATCH 093/242] Add initial cookie functionality --- protocols/rendezvous/src/behaviour.rs | 59 +++++++++++++++++++-------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index c03f391a652..619e0347cfd 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -261,7 +261,14 @@ impl NetworkBehaviour for Rendezvous { } } -pub struct Cookie {} +#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] +pub struct Cookie(Uuid); + +impl Cookie { + fn new() -> Self { + Self(Uuid::new_v4()) + } +} #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] struct RegistrationId(Uuid); @@ -276,6 +283,7 @@ impl RegistrationId { pub struct Registrations { registrations_for_peer: HashMap<(PeerId, String), RegistrationId>, registrations: HashMap, + cookies: HashMap>, ttl_upper_bound: i64, } @@ -291,6 +299,7 @@ impl Registrations { registrations_for_peer: Default::default(), registrations: Default::default(), ttl_upper_bound, + cookies: Default::default(), } } @@ -343,27 +352,43 @@ impl Registrations { pub fn get( &mut self, discover_namespace: Option, - _: Option, + cookie: Option, ) -> (impl Iterator + '_, Cookie) { - let discovered = self.registrations_for_peer.iter().filter_map({ - let reggos = &self.registrations; - - move |((_, namespace), registration_id)| { - let registration = reggos - .get(registration_id) - .expect("bad internal data structure"); + let reggos_of_last_discover = cookie + .and_then(|cookie| self.cookies.get(&cookie)) + .cloned() + .unwrap_or_default(); + + let ids = self + .registrations_for_peer + .iter() + .filter_map({ + move |((_, namespace), registration_id)| { + if reggos_of_last_discover.contains(registration_id) { + return None; + } - match discover_namespace.as_ref() { - Some(discover_namespace) if discover_namespace == namespace => { - Some(registration) + match discover_namespace.as_ref() { + Some(discover_namespace) if discover_namespace == namespace => { + Some(registration_id) + } + Some(_) => None, + None => Some(registration_id), } - Some(_) => None, - None => Some(registration), } - } - }); + }) + .cloned() + .collect::>(); + + let new_cookie = Cookie::new(); + self.cookies.insert(new_cookie, ids.clone()); + + let reggos = &self.registrations; + let registrations = ids + .into_iter() + .map(move |id| reggos.get(&id).expect("bad internal datastructure")); - (discovered, Cookie {}) + (registrations, new_cookie) } } From fbea1ef1f02c326ae5bfeff9835885d5b5d558f1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 11:42:36 +1000 Subject: [PATCH 094/242] Make sure new cookie implies old cookie --- protocols/rendezvous/src/behaviour.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 619e0347cfd..d6aefe3bb8a 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -354,7 +354,7 @@ impl Registrations { discover_namespace: Option, cookie: Option, ) -> (impl Iterator + '_, Cookie) { - let reggos_of_last_discover = cookie + let mut reggos_of_last_discover = cookie .and_then(|cookie| self.cookies.get(&cookie)) .cloned() .unwrap_or_default(); @@ -363,7 +363,7 @@ impl Registrations { .registrations_for_peer .iter() .filter_map({ - move |((_, namespace), registration_id)| { + |((_, namespace), registration_id)| { if reggos_of_last_discover.contains(registration_id) { return None; } @@ -380,8 +380,10 @@ impl Registrations { .cloned() .collect::>(); + reggos_of_last_discover.extend_from_slice(&ids); + let new_cookie = Cookie::new(); - self.cookies.insert(new_cookie, ids.clone()); + self.cookies.insert(new_cookie, reggos_of_last_discover); let reggos = &self.registrations; let registrations = ids @@ -453,6 +455,22 @@ mod tests { ); } + #[test] + fn given_cookie_from_2nd_discover_does_not_return_nodes_from_first_discover() { + let mut registrations = Registrations::new(); + registrations.add(new_dummy_registration("foo")); + registrations.add(new_dummy_registration("foo")); + + let (initial_discover, cookie1) = registrations.get(None, None); + assert_eq!(initial_discover.collect::>().len(), 2); + + let (subsequent_discover, cookie2) = registrations.get(None, Some(cookie1)); + assert_eq!(subsequent_discover.collect::>().len(), 0); + + let (subsequent_discover, _) = registrations.get(None, Some(cookie2)); + assert_eq!(subsequent_discover.collect::>().len(), 0); + } + fn new_dummy_registration(namespace: &str) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); From 7801601186bc9d27d5474fe1d145187298b8b01c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 11:57:48 +1000 Subject: [PATCH 095/242] Add cookies to wire messages --- protocols/rendezvous/src/behaviour.rs | 46 ++++++++++++------------- protocols/rendezvous/src/codec.rs | 49 +++++++++++++++++++++++---- protocols/rendezvous/src/handler.rs | 14 +++++--- 3 files changed, 73 insertions(+), 36 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index d6aefe3bb8a..611861a0bd5 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,4 +1,4 @@ -use crate::codec::{ErrorCode, Message, NewRegistration, Registration}; +use crate::codec::{Cookie, ErrorCode, Message, NewRegistration, Registration}; use crate::handler; use crate::handler::{InEvent, RendezvousHandler}; use libp2p_core::connection::ConnectionId; @@ -87,6 +87,7 @@ pub enum Event { Discovered { rendezvous_node: PeerId, registrations: HashMap<(String, PeerId), Registration>, + cookie: Cookie, }, AnsweredDiscoverRequest { enquirer: PeerId, @@ -200,8 +201,8 @@ impl NetworkBehaviour for Rendezvous { self.registrations.remove(namespace, peer_id); // TODO: Should send unregister response? } - Message::Discover { namespace } => { - let (registrations, _) = self.registrations.get(namespace, None); + Message::Discover { namespace, cookie } => { + let (registrations, cookie) = self.registrations.get(namespace, cookie); let discovered = registrations.cloned().collect::>(); @@ -211,6 +212,7 @@ impl NetworkBehaviour for Rendezvous { handler: NotifyHandler::Any, event: InEvent::DiscoverResponse { discovered: discovered.clone(), + cookie, }, }); self.events.push_back(NetworkBehaviourAction::GenerateEvent( @@ -220,16 +222,19 @@ impl NetworkBehaviour for Rendezvous { }, )); } - Message::DiscoverResponse { registrations } => { - self.events - .push_back(NetworkBehaviourAction::GenerateEvent(Event::Discovered { - rendezvous_node: peer_id, - registrations: registrations - .iter() - .map(|r| ((r.namespace.clone(), r.record.peer_id()), r.clone())) - .collect(), - })) - } + Message::DiscoverResponse { + registrations, + cookie, + } => self + .events + .push_back(NetworkBehaviourAction::GenerateEvent(Event::Discovered { + rendezvous_node: peer_id, + registrations: registrations + .iter() + .map(|r| ((r.namespace.clone(), r.record.peer_id()), r.clone())) + .collect(), + cookie, + })), Message::FailedToDiscover { error } => self.events.push_back( NetworkBehaviourAction::GenerateEvent(Event::FailedToDiscover { rendezvous_node: peer_id, @@ -261,15 +266,6 @@ impl NetworkBehaviour for Rendezvous { } } -#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] -pub struct Cookie(Uuid); - -impl Cookie { - fn new() -> Self { - Self(Uuid::new_v4()) - } -} - #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] struct RegistrationId(Uuid); @@ -457,9 +453,9 @@ mod tests { #[test] fn given_cookie_from_2nd_discover_does_not_return_nodes_from_first_discover() { - let mut registrations = Registrations::new(); - registrations.add(new_dummy_registration("foo")); - registrations.add(new_dummy_registration("foo")); + let mut registrations = Registrations::new(7201); + registrations.add(new_dummy_registration("foo")).unwrap(); + registrations.add(new_dummy_registration("foo")).unwrap(); let (initial_discover, cookie1) = registrations.get(None, None); assert_eq!(initial_discover.collect::>().len(), 2); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 462ebcd4804..4903a450787 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -3,6 +3,7 @@ use libp2p_core::{peer_record, signed_envelope, AuthenticatedPeerRecord, SignedE use std::convert::{TryFrom, TryInto}; use std::time::SystemTime; use unsigned_varint::codec::UviBytes; +use uuid::Uuid; #[derive(Debug, Clone)] pub enum Message { @@ -20,17 +21,41 @@ pub enum Message { Discover { namespace: Option, // TODO limit: Option - // TODO cookie: Option + cookie: Option, }, DiscoverResponse { registrations: Vec, - // TODO cookie: Option + cookie: Cookie, }, FailedToDiscover { error: ErrorCode, }, } +#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] +pub struct Cookie(Uuid); + +impl Cookie { + pub fn new() -> Self { + Self(Uuid::new_v4()) + } + + pub fn into_protobuf_encoding(self) -> Vec { + self.0.as_bytes().to_vec() + } + + pub fn from_protobuf_encoding(bytes: Vec) -> Result { + let bytes = <[u8; 16]>::try_from(bytes).map_err(|_| InvalidCookie)?; + let uuid = Uuid::from_bytes(bytes); + + Ok(Self(uuid)) + } +} + +#[derive(Debug, thiserror::Error)] +#[error("The cookie was malformed")] +pub struct InvalidCookie; + #[derive(Debug, Clone)] pub struct NewRegistration { pub namespace: String, @@ -200,11 +225,11 @@ impl From for wire::Message { discover: None, discover_response: None, }, - Message::Discover { namespace } => wire::Message { + Message::Discover { namespace, cookie } => wire::Message { r#type: Some(MessageType::Discover.into()), discover: Some(Discover { ns: namespace, - cookie: None, + cookie: cookie.map(|cookie| cookie.into_protobuf_encoding()), limit: None, }), register: None, @@ -212,7 +237,10 @@ impl From for wire::Message { unregister: None, discover_response: None, }, - Message::DiscoverResponse { registrations } => wire::Message { + Message::DiscoverResponse { + registrations, + cookie, + } => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), discover_response: Some(DiscoverResponse { registrations: registrations @@ -227,7 +255,7 @@ impl From for wire::Message { .collect(), status: Some(ResponseStatus::Ok.into()), status_text: None, - cookie: None, + cookie: Some(cookie.into_protobuf_encoding()), }), register: None, discover: None, @@ -290,13 +318,17 @@ impl TryFrom for Message { r#type: Some(3), discover: Some(Discover { ns, .. }), .. - } => Message::Discover { namespace: ns }, + } => Message::Discover { + namespace: ns, + cookie: None, + }, wire::Message { r#type: Some(4), discover_response: Some(DiscoverResponse { registrations, status: Some(0), + cookie: Some(cookie), .. }), .. @@ -318,6 +350,7 @@ impl TryFrom for Message { }) }) .collect::, ConversionError>>()?, + cookie: Cookie::from_protobuf_encoding(cookie)?, }, wire::Message { r#type: Some(1), @@ -375,6 +408,8 @@ pub enum ConversionError { BadSignedEnvelope(#[from] signed_envelope::DecodingError), #[error("Failed to decode envelope as signed peer record")] BadSignedPeerRecord(#[from] peer_record::FromEnvelopeError), + #[error(transparent)] + BadCookie(#[from] InvalidCookie), } impl TryFrom for ErrorCode { diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 1a63b48fb8f..3ed502d8a17 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,4 +1,4 @@ -use crate::codec::{ErrorCode, Message, Registration}; +use crate::codec::{Cookie, ErrorCode, Message, Registration}; use crate::codec::{NewRegistration, RendezvousCodec}; use crate::{codec, protocol}; use asynchronous_codec::Framed; @@ -56,6 +56,7 @@ pub enum InEvent { }, DiscoverResponse { discovered: Vec, + cookie: Cookie, }, } @@ -177,7 +178,8 @@ impl Advance for Inbound { } } Poll::Ready(Some(Err(e))) => { - panic!("Error when sending outbound: {:?}", e); + // TODO: investigate the error here and send an appropriate error response + panic!("Error when reading inbound: {:?}", e); } Poll::Ready(None) => { panic!("Honestly no idea what to do if this happens"); @@ -372,7 +374,10 @@ impl ProtocolsHandler for RendezvousHandler { ), (InEvent::DiscoverRequest { namespace }, inbound, SubstreamState::None) => ( inbound, - SubstreamState::Active(Outbound::Start(Message::Discover { namespace })), + SubstreamState::Active(Outbound::Start(Message::Discover { + namespace, + cookie: None, + })), ), ( InEvent::RegisterResponse { ttl }, @@ -397,7 +402,7 @@ impl ProtocolsHandler for RendezvousHandler { outbound, ), ( - InEvent::DiscoverResponse { discovered }, + InEvent::DiscoverResponse { discovered, cookie }, SubstreamState::Active(Inbound::WaitForBehaviour(substream)), outbound, ) => ( @@ -405,6 +410,7 @@ impl ProtocolsHandler for RendezvousHandler { substream, Message::DiscoverResponse { registrations: discovered, + cookie, }, )), outbound, From e23385082ce981a9782f700632999f2861ec92a4 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 12:05:10 +1000 Subject: [PATCH 096/242] Allow ttl to match exactly upper bound --- protocols/rendezvous/src/behaviour.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 611861a0bd5..6cda710f026 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -301,7 +301,7 @@ impl Registrations { pub fn add(&mut self, new_registration: NewRegistration) -> Result { let ttl = new_registration.effective_ttl(); - if ttl < self.ttl_upper_bound { + if ttl <= self.ttl_upper_bound { let namespace = new_registration.namespace; let registration_id = RegistrationId::new(); @@ -397,7 +397,7 @@ mod tests { #[test] fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { - let mut registrations = Registrations::new(7201); + let mut registrations = Registrations::new(7200); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -410,7 +410,7 @@ mod tests { #[test] fn given_registrations_when_discover_all_then_all_are_returned() { - let mut registrations = Registrations::new(7201); + let mut registrations = Registrations::new(7200); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -422,7 +422,7 @@ mod tests { #[test] fn given_registrations_when_discover_only_for_specific_namespace_then_only_those_are_returned() { - let mut registrations = Registrations::new(7201); + let mut registrations = Registrations::new(7200); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); @@ -437,7 +437,7 @@ mod tests { #[test] fn given_reregistration_old_registration_is_discarded() { let alice = identity::Keypair::generate_ed25519(); - let mut registrations = Registrations::new(7201); + let mut registrations = Registrations::new(7200); registrations .add(new_registration("foo", alice.clone())) .unwrap(); @@ -453,7 +453,7 @@ mod tests { #[test] fn given_cookie_from_2nd_discover_does_not_return_nodes_from_first_discover() { - let mut registrations = Registrations::new(7201); + let mut registrations = Registrations::new(7200); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); From 242d4446eea638d7063716edfe768555d877f1a3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 12:05:47 +1000 Subject: [PATCH 097/242] Use defensive programming to avoid additional levels of indentation --- protocols/rendezvous/src/behaviour.rs | 62 +++++++++++++-------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 6cda710f026..31f43bf64ee 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -301,40 +301,40 @@ impl Registrations { pub fn add(&mut self, new_registration: NewRegistration) -> Result { let ttl = new_registration.effective_ttl(); - if ttl <= self.ttl_upper_bound { - let namespace = new_registration.namespace; - let registration_id = RegistrationId::new(); - - match self - .registrations_for_peer - .entry((new_registration.record.peer_id(), namespace.clone())) - { - Entry::Occupied(mut occupied) => { - let old_registration = occupied.insert(registration_id); - - self.registrations.remove(&old_registration); - } - Entry::Vacant(vacant) => { - vacant.insert(registration_id); - } - } - - self.registrations.insert( - registration_id, - Registration { - namespace: namespace.clone(), - record: new_registration.record, - ttl, - timestamp: SystemTime::now(), - }, - ); - Ok(ttl) - } else { - Err(RegistrationError::TTLGreaterThanUpperBound { + if ttl > self.ttl_upper_bound { + return Err(RegistrationError::TTLGreaterThanUpperBound { upper_bound: self.ttl_upper_bound, requested: ttl, - }) + }); + } + + let namespace = new_registration.namespace; + let registration_id = RegistrationId::new(); + + match self + .registrations_for_peer + .entry((new_registration.record.peer_id(), namespace.clone())) + { + Entry::Occupied(mut occupied) => { + let old_registration = occupied.insert(registration_id); + + self.registrations.remove(&old_registration); + } + Entry::Vacant(vacant) => { + vacant.insert(registration_id); + } } + + self.registrations.insert( + registration_id, + Registration { + namespace: namespace.clone(), + record: new_registration.record, + ttl, + timestamp: SystemTime::now(), + }, + ); + Ok(ttl) } pub fn remove(&mut self, namespace: String, peer_id: PeerId) { From cc9a02ebfd787f200ea0fc69cb7a329075ffce0b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 12:08:34 +1000 Subject: [PATCH 098/242] Use `match` with specific error variant instead of if let Matching against the specific error variants will produce a compile error (non-exhaustive match) if anyone ever extends this enum. This is useful because it otherwise, we would erroneously send an `InvalidTtl` message to the client even though the error cause might be something completely different. --- protocols/rendezvous/src/behaviour.rs | 57 ++++++++++++++------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 31f43bf64ee..e620f878241 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -154,36 +154,37 @@ impl NetworkBehaviour for Rendezvous { Message::Register(new_registration) => { let namespace = new_registration.namespace.clone(); - if let Ok(effective_ttl) = self.registrations.add(new_registration) { - // notify the handler that to send a response - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: InEvent::RegisterResponse { ttl: effective_ttl }, - }); - - // emit behaviour event - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::PeerRegistered { - peer: peer_id, - namespace, - }, - )); - } else { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: InEvent::DeclineRegisterRequest { - error: ErrorCode::InvalidTtl, + let events = match self.registrations.add(new_registration) { + Ok(effective_ttl) => { + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: InEvent::RegisterResponse { ttl: effective_ttl }, + }, + NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { + peer: peer_id, + namespace, + }), + ] + } + Err(RegistrationError::TTLGreaterThanUpperBound { .. }) => { + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: InEvent::DeclineRegisterRequest { + error: ErrorCode::InvalidTtl, + }, }, - }); + NetworkBehaviourAction::GenerateEvent(Event::DeclinedRegisterRequest { + peer: peer_id, + }), + ] + } + }; - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::DeclinedRegisterRequest { peer: peer_id }, - )); - } + self.events.extend(events); } Message::RegisterResponse { ttl } => self.events.push_back( NetworkBehaviourAction::GenerateEvent(Event::RegisteredWithRendezvousNode { From 5277f6a791c4cddc031b5b6594ed5428f8d91e63 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 12:12:54 +1000 Subject: [PATCH 099/242] Remove TODO We've got some tests now. --- protocols/rendezvous/src/behaviour.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index e620f878241..c146c78a94b 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -276,7 +276,6 @@ impl RegistrationId { } } -// TODO: Unit Tests pub struct Registrations { registrations_for_peer: HashMap<(PeerId, String), RegistrationId>, registrations: HashMap, From 3240bc511f600d1f4e9e8326cb0ffed9716585f7 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 10 Jun 2021 14:13:39 +1000 Subject: [PATCH 100/242] Rename function to clarify that we are not using protobuf for cookies --- protocols/rendezvous/src/codec.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 4903a450787..553566e6945 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -40,11 +40,11 @@ impl Cookie { Self(Uuid::new_v4()) } - pub fn into_protobuf_encoding(self) -> Vec { + pub fn into_wire_encoding(self) -> Vec { self.0.as_bytes().to_vec() } - pub fn from_protobuf_encoding(bytes: Vec) -> Result { + pub fn from_wire_encoding(bytes: Vec) -> Result { let bytes = <[u8; 16]>::try_from(bytes).map_err(|_| InvalidCookie)?; let uuid = Uuid::from_bytes(bytes); @@ -229,7 +229,7 @@ impl From for wire::Message { r#type: Some(MessageType::Discover.into()), discover: Some(Discover { ns: namespace, - cookie: cookie.map(|cookie| cookie.into_protobuf_encoding()), + cookie: cookie.map(|cookie| cookie.into_wire_encoding()), limit: None, }), register: None, @@ -255,7 +255,7 @@ impl From for wire::Message { .collect(), status: Some(ResponseStatus::Ok.into()), status_text: None, - cookie: Some(cookie.into_protobuf_encoding()), + cookie: Some(cookie.into_wire_encoding()), }), register: None, discover: None, @@ -350,7 +350,7 @@ impl TryFrom for Message { }) }) .collect::, ConversionError>>()?, - cookie: Cookie::from_protobuf_encoding(cookie)?, + cookie: Cookie::from_wire_encoding(cookie)?, }, wire::Message { r#type: Some(1), From 2d96943d9ed13a5a3e6506114ff195ccab1be95d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 09:50:34 +1000 Subject: [PATCH 101/242] Allow tests to run in parallel These tests share the same thread and hence we should only try to initialize the logger once. --- protocols/rendezvous/tests/rendezvous.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 7a1c8192c51..28fd8cb219f 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -8,7 +8,7 @@ use libp2p_swarm::Swarm; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { - env_logger::init(); + let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; let namespace = "some-namespace".to_string(); @@ -37,7 +37,7 @@ async fn given_successful_registration_then_successful_discovery() { #[tokio::test] async fn given_successful_registration_then_refresh_ttl() { - env_logger::init(); + let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; let namespace = "some-namespace".to_string(); @@ -89,7 +89,7 @@ async fn given_successful_registration_then_refresh_ttl() { #[tokio::test] async fn given_invalid_ttl_then_unsuccessful_registration() { - env_logger::init(); + let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; let namespace = "some-namespace".to_string(); From 58ce18e3c9faaeb26181a0fa033987f8ac7254e6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 09:52:34 +1000 Subject: [PATCH 102/242] Make cookies specific to a namespace if given during DISCOVER --- protocols/rendezvous/src/behaviour.rs | 62 ++++++++++++++++----- protocols/rendezvous/src/codec.rs | 80 ++++++++++++++++++++++++--- 2 files changed, 122 insertions(+), 20 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index c146c78a94b..ebe32204bec 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -203,7 +203,10 @@ impl NetworkBehaviour for Rendezvous { // TODO: Should send unregister response? } Message::Discover { namespace, cookie } => { - let (registrations, cookie) = self.registrations.get(namespace, cookie); + let (registrations, cookie) = self + .registrations + .get(namespace, cookie) + .expect("TODO: error handling: send back bad cookie"); let discovered = registrations.cloned().collect::>(); @@ -349,7 +352,20 @@ impl Registrations { &mut self, discover_namespace: Option, cookie: Option, - ) -> (impl Iterator + '_, Cookie) { + ) -> Result<(impl Iterator + '_, Cookie), CookieNamespaceMismatch> { + let cookie_namespace = cookie.as_ref().and_then(|cookie| cookie.namespace()); + + match (discover_namespace.as_ref(), cookie_namespace) { + // discover all namespace but cookie is specific to a namespace? => bad + (None, Some(_)) => return Err(CookieNamespaceMismatch), + // discover for a namespace but cookie is for a different namesapce? => bad + (Some(namespace), Some(cookie_namespace)) if namespace != cookie_namespace => { + return Err(CookieNamespaceMismatch) + } + // every other combination is fine + _ => {} + } + let mut reggos_of_last_discover = cookie .and_then(|cookie| self.cookies.get(&cookie)) .cloned() @@ -378,18 +394,26 @@ impl Registrations { reggos_of_last_discover.extend_from_slice(&ids); - let new_cookie = Cookie::new(); - self.cookies.insert(new_cookie, reggos_of_last_discover); + let new_cookie = discover_namespace + .map(Cookie::for_namespace) + .unwrap_or_else(|| Cookie::for_all_namespaces()); + self.cookies + .insert(new_cookie.clone(), reggos_of_last_discover); let reggos = &self.registrations; let registrations = ids .into_iter() .map(move |id| reggos.get(&id).expect("bad internal datastructure")); - (registrations, new_cookie) + Ok((registrations, new_cookie)) } } +// TODO: Be more specific in what the bad combination was? +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +#[error("The provided cookie is not valid for a DISCOVER request for the given namespace")] +pub struct CookieNamespaceMismatch; + #[cfg(test)] mod tests { use super::*; @@ -401,10 +425,10 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); - let (initial_discover, cookie) = registrations.get(None, None); + let (initial_discover, cookie) = registrations.get(None, None).unwrap(); assert_eq!(initial_discover.collect::>().len(), 2); - let (subsequent_discover, _) = registrations.get(None, Some(cookie)); + let (subsequent_discover, _) = registrations.get(None, Some(cookie)).unwrap(); assert_eq!(subsequent_discover.collect::>().len(), 0); } @@ -414,7 +438,7 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); - let (discover, _) = registrations.get(None, None); + let (discover, _) = registrations.get(None, None).unwrap(); assert_eq!(discover.collect::>().len(), 2); } @@ -426,7 +450,7 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); - let (discover, _) = registrations.get(Some("foo".to_owned()), None); + let (discover, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); assert_eq!( discover.map(|r| r.namespace.as_str()).collect::>(), @@ -443,7 +467,7 @@ mod tests { .unwrap(); registrations.add(new_registration("foo", alice)).unwrap(); - let (discover, _) = registrations.get(Some("foo".to_owned()), None); + let (discover, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); assert_eq!( discover.map(|r| r.namespace.as_str()).collect::>(), @@ -457,16 +481,28 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); - let (initial_discover, cookie1) = registrations.get(None, None); + let (initial_discover, cookie1) = registrations.get(None, None).unwrap(); assert_eq!(initial_discover.collect::>().len(), 2); - let (subsequent_discover, cookie2) = registrations.get(None, Some(cookie1)); + let (subsequent_discover, cookie2) = registrations.get(None, Some(cookie1)).unwrap(); assert_eq!(subsequent_discover.collect::>().len(), 0); - let (subsequent_discover, _) = registrations.get(None, Some(cookie2)); + let (subsequent_discover, _) = registrations.get(None, Some(cookie2)).unwrap(); assert_eq!(subsequent_discover.collect::>().len(), 0); } + #[test] + fn cookie_from_different_discover_request_is_not_valid() { + let mut registrations = Registrations::new(7200); + registrations.add(new_dummy_registration("foo")).unwrap(); + registrations.add(new_dummy_registration("bar")).unwrap(); + + let (_, foo_discover_cookie) = registrations.get(Some("foo".to_owned()), None).unwrap(); + let result = registrations.get(Some("bar".to_owned()), Some(foo_discover_cookie)); + + assert!(matches!(result, Err(CookieNamespaceMismatch))) + } + fn new_dummy_registration(namespace: &str) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 553566e6945..ceca4ff3849 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -32,23 +32,65 @@ pub enum Message { }, } -#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] -pub struct Cookie(Uuid); +#[derive(Debug, Eq, PartialEq, Hash, Clone)] +pub struct Cookie { + id: Uuid, + namespace: Option, +} impl Cookie { - pub fn new() -> Self { - Self(Uuid::new_v4()) + /// Construct a new [`Cookie`] for a given namespace. + /// + /// This cookie will only be valid for subsequent DISCOVER requests targeting the same namespace. + pub fn for_namespace(namespace: String) -> Self { + Self { + id: Uuid::new_v4(), + namespace: Some(namespace), + } + } + + /// Construct a new [`Cookie`] for a DISCOVER request that inquires about all namespaces. + pub fn for_all_namespaces() -> Self { + Self { + id: Uuid::new_v4(), + namespace: None, + } } pub fn into_wire_encoding(self) -> Vec { - self.0.as_bytes().to_vec() + let namespace = self.namespace.unwrap_or_default(); + + let mut buffer = Vec::with_capacity(16 + namespace.len()); + buffer.extend_from_slice(self.id.as_bytes()); + buffer.extend_from_slice(namespace.as_bytes()); + + buffer } - pub fn from_wire_encoding(bytes: Vec) -> Result { + pub fn from_wire_encoding(mut bytes: Vec) -> Result { + // check length early to avoid panic during slicing + if bytes.len() < 16 { + return Err(InvalidCookie); + } + + let namespace = bytes.split_off(16); + let namespace = if namespace.len() == 0 { + None + } else { + Some(String::from_utf8(namespace).map_err(|_| InvalidCookie)?) + }; + let bytes = <[u8; 16]>::try_from(bytes).map_err(|_| InvalidCookie)?; let uuid = Uuid::from_bytes(bytes); - Ok(Self(uuid)) + Ok(Self { + id: uuid, + namespace, + }) + } + + pub fn namespace(&self) -> Option<&str> { + self.namespace.as_deref() } } @@ -462,3 +504,27 @@ pub struct NotAnError; mod wire { include!(concat!(env!("OUT_DIR"), "/rendezvous.pb.rs")); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn cookie_wire_encoding_roundtrip() { + let cookie = Cookie::for_namespace("foo".to_owned()); + + let bytes = cookie.clone().into_wire_encoding(); + let parsed = Cookie::from_wire_encoding(bytes).unwrap(); + + assert_eq!(parsed, cookie); + } + + #[test] + fn cookie_wire_encoding_length() { + let cookie = Cookie::for_namespace("foo".to_owned()); + + let bytes = cookie.into_wire_encoding(); + + assert_eq!(bytes.len(), 16 + 3) + } +} From 36d7039db800eaea9ec90893f17634f90db68055 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 14:33:49 +1000 Subject: [PATCH 103/242] Fix typo in function name --- core/src/peer_record.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index a7cdde0a6d0..b9e4ab21dce 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -56,7 +56,7 @@ impl PeerRecord { } // TODO: docs - pub fn warp_in_envelope(self, key: Keypair) -> SignedEnvelope { + pub fn wrap_in_envelope(self, key: Keypair) -> SignedEnvelope { if key.public().into_peer_id() != self.peer_id { panic!("bad key") } @@ -154,7 +154,7 @@ impl AuthenticatedPeerRecord { } pub fn from_record(key: Keypair, record: PeerRecord) -> Self { - let envelope = record.clone().warp_in_envelope(key); + let envelope = record.clone().wrap_in_envelope(key); Self { inner: record, @@ -246,7 +246,7 @@ mod tests { addresses: vec![HOME.parse().unwrap()], }; - let envelope = record.clone().warp_in_envelope(identity); + let envelope = record.clone().wrap_in_envelope(identity); let authenticated = AuthenticatedPeerRecord::from_signed_envelope(envelope).unwrap(); assert_eq!(authenticated, record) From 1241d77e9d6b99e1b611916b30f6270df5f64356 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 14:35:20 +1000 Subject: [PATCH 104/242] Remove `PeerRecord::authenticate` --- core/src/peer_record.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index b9e4ab21dce..b715fa70a2c 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -71,10 +71,6 @@ impl PeerRecord { ) .unwrap() // TODO: Error handling } - - pub fn authenticate(self, key: Keypair) -> AuthenticatedPeerRecord { - AuthenticatedPeerRecord::from_record(key, self) - } } #[derive(Debug)] From fb39c2ce90bf86315d5ad4405111b23bc36b6508 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 14:39:41 +1000 Subject: [PATCH 105/242] Re-order module --- core/src/peer_record.rs | 108 ++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index b715fa70a2c..cdd65957da0 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -7,6 +7,60 @@ use std::fmt; const PAYLOAD_TYPE: &str = "/libp2p/routing-state-record"; const DOMAIN_SEP: &str = "libp2p-routing-state"; +// TODO: docs +#[derive(Debug)] +pub struct AuthenticatedPeerRecord { + inner: PeerRecord, + + /// A signed envelope containing the above inner [`PeerRecord`]. + /// + /// If this [`AuthenticatedPeerRecord`] was constructed from a [`SignedEnvelope`], this is the original instance. + /// If this [`AuthenticatedPeerRecord`] was created by [`authenticating`](PeerRecord::authenticate) an existing [`PeerRecord`], then this is a pre-computed [`SignedEnvelope`] to make it easier to send an [`AuthenticatedPeerRecord`] across the wire. + envelope: SignedEnvelope, +} + +impl AuthenticatedPeerRecord { + // TODO: docs + pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result { + let payload = envelope.payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes())?; + let record = PeerRecord::from_protobuf_encoding(payload)?; + + Ok(Self { + inner: record, + envelope, + }) + } + + pub fn from_record(key: Keypair, record: PeerRecord) -> Self { + let envelope = record.clone().wrap_in_envelope(key); + + Self { + inner: record, + envelope, + } + } + + pub fn to_signed_envelope(&self) -> SignedEnvelope { + self.envelope.clone() + } + + pub fn into_signed_envelope(self) -> SignedEnvelope { + self.envelope + } + + pub fn peer_id(&self) -> PeerId { + self.inner.peer_id + } + + pub fn seq(&self) -> u64 { + self.inner.seq + } + + pub fn addresses(&self) -> &[Multiaddr] { + self.inner.addresses.as_slice() + } +} + // TODO: docs #[derive(Debug, PartialEq, Clone)] pub struct PeerRecord { @@ -125,60 +179,6 @@ impl std::error::Error for DecodingError { } } -// TODO: docs -#[derive(Debug)] -pub struct AuthenticatedPeerRecord { - inner: PeerRecord, - - /// A signed envelope containing the above inner [`PeerRecord`]. - /// - /// If this [`AuthenticatedPeerRecord`] was constructed from a [`SignedEnvelope`], this is the original instance. - /// If this [`AuthenticatedPeerRecord`] was created by [`authenticating`](PeerRecord::authenticate) an existing [`PeerRecord`], then this is a pre-computed [`SignedEnvelope`] to make it easier to send an [`AuthenticatedPeerRecord`] across the wire. - envelope: SignedEnvelope, -} - -impl AuthenticatedPeerRecord { - // TODO: docs - pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result { - let payload = envelope.payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes())?; - let record = PeerRecord::from_protobuf_encoding(payload)?; - - Ok(Self { - inner: record, - envelope, - }) - } - - pub fn from_record(key: Keypair, record: PeerRecord) -> Self { - let envelope = record.clone().wrap_in_envelope(key); - - Self { - inner: record, - envelope, - } - } - - pub fn to_signed_envelope(&self) -> SignedEnvelope { - self.envelope.clone() - } - - pub fn into_signed_envelope(self) -> SignedEnvelope { - self.envelope - } - - pub fn peer_id(&self) -> PeerId { - self.inner.peer_id - } - - pub fn seq(&self) -> u64 { - self.inner.seq - } - - pub fn addresses(&self) -> &[Multiaddr] { - self.inner.addresses.as_slice() - } -} - impl PartialEq for AuthenticatedPeerRecord { fn eq(&self, other: &PeerRecord) -> bool { self.inner.eq(other) From 0251bd41a53a2f624601b00509b4a79bfd56f80e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 14:50:00 +1000 Subject: [PATCH 106/242] Make `PeerRecord` private --- core/src/lib.rs | 2 +- core/src/peer_record.rs | 80 ++++++++++++++++--------------------- core/src/signed_envelope.rs | 2 +- 3 files changed, 36 insertions(+), 48 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 9041f02ef30..a5b8d54d82a 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -75,7 +75,7 @@ pub use upgrade::{InboundUpgrade, OutboundUpgrade, UpgradeInfo, UpgradeError, Pr pub use connection::{Connected, Endpoint, ConnectedPoint}; pub use network::Network; pub use signed_envelope::SignedEnvelope; -pub use peer_record::{AuthenticatedPeerRecord, PeerRecord}; +pub use peer_record::{AuthenticatedPeerRecord}; use std::{future::Future, pin::Pin}; diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index cdd65957da0..59479d7b026 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -1,14 +1,16 @@ +use crate::identity::error::SigningError; use crate::identity::Keypair; use crate::signed_envelope::SignedEnvelope; use crate::{peer_record_proto, signed_envelope, Multiaddr, PeerId}; use std::convert::TryInto; use std::fmt; +use std::time::SystemTime; const PAYLOAD_TYPE: &str = "/libp2p/routing-state-record"; const DOMAIN_SEP: &str = "libp2p-routing-state"; // TODO: docs -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct AuthenticatedPeerRecord { inner: PeerRecord, @@ -31,13 +33,31 @@ impl AuthenticatedPeerRecord { }) } - pub fn from_record(key: Keypair, record: PeerRecord) -> Self { - let envelope = record.clone().wrap_in_envelope(key); + pub fn new(key: Keypair, addresses: Vec) -> Result { + let secs_since_epoch = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("now() is never before UNIX_EPOCH") + .as_secs(); + + let record = PeerRecord { + peer_id: key.public().into_peer_id(), + seq: secs_since_epoch, + addresses, + }; + + let payload = record.clone().into_protobuf_encoding(); + + let envelope = SignedEnvelope::new( + key, + String::from(DOMAIN_SEP), + PAYLOAD_TYPE.as_bytes().to_vec(), + payload, + )?; - Self { + Ok(Self { inner: record, envelope, - } + }) } pub fn to_signed_envelope(&self) -> SignedEnvelope { @@ -63,7 +83,7 @@ impl AuthenticatedPeerRecord { // TODO: docs #[derive(Debug, PartialEq, Clone)] -pub struct PeerRecord { +struct PeerRecord { pub peer_id: PeerId, pub seq: u64, pub addresses: Vec, @@ -71,7 +91,7 @@ pub struct PeerRecord { impl PeerRecord { // TODO: docs - pub fn into_protobuf_encoding(self) -> Vec { + fn into_protobuf_encoding(self) -> Vec { use prost::Message; let record = peer_record_proto::PeerRecord { @@ -93,7 +113,7 @@ impl PeerRecord { buf } - pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + fn from_protobuf_encoding(bytes: &[u8]) -> Result { use prost::Message; let record = peer_record_proto::PeerRecord::decode(bytes)?; @@ -108,23 +128,6 @@ impl PeerRecord { .collect::, _>>()?, }) } - - // TODO: docs - pub fn wrap_in_envelope(self, key: Keypair) -> SignedEnvelope { - if key.public().into_peer_id() != self.peer_id { - panic!("bad key") - } - - let payload = self.into_protobuf_encoding(); - - SignedEnvelope::new( - key, - String::from(DOMAIN_SEP), - PAYLOAD_TYPE.as_bytes().to_vec(), - payload, - ) - .unwrap() // TODO: Error handling - } } #[derive(Debug)] @@ -179,18 +182,6 @@ impl std::error::Error for DecodingError { } } -impl PartialEq for AuthenticatedPeerRecord { - fn eq(&self, other: &PeerRecord) -> bool { - self.inner.eq(other) - } -} - -impl PartialEq for PeerRecord { - fn eq(&self, other: &AuthenticatedPeerRecord) -> bool { - other.inner.eq(self) - } -} - #[derive(Debug)] pub enum FromEnvelopeError { BadPayload(signed_envelope::ReadPayloadError), @@ -235,16 +226,13 @@ mod tests { #[test] fn roundtrip_envelope() { - let identity = Keypair::generate_ed25519(); - let record = PeerRecord { - peer_id: identity.public().into_peer_id(), - seq: 0, - addresses: vec![HOME.parse().unwrap()], - }; + let record = + AuthenticatedPeerRecord::new(Keypair::generate_ed25519(), vec![HOME.parse().unwrap()]) + .unwrap(); - let envelope = record.clone().wrap_in_envelope(identity); - let authenticated = AuthenticatedPeerRecord::from_signed_envelope(envelope).unwrap(); + let envelope = record.to_signed_envelope(); + let reconstructed = AuthenticatedPeerRecord::from_signed_envelope(envelope).unwrap(); - assert_eq!(authenticated, record) + assert_eq!(reconstructed, record) } } diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index 8f6a883972e..a1d8b4339be 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -7,7 +7,7 @@ use std::fmt; use unsigned_varint::encode::usize_buffer; // TODO: docs -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct SignedEnvelope { key: PublicKey, payload_type: Vec, From 799103e155b3c7b74e1997b124dba7805e8a5eb3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:13:23 +1000 Subject: [PATCH 107/242] Merge `PeerRecord` into `AuthenticatedPeerRecord` for simplicity --- core/src/lib.rs | 2 +- core/src/peer_record.rs | 191 ++++++++++++++++------------------------ 2 files changed, 76 insertions(+), 117 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index a5b8d54d82a..bdc0c7a7653 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -75,7 +75,7 @@ pub use upgrade::{InboundUpgrade, OutboundUpgrade, UpgradeInfo, UpgradeError, Pr pub use connection::{Connected, Endpoint, ConnectedPoint}; pub use network::Network; pub use signed_envelope::SignedEnvelope; -pub use peer_record::{AuthenticatedPeerRecord}; +pub use peer_record::PeerRecord; use std::{future::Future, pin::Pin}; diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index 59479d7b026..b4e02a09c26 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -9,43 +9,76 @@ use std::time::SystemTime; const PAYLOAD_TYPE: &str = "/libp2p/routing-state-record"; const DOMAIN_SEP: &str = "libp2p-routing-state"; -// TODO: docs +/// Represents a peer routing record. +/// +/// Peer records are designed to be distributable and carry a signature by wrapping them in a signed envelope. +/// For more information see RFC0003 of the libp2p specifications: https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md #[derive(Debug, PartialEq)] -pub struct AuthenticatedPeerRecord { - inner: PeerRecord, +pub struct PeerRecord { + peer_id: PeerId, + seq: u64, + addresses: Vec, - /// A signed envelope containing the above inner [`PeerRecord`]. + /// A signed envelope representing this [`PeerRecord`]. /// - /// If this [`AuthenticatedPeerRecord`] was constructed from a [`SignedEnvelope`], this is the original instance. - /// If this [`AuthenticatedPeerRecord`] was created by [`authenticating`](PeerRecord::authenticate) an existing [`PeerRecord`], then this is a pre-computed [`SignedEnvelope`] to make it easier to send an [`AuthenticatedPeerRecord`] across the wire. + /// If this [`PeerRecord`] was constructed from a [`SignedEnvelope`], this is the original instance. envelope: SignedEnvelope, } -impl AuthenticatedPeerRecord { - // TODO: docs +impl PeerRecord { + /// Attempt to re-construct a [`PeerRecord`] from a [`SignedEnvelope`]. + /// + /// If this function succeeds, the [`SignedEnvelope`] contained a peer record with a valid signature and can hence be considered authenticated. pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result { + use prost::Message; + let payload = envelope.payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes())?; - let record = PeerRecord::from_protobuf_encoding(payload)?; + let record = peer_record_proto::PeerRecord::decode(payload)?; Ok(Self { - inner: record, + peer_id: PeerId::from_bytes(&record.peer_id)?, + seq: record.seq, + addresses: record + .addresses + .into_iter() + .map(|a| a.multiaddr.try_into()) + .collect::, _>>()?, envelope, }) } + /// Construct a new [`PeerRecord`] by authenticating the provided addresses with the given key. + /// + /// This is the same key that is used for authenticating every libp2p connection of your application, i.e. what you use when setting up your [`libp2p_core::transport::Transport`]. pub fn new(key: Keypair, addresses: Vec) -> Result { + use prost::Message; + let secs_since_epoch = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .expect("now() is never before UNIX_EPOCH") .as_secs(); - let record = PeerRecord { - peer_id: key.public().into_peer_id(), - seq: secs_since_epoch, - addresses, + let peer_id = key.public().into_peer_id(); + let seq = secs_since_epoch; + + let record = peer_record_proto::PeerRecord { + peer_id: peer_id.to_bytes(), + seq, + addresses: addresses + .iter() + .map(|m| peer_record_proto::peer_record::AddressInfo { + multiaddr: m.to_vec(), + }) + .collect(), }; - let payload = record.clone().into_protobuf_encoding(); + let payload = { + let mut buf = Vec::with_capacity(record.encoded_len()); + record + .encode(&mut buf) + .expect("Vec provides capacity as needed"); + buf + }; let envelope = SignedEnvelope::new( key, @@ -55,7 +88,9 @@ impl AuthenticatedPeerRecord { )?; Ok(Self { - inner: record, + peer_id, + seq, + addresses, envelope, }) } @@ -69,69 +104,22 @@ impl AuthenticatedPeerRecord { } pub fn peer_id(&self) -> PeerId { - self.inner.peer_id + self.peer_id } pub fn seq(&self) -> u64 { - self.inner.seq + self.seq } pub fn addresses(&self) -> &[Multiaddr] { - self.inner.addresses.as_slice() - } -} - -// TODO: docs -#[derive(Debug, PartialEq, Clone)] -struct PeerRecord { - pub peer_id: PeerId, - pub seq: u64, - pub addresses: Vec, -} - -impl PeerRecord { - // TODO: docs - fn into_protobuf_encoding(self) -> Vec { - use prost::Message; - - let record = peer_record_proto::PeerRecord { - peer_id: self.peer_id.to_bytes(), - seq: self.seq, - addresses: self - .addresses - .into_iter() - .map(|m| peer_record_proto::peer_record::AddressInfo { - multiaddr: m.to_vec(), - }) - .collect(), - }; - - let mut buf = Vec::with_capacity(record.encoded_len()); - record - .encode(&mut buf) - .expect("Vec provides capacity as needed"); - buf - } - - fn from_protobuf_encoding(bytes: &[u8]) -> Result { - use prost::Message; - - let record = peer_record_proto::PeerRecord::decode(bytes)?; - - Ok(Self { - peer_id: PeerId::from_bytes(&record.peer_id)?, - seq: record.seq, - addresses: record - .addresses - .into_iter() - .map(|a| a.multiaddr.try_into()) - .collect::, _>>()?, - }) + self.addresses.as_slice() } } #[derive(Debug)] -pub enum DecodingError { +pub enum FromEnvelopeError { + /// Failed to extract the payload from the envelope. + BadPayload(signed_envelope::ReadPayloadError), /// Failed to decode the provided bytes as a [`PeerRecord`]. InvalidPeerRecord(prost::DecodeError), /// Failed to decode the peer ID. @@ -140,79 +128,51 @@ pub enum DecodingError { InvalidMultiaddr(multiaddr::Error), } -impl From for DecodingError { +impl From for FromEnvelopeError { + fn from(e: signed_envelope::ReadPayloadError) -> Self { + Self::BadPayload(e) + } +} + +impl From for FromEnvelopeError { fn from(e: prost::DecodeError) -> Self { Self::InvalidPeerRecord(e) } } -impl From for DecodingError { +impl From for FromEnvelopeError { fn from(e: multihash::Error) -> Self { Self::InvalidPeerId(e) } } -impl From for DecodingError { +impl From for FromEnvelopeError { fn from(e: multiaddr::Error) -> Self { Self::InvalidMultiaddr(e) } } -impl fmt::Display for DecodingError { +impl fmt::Display for FromEnvelopeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - DecodingError::InvalidPeerRecord(_) => { + Self::BadPayload(_) => write!(f, "Failed to extract payload from envelope"), + Self::InvalidPeerRecord(_) => { write!(f, "Failed to decode bytes as PeerRecord") } - DecodingError::InvalidPeerId(_) => write!(f, "Failed to decode bytes as PeerId"), - DecodingError::InvalidMultiaddr(_) => { + Self::InvalidPeerId(_) => write!(f, "Failed to decode bytes as PeerId"), + Self::InvalidMultiaddr(_) => { write!(f, "Failed to decode bytes as MultiAddress") } } } } -impl std::error::Error for DecodingError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - DecodingError::InvalidPeerRecord(inner) => Some(inner), - DecodingError::InvalidPeerId(inner) => Some(inner), - DecodingError::InvalidMultiaddr(inner) => Some(inner), - } - } -} - -#[derive(Debug)] -pub enum FromEnvelopeError { - BadPayload(signed_envelope::ReadPayloadError), - InvalidPeerRecord(DecodingError), -} - -impl From for FromEnvelopeError { - fn from(e: signed_envelope::ReadPayloadError) -> Self { - Self::BadPayload(e) - } -} - -impl From for FromEnvelopeError { - fn from(e: DecodingError) -> Self { - Self::InvalidPeerRecord(e) - } -} - -impl fmt::Display for FromEnvelopeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BadPayload(_) => write!(f, "Failed to extract payload from envelope"), - Self::InvalidPeerRecord(_) => write!(f, "Failed to decode payload as PeerRecord"), - } - } -} - impl std::error::Error for FromEnvelopeError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::InvalidPeerRecord(inner) => Some(inner), + Self::InvalidPeerId(inner) => Some(inner), + Self::InvalidMultiaddr(inner) => Some(inner), Self::BadPayload(inner) => Some(inner), } } @@ -227,11 +187,10 @@ mod tests { #[test] fn roundtrip_envelope() { let record = - AuthenticatedPeerRecord::new(Keypair::generate_ed25519(), vec![HOME.parse().unwrap()]) - .unwrap(); + PeerRecord::new(Keypair::generate_ed25519(), vec![HOME.parse().unwrap()]).unwrap(); let envelope = record.to_signed_envelope(); - let reconstructed = AuthenticatedPeerRecord::from_signed_envelope(envelope).unwrap(); + let reconstructed = PeerRecord::from_signed_envelope(envelope).unwrap(); assert_eq!(reconstructed, record) } From dde489d50fcf59ec6cc2f2a06819a4959434a5f9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:15:41 +1000 Subject: [PATCH 108/242] Use stdlib rather than bytes::BufMut --- core/src/signed_envelope.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index a1d8b4339be..bb2bf06d431 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -1,7 +1,6 @@ use crate::identity::error::SigningError; use crate::identity::Keypair; use crate::{identity, PublicKey}; -use prost::bytes::BufMut; use std::convert::TryInto; use std::fmt; use unsigned_varint::encode::usize_buffer; @@ -46,12 +45,12 @@ impl SignedEnvelope { + payload.len(), ); - buffer.put(domain_sep_length); - buffer.put(domain_separation.as_bytes()); - buffer.put(payload_type_length); - buffer.put(payload_type.as_slice()); - buffer.put(payload_length); - buffer.put(payload.as_slice()); + buffer.extend_from_slice(domain_sep_length); + buffer.extend_from_slice(domain_separation.as_bytes()); + buffer.extend_from_slice(payload_type_length); + buffer.extend_from_slice(payload_type.as_slice()); + buffer.extend_from_slice(payload_length); + buffer.extend_from_slice(payload.as_slice()); let signature = key.sign(&buffer)?; @@ -88,12 +87,12 @@ impl SignedEnvelope { + self.payload.len(), ); - buffer.put(domain_sep_length); - buffer.put(domain_separation.as_bytes()); - buffer.put(payload_type_length); - buffer.put(self.payload_type.as_slice()); - buffer.put(payload_length); - buffer.put(self.payload.as_slice()); + buffer.extend_from_slice(domain_sep_length); + buffer.extend_from_slice(domain_separation.as_bytes()); + buffer.extend_from_slice(payload_type_length); + buffer.extend_from_slice(self.payload_type.as_slice()); + buffer.extend_from_slice(payload_length); + buffer.extend_from_slice(self.payload.as_slice()); self.key.verify(&buffer, &self.signature) } From a59a60ddb8aa6b964b526d85f8fc9fd6e52a3a92 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:20:22 +1000 Subject: [PATCH 109/242] Reduce duplication between sign and verify --- core/src/signed_envelope.rs | 91 ++++++++++++++----------------------- 1 file changed, 33 insertions(+), 58 deletions(-) diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index bb2bf06d431..8f1fdcdd785 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -22,35 +22,7 @@ impl SignedEnvelope { payload_type: Vec, payload: Vec, ) -> Result { - // TODO: fix duplication - - let mut domain_sep_length_buffer = usize_buffer(); - let domain_sep_length = - unsigned_varint::encode::usize(domain_separation.len(), &mut domain_sep_length_buffer); - - let mut payload_type_length_buffer = usize_buffer(); - let payload_type_length = - unsigned_varint::encode::usize(payload_type.len(), &mut payload_type_length_buffer); - - let mut payload_length_buffer = usize_buffer(); - let payload_length = - unsigned_varint::encode::usize(payload.len(), &mut payload_length_buffer); - - let mut buffer = Vec::with_capacity( - domain_sep_length.len() - + domain_separation.len() - + payload_type_length.len() - + payload_type.len() - + payload_length.len() - + payload.len(), - ); - - buffer.extend_from_slice(domain_sep_length); - buffer.extend_from_slice(domain_separation.as_bytes()); - buffer.extend_from_slice(payload_type_length); - buffer.extend_from_slice(payload_type.as_slice()); - buffer.extend_from_slice(payload_length); - buffer.extend_from_slice(payload.as_slice()); + let buffer = signature_payload(domain_separation, &payload_type, &payload); let signature = key.sign(&buffer)?; @@ -64,35 +36,7 @@ impl SignedEnvelope { #[must_use] pub fn verify(&self, domain_separation: String) -> bool { - let mut domain_sep_length_buffer = usize_buffer(); - let domain_sep_length = - unsigned_varint::encode::usize(domain_separation.len(), &mut domain_sep_length_buffer); - - let mut payload_type_length_buffer = usize_buffer(); - let payload_type_length = unsigned_varint::encode::usize( - self.payload_type.len(), - &mut payload_type_length_buffer, - ); - - let mut payload_length_buffer = usize_buffer(); - let payload_length = - unsigned_varint::encode::usize(self.payload.len(), &mut payload_length_buffer); - - let mut buffer = Vec::with_capacity( - domain_sep_length.len() - + domain_separation.len() - + payload_type_length.len() - + self.payload_type.len() - + payload_length.len() - + self.payload.len(), - ); - - buffer.extend_from_slice(domain_sep_length); - buffer.extend_from_slice(domain_separation.as_bytes()); - buffer.extend_from_slice(payload_type_length); - buffer.extend_from_slice(self.payload_type.as_slice()); - buffer.extend_from_slice(payload_length); - buffer.extend_from_slice(self.payload.as_slice()); + let buffer = signature_payload(domain_separation, &self.payload_type, &self.payload); self.key.verify(&buffer, &self.signature) } @@ -153,6 +97,37 @@ impl SignedEnvelope { } } +fn signature_payload(domain_separation: String, payload_type: &[u8], payload: &[u8]) -> Vec { + let mut domain_sep_length_buffer = usize_buffer(); + let domain_sep_length = + unsigned_varint::encode::usize(domain_separation.len(), &mut domain_sep_length_buffer); + + let mut payload_type_length_buffer = usize_buffer(); + let payload_type_length = + unsigned_varint::encode::usize(payload_type.len(), &mut payload_type_length_buffer); + + let mut payload_length_buffer = usize_buffer(); + let payload_length = unsigned_varint::encode::usize(payload.len(), &mut payload_length_buffer); + + let mut buffer = Vec::with_capacity( + domain_sep_length.len() + + domain_separation.len() + + payload_type_length.len() + + payload_type.len() + + payload_length.len() + + payload.len(), + ); + + buffer.extend_from_slice(domain_sep_length); + buffer.extend_from_slice(domain_separation.as_bytes()); + buffer.extend_from_slice(payload_type_length); + buffer.extend_from_slice(payload_type); + buffer.extend_from_slice(payload_length); + buffer.extend_from_slice(payload); + + buffer +} + #[derive(Debug)] pub enum DecodingError { /// Decoding the provided bytes as a signed envelope failed. From a9ab281a062f560dbfb222d7d6907cbdab0dbe6d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:25:42 +1000 Subject: [PATCH 110/242] Extend docs of `SignedEnvelope` --- core/src/signed_envelope.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index 8f1fdcdd785..2d48097edc2 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -5,7 +5,9 @@ use std::convert::TryInto; use std::fmt; use unsigned_varint::encode::usize_buffer; -// TODO: docs +/// A signed envelope contains an arbitrary byte string payload, a signature of the payload, and the public key that can be used to verify the signature. +/// +/// For more details see libp2p RFC0002: https://github.com/libp2p/specs/blob/master/RFC/0002-signed-envelopes.md #[derive(Debug, Clone, PartialEq)] pub struct SignedEnvelope { key: PublicKey, @@ -15,7 +17,7 @@ pub struct SignedEnvelope { } impl SignedEnvelope { - // TODO: docs + /// Constructs a new [`SignedEnvelope`]. pub fn new( key: Keypair, domain_separation: String, @@ -34,6 +36,7 @@ impl SignedEnvelope { }) } + /// Verify this [`SignedEnvelope`] against the provided domain-separation string. #[must_use] pub fn verify(&self, domain_separation: String) -> bool { let buffer = signature_payload(domain_separation, &self.payload_type, &self.payload); @@ -41,7 +44,10 @@ impl SignedEnvelope { self.key.verify(&buffer, &self.signature) } - // TODO: docs + /// Extract the payload of this [`SignedEnvelope`]. + /// + /// You must provide the correct domain-separation string and expected payload type in order to get the payload. + /// This guards against accidental mis-use of the payload where the signature was created for a different purpose or payload type. pub fn payload( &self, domain_separation: String, @@ -61,11 +67,7 @@ impl SignedEnvelope { Ok(&self.payload) } - // TODO: Do we need this? - // pub fn payload_unchecked(&self) -> Vec { - // - // } - + /// Encode this [`SignedEnvelope`] using the protobuf encoding specified in the RFC. pub fn into_protobuf_encoding(self) -> Vec { use prost::Message; @@ -80,9 +82,11 @@ impl SignedEnvelope { envelope .encode(&mut buf) .expect("Vec provides capacity as needed"); + buf } + /// Decode a [`SignedEnvelope`] using the protobuf encoding specified in the RFC. pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { use prost::Message; @@ -128,6 +132,7 @@ fn signature_payload(domain_separation: String, payload_type: &[u8], payload: &[ buffer } +/// Errors that occur whilst decoding a [`SignedEnvelope`] from its byte representation. #[derive(Debug)] pub enum DecodingError { /// Decoding the provided bytes as a signed envelope failed. @@ -166,6 +171,7 @@ impl std::error::Error for DecodingError { } } +/// Errors that occur whilst extracting the payload of a [`SignedEnvelope`]. #[derive(Debug)] pub enum ReadPayloadError { /// The signature on the signed envelope does not verify with the provided domain separation string. From de19fda591be14e4438b029a85262b017d579c9b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:29:19 +1000 Subject: [PATCH 111/242] Slightly re-organize PeerRecord implementations --- core/src/peer_record.rs | 44 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index b4e02a09c26..2dea5ea9bdc 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -35,14 +35,18 @@ impl PeerRecord { let payload = envelope.payload(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes())?; let record = peer_record_proto::PeerRecord::decode(payload)?; + let peer_id = PeerId::from_bytes(&record.peer_id)?; + let seq = record.seq; + let addresses = record + .addresses + .into_iter() + .map(|a| a.multiaddr.try_into()) + .collect::, _>>()?; + Ok(Self { - peer_id: PeerId::from_bytes(&record.peer_id)?, - seq: record.seq, - addresses: record - .addresses - .into_iter() - .map(|a| a.multiaddr.try_into()) - .collect::, _>>()?, + peer_id, + seq, + addresses, envelope, }) } @@ -53,26 +57,24 @@ impl PeerRecord { pub fn new(key: Keypair, addresses: Vec) -> Result { use prost::Message; - let secs_since_epoch = SystemTime::now() + let seq = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .expect("now() is never before UNIX_EPOCH") .as_secs(); - let peer_id = key.public().into_peer_id(); - let seq = secs_since_epoch; - - let record = peer_record_proto::PeerRecord { - peer_id: peer_id.to_bytes(), - seq, - addresses: addresses - .iter() - .map(|m| peer_record_proto::peer_record::AddressInfo { - multiaddr: m.to_vec(), - }) - .collect(), - }; let payload = { + let record = peer_record_proto::PeerRecord { + peer_id: peer_id.to_bytes(), + seq, + addresses: addresses + .iter() + .map(|m| peer_record_proto::peer_record::AddressInfo { + multiaddr: m.to_vec(), + }) + .collect(), + }; + let mut buf = Vec::with_capacity(record.encoded_len()); record .encode(&mut buf) From be4bea0441b7ed90e875767923a0864fab264191 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:30:31 +1000 Subject: [PATCH 112/242] Fix link in docs --- core/src/peer_record.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index 2dea5ea9bdc..837a75cf59b 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -53,7 +53,7 @@ impl PeerRecord { /// Construct a new [`PeerRecord`] by authenticating the provided addresses with the given key. /// - /// This is the same key that is used for authenticating every libp2p connection of your application, i.e. what you use when setting up your [`libp2p_core::transport::Transport`]. + /// This is the same key that is used for authenticating every libp2p connection of your application, i.e. what you use when setting up your [`crate::transport::Transport`]. pub fn new(key: Keypair, addresses: Vec) -> Result { use prost::Message; From b33d7d59e6aa4d9d214ce5fde9be66588e75bfc0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:32:25 +1000 Subject: [PATCH 113/242] Improve docs wording --- core/src/peer_record.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index 837a75cf59b..d07f3cf4f6e 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -11,7 +11,7 @@ const DOMAIN_SEP: &str = "libp2p-routing-state"; /// Represents a peer routing record. /// -/// Peer records are designed to be distributable and carry a signature by wrapping them in a signed envelope. +/// Peer records are designed to be distributable and carry a signature by being wrapped in a signed envelope. /// For more information see RFC0003 of the libp2p specifications: https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md #[derive(Debug, PartialEq)] pub struct PeerRecord { From 65d0ba651e8aaa4e1d7a188b2d8a205177359751 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:42:19 +1000 Subject: [PATCH 114/242] Make `PeerRecord`s clonable --- core/src/peer_record.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index d07f3cf4f6e..4984a15627e 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -13,7 +13,7 @@ const DOMAIN_SEP: &str = "libp2p-routing-state"; /// /// Peer records are designed to be distributable and carry a signature by being wrapped in a signed envelope. /// For more information see RFC0003 of the libp2p specifications: https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct PeerRecord { peer_id: PeerId, seq: u64, From 2f0e4c479456a78eae07b074b5034ddee42eb5d9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:51:46 +1000 Subject: [PATCH 115/242] Remodel `RegistrationError` as a struct error --- protocols/rendezvous/src/behaviour.rs | 22 ++++++++++------------ protocols/rendezvous/src/codec.rs | 6 +++--- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 2b956b635b6..bc3007a8b83 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -2,6 +2,7 @@ use crate::codec::{Cookie, ErrorCode, Message, NewRegistration, Registration}; use crate::handler; use crate::handler::{InEvent, RendezvousHandler}; use libp2p_core::connection::ConnectionId; +use libp2p_core::identity::error::SigningError; use libp2p_core::identity::Keypair; use libp2p_core::{Multiaddr, PeerId, PeerRecord}; use libp2p_swarm::{ @@ -11,9 +12,8 @@ use log::debug; use std::collections::hash_map::Entry; use std::collections::{HashMap, VecDeque}; use std::task::{Context, Poll}; -use std::time::{SystemTime}; +use std::time::SystemTime; use uuid::Uuid; -use libp2p_core::identity::error::SigningError; pub struct Rendezvous { events: VecDeque>, @@ -39,10 +39,7 @@ impl Rendezvous { rendezvous_node: PeerId, ttl: Option, ) -> Result<(), SigningError> { - let peer_record = PeerRecord::new( - self.key_pair.clone(), - self.external_addresses.clone() - )?; + let peer_record = PeerRecord::new(self.key_pair.clone(), self.external_addresses.clone())?; self.events .push_back(NetworkBehaviourAction::NotifyHandler { @@ -165,7 +162,7 @@ impl NetworkBehaviour for Rendezvous { }), ] } - Err(RegistrationError::TTLGreaterThanUpperBound { .. }) => { + Err(TtlTooLong { .. }) => { vec![ NetworkBehaviourAction::NotifyHandler { peer_id, @@ -284,9 +281,10 @@ pub struct Registrations { } #[derive(Debug, thiserror::Error)] -pub enum RegistrationError { - #[error("Requested TTL: {requested} is greater than upper bound: {upper_bound} ")] - TTLGreaterThanUpperBound { upper_bound: i64, requested: i64 }, +#[error("Requested TTL {requested}s is longer than what we allow ({upper_bound}s)")] +pub struct TtlTooLong { + upper_bound: i64, + requested: i64, } impl Registrations { @@ -299,10 +297,10 @@ impl Registrations { } } - pub fn add(&mut self, new_registration: NewRegistration) -> Result { + pub fn add(&mut self, new_registration: NewRegistration) -> Result { let ttl = new_registration.effective_ttl(); if ttl > self.ttl_upper_bound { - return Err(RegistrationError::TTLGreaterThanUpperBound { + return Err(TtlTooLong { upper_bound: self.ttl_upper_bound, requested: ttl, }); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 496966f5bce..f8134fbb472 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -340,9 +340,9 @@ impl TryFrom for Message { } => Message::Register(NewRegistration { namespace: ns.ok_or(ConversionError::MissingNamespace)?, ttl, - record: PeerRecord::from_signed_envelope( - SignedEnvelope::from_protobuf_encoding(&signed_peer_record)?, - )?, + record: PeerRecord::from_signed_envelope(SignedEnvelope::from_protobuf_encoding( + &signed_peer_record, + )?)?, }), wire::Message { r#type: Some(1), From 8a4d747dc7a0565f43149e469522822cc8dfe77f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:55:07 +1000 Subject: [PATCH 116/242] Fail if we don't have any external addresses --- protocols/rendezvous/src/behaviour.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index bc3007a8b83..372fba3c7d8 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -38,7 +38,11 @@ impl Rendezvous { namespace: String, rendezvous_node: PeerId, ttl: Option, - ) -> Result<(), SigningError> { + ) -> Result<(), RegisterError> { + if self.external_addresses.is_empty() { + return Err(RegisterError::NoExternalAddresses); + } + let peer_record = PeerRecord::new(self.key_pair.clone(), self.external_addresses.clone())?; self.events @@ -76,6 +80,14 @@ impl Rendezvous { } } +#[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), +} + #[derive(Debug)] pub enum Event { Discovered { From b9ca692676ddab029119c99718d6bdeac8ee30fe Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 15:57:43 +1000 Subject: [PATCH 117/242] Remove noisy logs --- protocols/rendezvous/src/handler.rs | 48 +++-------------------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 3ed502d8a17..bea594e3e6c 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -8,18 +8,14 @@ use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; -use log::debug; -use log::error; -use std::fmt::{Debug, Formatter}; +use std::fmt::Debug; +use std::mem; use std::task::{Context, Poll}; -use std::{fmt, mem}; use void::Void; -#[derive(Debug)] pub struct RendezvousHandler { outbound: SubstreamState, inbound: SubstreamState, - keep_alive: KeepAlive, } impl RendezvousHandler { @@ -27,7 +23,6 @@ impl RendezvousHandler { Self { outbound: SubstreamState::None, inbound: SubstreamState::None, - keep_alive: KeepAlive::Yes, } } } @@ -163,8 +158,6 @@ impl Advance for Inbound { match self { Inbound::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { - debug!("read message from inbound {:?}", msg); - if let Message::Register(..) | Message::Discover { .. } | Message::Unregister { .. } = msg @@ -326,7 +319,6 @@ impl ProtocolsHandler for RendezvousHandler { type OutboundOpenInfo = Message; fn listen_protocol(&self) -> SubstreamProtocol { - debug!("creating substream protocol"); SubstreamProtocol::new(protocol::new(), ()) } @@ -335,7 +327,6 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, _msg: Self::InboundOpenInfo, ) { - debug!("injected inbound"); if let SubstreamState::None = self.inbound { self.inbound = SubstreamState::Active(Inbound::Reading(substream)); } else { @@ -348,7 +339,6 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, msg: Self::OutboundOpenInfo, ) { - debug!("injected outbound"); if let SubstreamState::Active(Outbound::WaitingUpgrade) = self.outbound { self.outbound = SubstreamState::Active(Outbound::PendingSend(substream, msg)); } else { @@ -358,7 +348,6 @@ impl ProtocolsHandler for RendezvousHandler { // event injected from NotifyHandler fn inject_event(&mut self, req: InEvent) { - debug!("injecting event into handler from behaviour: {:?}", &req); let (inbound, outbound) = match ( req, mem::replace(&mut self.inbound, SubstreamState::Poisoned), @@ -424,10 +413,9 @@ impl ProtocolsHandler for RendezvousHandler { fn inject_dial_upgrade_error( &mut self, - _info: Self::OutboundOpenInfo, - error: ProtocolsHandlerUpgrErr, + _: Self::OutboundOpenInfo, + _: ProtocolsHandlerUpgrErr, ) { - error!("Dial upgrade error {:?}", error); } fn connection_keep_alive(&self) -> KeepAlive { @@ -446,9 +434,6 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { - debug!("polling handler: inbound_state: {:?}", &self.inbound); - debug!("polling handler: outbound_state {:?}", &self.outbound); - if let Poll::Ready(event) = self.inbound.poll(cx) { return Poll::Ready(event); } @@ -460,28 +445,3 @@ impl ProtocolsHandler for RendezvousHandler { Poll::Pending } } - -impl Debug for Outbound { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Outbound::Start(_) => f.write_str("start"), - Outbound::WaitingUpgrade => f.write_str("waiting_upgrade"), - Outbound::PendingSend(_, _) => f.write_str("pending_send"), - Outbound::PendingFlush(_) => f.write_str("pending_flush"), - Outbound::WaitForRemote(_) => f.write_str("waiting_for_remote"), - Outbound::Closing(_) => f.write_str("closing"), - } - } -} - -impl Debug for Inbound { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Inbound::Reading(_) => f.write_str("reading"), - Inbound::PendingSend(_, _) => f.write_str("pending_send"), - Inbound::PendingFlush(_) => f.write_str("pending_flush"), - Inbound::WaitForBehaviour(_) => f.write_str("waiting_for_behaviour"), - Inbound::Closing(_) => f.write_str("closing"), - } - } -} From 444e7a10313495da3beb72a009bb9d2830fe530b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 16:00:31 +1000 Subject: [PATCH 118/242] Unify naming of substream states --- protocols/rendezvous/src/handler.rs | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index bea594e3e6c..94bee17239b 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -122,13 +122,13 @@ enum Inbound { /// We are in the process of reading a message from the substream. Reading(Framed), /// We read a message, dispatched it to the behaviour and are waiting for the response. - WaitForBehaviour(Framed), + PendingBehaviour(Framed), /// We are in the process of sending a response. PendingSend(Framed, Message), /// We started sending and are currently flushing the data out. PendingFlush(Framed), /// We've sent the message and are now closing down the substream. - Closing(Framed), + PendingClose(Framed), } /// The state of an outbound substream (i.e. we opened it). @@ -136,15 +136,15 @@ enum Outbound { /// We got a message to send from the behaviour. Start(Message), /// We've requested a substream and are waiting for it to be set up. - WaitingUpgrade, + PendingSubstream, /// We got the substream, now we need to send the message. PendingSend(Framed, Message), /// We sent the message, now we need to flush the data out. PendingFlush(Framed), /// We are waiting for the response from the remote. - WaitForRemote(Framed), + PendingRemote(Framed), /// We got a message from the remote and dispatched it to the behaviour, now we are closing down the substream. - Closing(Framed), + PendingClose(Framed), } impl Advance for Inbound { @@ -164,7 +164,7 @@ impl Advance for Inbound { { Next::Return { poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), - next_state: Inbound::WaitForBehaviour(substream), + next_state: Inbound::PendingBehaviour(substream), } } else { panic!("Invalid inbound message"); @@ -182,9 +182,9 @@ impl Advance for Inbound { next_state: Inbound::Reading(substream), }, }, - Inbound::WaitForBehaviour(substream) => Next::Return { + Inbound::PendingBehaviour(substream) => Next::Return { poll: Poll::Pending, - next_state: Inbound::WaitForBehaviour(substream), + next_state: Inbound::PendingBehaviour(substream), }, Inbound::PendingSend(mut substream, message) => match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { @@ -205,7 +205,7 @@ impl Advance for Inbound { }, Inbound::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { Poll::Ready(Ok(())) => Next::Continue { - next_state: Inbound::Closing(substream), + next_state: Inbound::PendingClose(substream), }, Poll::Ready(Err(e)) => panic!("pending send from inbound error: {:?}", e), Poll::Pending => Next::Return { @@ -213,11 +213,11 @@ impl Advance for Inbound { next_state: Inbound::PendingFlush(substream), }, }, - Inbound::Closing(mut substream) => match substream.poll_close_unpin(cx) { + Inbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { Poll::Ready(..) => Next::Done, Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: Inbound::Closing(substream), + next_state: Inbound::PendingClose(substream), }, }, } @@ -237,11 +237,11 @@ impl Advance for Outbound { poll: Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol: SubstreamProtocol::new(protocol::new(), msg), }), - next_state: Outbound::WaitingUpgrade, + next_state: Outbound::PendingSubstream, }, - Outbound::WaitingUpgrade => Next::Return { + Outbound::PendingSubstream => Next::Return { poll: Poll::Pending, - next_state: Outbound::WaitingUpgrade, + next_state: Outbound::PendingSubstream, }, Outbound::PendingSend(mut substream, message) => match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { @@ -262,7 +262,7 @@ impl Advance for Outbound { }, Outbound::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { Poll::Ready(Ok(())) => Next::Continue { - next_state: Outbound::WaitForRemote(substream), + next_state: Outbound::PendingRemote(substream), }, Poll::Ready(Err(e)) => { panic!("Error when flushing outbound: {:?}", e); @@ -272,7 +272,7 @@ impl Advance for Outbound { next_state: Outbound::PendingFlush(substream), }, }, - Outbound::WaitForRemote(mut substream) => match substream.poll_next_unpin(cx) { + Outbound::PendingRemote(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { if let Message::DiscoverResponse { .. } | Message::RegisterResponse { .. } @@ -281,7 +281,7 @@ impl Advance for Outbound { { Next::Return { poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), - next_state: Outbound::Closing(substream), + next_state: Outbound::PendingClose(substream), } } else { panic!("Invalid inbound message"); @@ -295,14 +295,14 @@ impl Advance for Outbound { } Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: Outbound::WaitForRemote(substream), + next_state: Outbound::PendingRemote(substream), }, }, - Outbound::Closing(mut substream) => match substream.poll_close_unpin(cx) { + Outbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { Poll::Ready(..) => Next::Done, Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: Outbound::Closing(substream), + next_state: Outbound::PendingClose(substream), }, }, } @@ -339,7 +339,7 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, msg: Self::OutboundOpenInfo, ) { - if let SubstreamState::Active(Outbound::WaitingUpgrade) = self.outbound { + if let SubstreamState::Active(Outbound::PendingSubstream) = self.outbound { self.outbound = SubstreamState::Active(Outbound::PendingSend(substream, msg)); } else { unreachable!("Invalid outbound state") @@ -370,7 +370,7 @@ impl ProtocolsHandler for RendezvousHandler { ), ( InEvent::RegisterResponse { ttl }, - SubstreamState::Active(Inbound::WaitForBehaviour(substream)), + SubstreamState::Active(Inbound::PendingBehaviour(substream)), outbound, ) => ( SubstreamState::Active(Inbound::PendingSend( @@ -381,7 +381,7 @@ impl ProtocolsHandler for RendezvousHandler { ), ( InEvent::DeclineRegisterRequest { error }, - SubstreamState::Active(Inbound::WaitForBehaviour(substream)), + SubstreamState::Active(Inbound::PendingBehaviour(substream)), outbound, ) => ( SubstreamState::Active(Inbound::PendingSend( @@ -392,7 +392,7 @@ impl ProtocolsHandler for RendezvousHandler { ), ( InEvent::DiscoverResponse { discovered, cookie }, - SubstreamState::Active(Inbound::WaitForBehaviour(substream)), + SubstreamState::Active(Inbound::PendingBehaviour(substream)), outbound, ) => ( SubstreamState::Active(Inbound::PendingSend( From 09ad2502b9f87f608517593abd534f429d087fbc Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 16:06:54 +1000 Subject: [PATCH 119/242] Close the substream on invalid messages --- protocols/rendezvous/src/handler.rs | 41 +++++++++++++++++------------ 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 94bee17239b..290ee8b2892 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -158,16 +158,20 @@ impl Advance for Inbound { match self { Inbound::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { - if let Message::Register(..) - | Message::Discover { .. } - | Message::Unregister { .. } = msg - { - Next::Return { + match msg { + Message::Register(..) + | Message::Discover { .. } + | Message::Unregister { .. } => Next::Return { poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), next_state: Inbound::PendingBehaviour(substream), - } - } else { - panic!("Invalid inbound message"); + }, + // receiving these messages on an inbound substream is a protocol violation + Message::DiscoverResponse { .. } + | Message::RegisterResponse { .. } + | Message::FailedToDiscover { .. } + | Message::FailedToRegister { .. } => Next::Continue { + next_state: Inbound::PendingClose(substream), + }, } } Poll::Ready(Some(Err(e))) => { @@ -274,17 +278,20 @@ impl Advance for Outbound { }, Outbound::PendingRemote(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { - if let Message::DiscoverResponse { .. } - | Message::RegisterResponse { .. } - | Message::FailedToDiscover { .. } - | Message::FailedToRegister { .. } = msg - { - Next::Return { + match msg { + Message::DiscoverResponse { .. } + | Message::RegisterResponse { .. } + | Message::FailedToDiscover { .. } + | Message::FailedToRegister { .. } => Next::Return { poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), next_state: Outbound::PendingClose(substream), - } - } else { - panic!("Invalid inbound message"); + }, + // receiving these messages on an outbound substream is a protocol violation + Message::Register(_) + | Message::Unregister { .. } + | Message::Discover { .. } => Next::Continue { + next_state: Outbound::PendingClose(substream), + }, } } Poll::Ready(Some(Err(e))) => { From ee747067e006fdf2bc977111a7a03f64712174f1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 16:17:56 +1000 Subject: [PATCH 120/242] Handle error upon reading from outbound gracefully --- protocols/rendezvous/src/handler.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 290ee8b2892..710adc3eb34 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -295,7 +295,10 @@ impl Advance for Outbound { } } Poll::Ready(Some(Err(e))) => { - panic!("Error when receiving message from outbound: {:?}", e) + log::debug!("Failed to read message from substream: {}", e); + Next::Continue { + next_state: Outbound::PendingClose(substream), + } } Poll::Ready(None) => { panic!("Honestly no idea what to do if this happens"); From 15e0c67ebf422f8c7bbe16854b8fa732774ec7c2 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 16:19:12 +1000 Subject: [PATCH 121/242] Handle EOF gracefully --- protocols/rendezvous/src/handler.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 710adc3eb34..7e232128528 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -301,7 +301,8 @@ impl Advance for Outbound { } } Poll::Ready(None) => { - panic!("Honestly no idea what to do if this happens"); + log::debug!("Unexpected EOF while waiting for response from remote"); + Next::Done } Poll::Pending => Next::Return { poll: Poll::Pending, From b5a536472b03efebb880d75a34593a5c17b99ebc Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 16:21:15 +1000 Subject: [PATCH 122/242] Handle errors during sending gracefully --- protocols/rendezvous/src/handler.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 7e232128528..9a1d4546520 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -253,11 +253,17 @@ impl Advance for Outbound { next_state: Outbound::PendingFlush(substream), }, Err(e) => { - panic!("Error when sending outbound: {:?}", e); + log::debug!("Failed to send message on outbound substream: {}", e); + Next::Continue { + next_state: Outbound::PendingClose(substream), + } } }, Poll::Ready(Err(e)) => { - panic!("Error when sending outbound: {:?}", e); + log::debug!("Failed to send message on outbound substream: {}", e); + Next::Continue { + next_state: Outbound::PendingClose(substream), + } } Poll::Pending => Next::Return { poll: Poll::Pending, @@ -269,7 +275,10 @@ impl Advance for Outbound { next_state: Outbound::PendingRemote(substream), }, Poll::Ready(Err(e)) => { - panic!("Error when flushing outbound: {:?}", e); + log::debug!("Failed to send message on outbound substream: {}", e); + Next::Continue { + next_state: Outbound::PendingClose(substream), + } } Poll::Pending => Next::Return { poll: Poll::Pending, From c4c8795134ec3998f71dfd8b36876f552a97ccde Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 16:23:15 +1000 Subject: [PATCH 123/242] Allow converting a `ConversionError` to an error code --- protocols/rendezvous/src/codec.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index f8134fbb472..14dccbcab71 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -454,6 +454,21 @@ pub enum ConversionError { BadCookie(#[from] InvalidCookie), } +impl ConversionError { + pub fn to_error_code(&self) -> ErrorCode { + match self { + ConversionError::MissingNamespace => ErrorCode::InvalidNamespace, + ConversionError::MissingSignedPeerRecord => ErrorCode::InvalidSignedPeerRecord, + ConversionError::BadSignedEnvelope(_) => ErrorCode::InvalidSignedPeerRecord, + ConversionError::BadSignedPeerRecord(_) => ErrorCode::InvalidSignedPeerRecord, + ConversionError::BadCookie(_) => ErrorCode::InvalidCookie, + ConversionError::MissingTtl => ErrorCode::InvalidTtl, + ConversionError::InconsistentWireMessage => ErrorCode::InternalError, + ConversionError::BadStatusCode => ErrorCode::InternalError + } + } +} + impl TryFrom for ErrorCode { type Error = NotAnError; From cf8135fbbeb1614f58c5bfc5f5a22a96edbe440d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 16:31:42 +1000 Subject: [PATCH 124/242] DRY --- protocols/rendezvous/src/handler.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 9a1d4546520..0f2a42df6af 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -150,11 +150,7 @@ enum Outbound { impl Advance for Inbound { type Event = ProtocolsHandlerEvent; - fn advance( - self, - cx: &mut Context<'_>, - ) -> Next> - { + fn advance(self, cx: &mut Context<'_>) -> Next { match self { Inbound::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { @@ -231,11 +227,7 @@ impl Advance for Inbound { impl Advance for Outbound { type Event = ProtocolsHandlerEvent; - fn advance( - self, - cx: &mut Context<'_>, - ) -> Next> - { + fn advance(self, cx: &mut Context<'_>) -> Next { match self { Outbound::Start(msg) => Next::Return { poll: Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { From 7a2bc6d98d15ead3bfc7b6e274a2faee44e51a07 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 16:52:26 +1000 Subject: [PATCH 125/242] Close connection on errors --- protocols/rendezvous/src/handler.rs | 145 ++++++++++++++++------------ 1 file changed, 85 insertions(+), 60 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 0f2a42df6af..c737f6e9906 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -78,8 +78,8 @@ enum Next { Return { poll: Poll, next_state: S }, /// Continue with polling the state. Continue { next_state: S }, - /// The state machine finished. - Done, + /// The state machine finished gracefully. + Done { final_event: Option }, } impl SubstreamState @@ -108,8 +108,12 @@ where *self = SubstreamState::Active(next_state); return poll; } - Next::Done => { + Next::Done { final_event } => { *self = SubstreamState::None; + if let Some(final_event) = final_event { + return Poll::Ready(final_event); + } + return Poll::Pending; } } @@ -143,12 +147,25 @@ enum Outbound { PendingFlush(Framed), /// We are waiting for the response from the remote. PendingRemote(Framed), - /// We got a message from the remote and dispatched it to the behaviour, now we are closing down the substream. + /// We are closing down the substream. PendingClose(Framed), } +/// Errors that can occur while interacting with a substream. +#[derive(Debug, thiserror::Error)] +pub enum SubstreamError { + #[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, +} + impl Advance for Inbound { - type Event = ProtocolsHandlerEvent; + type Event = ProtocolsHandlerEvent; fn advance(self, cx: &mut Context<'_>) -> Next { match self { @@ -162,21 +179,24 @@ impl Advance for Inbound { next_state: Inbound::PendingBehaviour(substream), }, // receiving these messages on an inbound substream is a protocol violation - Message::DiscoverResponse { .. } - | Message::RegisterResponse { .. } - | Message::FailedToDiscover { .. } - | Message::FailedToRegister { .. } => Next::Continue { - next_state: Inbound::PendingClose(substream), + m @ Message::DiscoverResponse { .. } + | m @ Message::RegisterResponse { .. } + | m @ Message::FailedToDiscover { .. } + | m @ Message::FailedToRegister { .. } => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close( + SubstreamError::BadMessage(m), + )), }, } } - Poll::Ready(Some(Err(e))) => { - // TODO: investigate the error here and send an appropriate error response - panic!("Error when reading inbound: {:?}", e); - } - Poll::Ready(None) => { - panic!("Honestly no idea what to do if this happens"); - } + Poll::Ready(Some(Err(e))) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::ReadMessage(e))), + }, + Poll::Ready(None) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close( + SubstreamError::UnexpectedEndOfStream, + )), + }, Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Inbound::Reading(substream), @@ -191,13 +211,17 @@ impl Advance for Inbound { Ok(()) => Next::Continue { next_state: Inbound::PendingFlush(substream), }, - Err(e) => { - panic!("pending send from inbound error: {:?}", e); - } + Err(e) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close( + SubstreamError::WriteMessage(e), + )), + }, + }, + Poll::Ready(Err(e)) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::WriteMessage( + e, + ))), }, - Poll::Ready(Err(e)) => { - panic!("pending send from inbound error: {:?}", e); - } Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Inbound::PendingSend(substream, message), @@ -207,14 +231,18 @@ impl Advance for Inbound { Poll::Ready(Ok(())) => Next::Continue { next_state: Inbound::PendingClose(substream), }, - Poll::Ready(Err(e)) => panic!("pending send from inbound error: {:?}", e), + Poll::Ready(Err(e)) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::WriteMessage( + e, + ))), + }, Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Inbound::PendingFlush(substream), }, }, Inbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => Next::Done, + Poll::Ready(..) => Next::Done { final_event: None }, Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Inbound::PendingClose(substream), @@ -225,7 +253,7 @@ impl Advance for Inbound { } impl Advance for Outbound { - type Event = ProtocolsHandlerEvent; + type Event = ProtocolsHandlerEvent; fn advance(self, cx: &mut Context<'_>) -> Next { match self { @@ -244,19 +272,17 @@ impl Advance for Outbound { Ok(()) => Next::Continue { next_state: Outbound::PendingFlush(substream), }, - Err(e) => { - log::debug!("Failed to send message on outbound substream: {}", e); - Next::Continue { - next_state: Outbound::PendingClose(substream), - } - } + Err(e) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close( + SubstreamError::WriteMessage(e), + )), + }, + }, + Poll::Ready(Err(e)) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::WriteMessage( + e, + ))), }, - Poll::Ready(Err(e)) => { - log::debug!("Failed to send message on outbound substream: {}", e); - Next::Continue { - next_state: Outbound::PendingClose(substream), - } - } Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Outbound::PendingSend(substream, message), @@ -266,12 +292,11 @@ impl Advance for Outbound { Poll::Ready(Ok(())) => Next::Continue { next_state: Outbound::PendingRemote(substream), }, - Poll::Ready(Err(e)) => { - log::debug!("Failed to send message on outbound substream: {}", e); - Next::Continue { - next_state: Outbound::PendingClose(substream), - } - } + Poll::Ready(Err(e)) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::WriteMessage( + e, + ))), + }, Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Outbound::PendingFlush(substream), @@ -288,30 +313,30 @@ impl Advance for Outbound { next_state: Outbound::PendingClose(substream), }, // receiving these messages on an outbound substream is a protocol violation - Message::Register(_) - | Message::Unregister { .. } - | Message::Discover { .. } => Next::Continue { - next_state: Outbound::PendingClose(substream), + m @ Message::Register(_) + | m @ Message::Unregister { .. } + | m @ Message::Discover { .. } => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close( + SubstreamError::BadMessage(m), + )), }, } } - Poll::Ready(Some(Err(e))) => { - log::debug!("Failed to read message from substream: {}", e); - Next::Continue { - next_state: Outbound::PendingClose(substream), - } - } - Poll::Ready(None) => { - log::debug!("Unexpected EOF while waiting for response from remote"); - Next::Done - } + Poll::Ready(Some(Err(e))) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::ReadMessage(e))), + }, + Poll::Ready(None) => Next::Done { + final_event: Some(ProtocolsHandlerEvent::Close( + SubstreamError::UnexpectedEndOfStream, + )), + }, Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Outbound::PendingRemote(substream), }, }, Outbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => Next::Done, + Poll::Ready(..) => Next::Done { final_event: None }, Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Outbound::PendingClose(substream), @@ -324,7 +349,7 @@ impl Advance for Outbound { impl ProtocolsHandler for RendezvousHandler { type InEvent = InEvent; type OutEvent = OutEvent; - type Error = codec::Error; + type Error = SubstreamError; type InboundProtocol = protocol::Rendezvous; type OutboundProtocol = protocol::Rendezvous; type InboundOpenInfo = (); From eca21a5815eb689c93c1d189a70eb6bca2cd5bb5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 17:55:17 +1000 Subject: [PATCH 126/242] Add TODO for simplification once or-patterns hit stable --- protocols/rendezvous/src/handler.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index c737f6e9906..a15868e70b7 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -171,6 +171,7 @@ impl Advance for Inbound { match self { Inbound::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { + // TODO: Lift these patterns out once or-patterns hits stable (1.53) match msg { Message::Register(..) | Message::Discover { .. } @@ -304,6 +305,7 @@ impl Advance for Outbound { }, Outbound::PendingRemote(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { + // TODO: Lift these patterns out once or-patterns hits stable (1.53) match msg { Message::DiscoverResponse { .. } | Message::RegisterResponse { .. } From 42ead09fc8c39e6bc085107f3dd48cda34f45128 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 17:57:17 +1000 Subject: [PATCH 127/242] Shorten type and field names to fit things onto one line --- protocols/rendezvous/src/handler.rs | 60 ++++++++++------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index a15868e70b7..56273369d7e 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -79,7 +79,7 @@ enum Next { /// Continue with polling the state. Continue { next_state: S }, /// The state machine finished gracefully. - Done { final_event: Option }, + Done { event: Option }, } impl SubstreamState @@ -108,7 +108,7 @@ where *self = SubstreamState::Active(next_state); return poll; } - Next::Done { final_event } => { + Next::Done { event: final_event } => { *self = SubstreamState::None; if let Some(final_event) = final_event { return Poll::Ready(final_event); @@ -153,7 +153,7 @@ enum Outbound { /// Errors that can occur while interacting with a substream. #[derive(Debug, thiserror::Error)] -pub enum SubstreamError { +pub enum Error { #[error("Reading message {0:?} at this stage is a protocol violation")] BadMessage(Message), #[error("Failed to write message to substream")] @@ -165,7 +165,7 @@ pub enum SubstreamError { } impl Advance for Inbound { - type Event = ProtocolsHandlerEvent; + type Event = ProtocolsHandlerEvent; fn advance(self, cx: &mut Context<'_>) -> Next { match self { @@ -184,19 +184,15 @@ impl Advance for Inbound { | m @ Message::RegisterResponse { .. } | m @ Message::FailedToDiscover { .. } | m @ Message::FailedToRegister { .. } => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close( - SubstreamError::BadMessage(m), - )), + event: Some(ProtocolsHandlerEvent::Close(Error::BadMessage(m))), }, } } Poll::Ready(Some(Err(e))) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::ReadMessage(e))), + event: Some(ProtocolsHandlerEvent::Close(Error::ReadMessage(e))), }, Poll::Ready(None) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close( - SubstreamError::UnexpectedEndOfStream, - )), + event: Some(ProtocolsHandlerEvent::Close(Error::UnexpectedEndOfStream)), }, Poll::Pending => Next::Return { poll: Poll::Pending, @@ -213,15 +209,11 @@ impl Advance for Inbound { next_state: Inbound::PendingFlush(substream), }, Err(e) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close( - SubstreamError::WriteMessage(e), - )), + event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), }, }, Poll::Ready(Err(e)) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::WriteMessage( - e, - ))), + event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), }, Poll::Pending => Next::Return { poll: Poll::Pending, @@ -233,9 +225,7 @@ impl Advance for Inbound { next_state: Inbound::PendingClose(substream), }, Poll::Ready(Err(e)) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::WriteMessage( - e, - ))), + event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), }, Poll::Pending => Next::Return { poll: Poll::Pending, @@ -243,7 +233,7 @@ impl Advance for Inbound { }, }, Inbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => Next::Done { final_event: None }, + Poll::Ready(..) => Next::Done { event: None }, Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Inbound::PendingClose(substream), @@ -254,7 +244,7 @@ impl Advance for Inbound { } impl Advance for Outbound { - type Event = ProtocolsHandlerEvent; + type Event = ProtocolsHandlerEvent; fn advance(self, cx: &mut Context<'_>) -> Next { match self { @@ -274,15 +264,11 @@ impl Advance for Outbound { next_state: Outbound::PendingFlush(substream), }, Err(e) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close( - SubstreamError::WriteMessage(e), - )), + event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), }, }, Poll::Ready(Err(e)) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::WriteMessage( - e, - ))), + event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), }, Poll::Pending => Next::Return { poll: Poll::Pending, @@ -294,9 +280,7 @@ impl Advance for Outbound { next_state: Outbound::PendingRemote(substream), }, Poll::Ready(Err(e)) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::WriteMessage( - e, - ))), + event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), }, Poll::Pending => Next::Return { poll: Poll::Pending, @@ -318,19 +302,15 @@ impl Advance for Outbound { m @ Message::Register(_) | m @ Message::Unregister { .. } | m @ Message::Discover { .. } => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close( - SubstreamError::BadMessage(m), - )), + event: Some(ProtocolsHandlerEvent::Close(Error::BadMessage(m))), }, } } Poll::Ready(Some(Err(e))) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close(SubstreamError::ReadMessage(e))), + event: Some(ProtocolsHandlerEvent::Close(Error::ReadMessage(e))), }, Poll::Ready(None) => Next::Done { - final_event: Some(ProtocolsHandlerEvent::Close( - SubstreamError::UnexpectedEndOfStream, - )), + event: Some(ProtocolsHandlerEvent::Close(Error::UnexpectedEndOfStream)), }, Poll::Pending => Next::Return { poll: Poll::Pending, @@ -338,7 +318,7 @@ impl Advance for Outbound { }, }, Outbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => Next::Done { final_event: None }, + Poll::Ready(..) => Next::Done { event: None }, Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Outbound::PendingClose(substream), @@ -351,7 +331,7 @@ impl Advance for Outbound { impl ProtocolsHandler for RendezvousHandler { type InEvent = InEvent; type OutEvent = OutEvent; - type Error = SubstreamError; + type Error = Error; type InboundProtocol = protocol::Rendezvous; type OutboundProtocol = protocol::Rendezvous; type InboundOpenInfo = (); From ebc6f03b796fc3078a40f6fac4693b8ec1cf9a68 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 18:03:12 +1000 Subject: [PATCH 128/242] Move `SubstreamState` to dedicated module --- protocols/rendezvous/src/handler.rs | 74 ++------------------------- protocols/rendezvous/src/lib.rs | 1 + protocols/rendezvous/src/substream.rs | 68 ++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 69 deletions(-) create mode 100644 protocols/rendezvous/src/substream.rs diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 56273369d7e..ca9d856fe10 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,6 +1,8 @@ -use crate::codec::{Cookie, ErrorCode, Message, Registration}; -use crate::codec::{NewRegistration, RendezvousCodec}; -use crate::{codec, protocol}; +use crate::codec::{ + self, Cookie, ErrorCode, Message, NewRegistration, Registration, RendezvousCodec, +}; +use crate::protocol; +use crate::substream::{Advance, Next, SubstreamState}; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; use libp2p_core::{InboundUpgrade, OutboundUpgrade}; @@ -55,72 +57,6 @@ pub enum InEvent { }, } -#[derive(Debug)] -enum SubstreamState { - /// There is no substream. - None, - /// The substream is in an active state. - Active(S), - /// Something went seriously wrong. - Poisoned, -} - -/// Advances a state machine. -trait Advance: Sized { - type Event; - - fn advance(self, cx: &mut Context<'_>) -> Next; -} - -/// Defines the results of advancing a state machine. -enum Next { - /// Return from the `poll` function, either because are `Ready` or there is no more work to do (`Pending`). - Return { poll: Poll, next_state: S }, - /// Continue with polling the state. - Continue { next_state: S }, - /// The state machine finished gracefully. - Done { event: Option }, -} - -impl SubstreamState -where - S: Advance, -{ - fn poll(&mut self, cx: &mut Context<'_>) -> Poll { - loop { - let next = match mem::replace(self, SubstreamState::Poisoned) { - SubstreamState::None => { - *self = SubstreamState::None; - return Poll::Pending; - } - SubstreamState::Active(state) => state.advance(cx), - SubstreamState::Poisoned => { - unreachable!("reached poisoned state") - } - }; - - match next { - Next::Continue { next_state } => { - *self = SubstreamState::Active(next_state); - continue; - } - Next::Return { poll, next_state } => { - *self = SubstreamState::Active(next_state); - return poll; - } - Next::Done { event: final_event } => { - *self = SubstreamState::None; - if let Some(final_event) = final_event { - return Poll::Ready(final_event); - } - - return Poll::Pending; - } - } - } - } -} - /// The state of an inbound substream (i.e. the remote node opened it). enum Inbound { /// We are in the process of reading a message from the substream. diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 8a04e547830..8d5432fd806 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -2,3 +2,4 @@ pub mod behaviour; pub mod codec; mod handler; mod protocol; +mod substream; diff --git a/protocols/rendezvous/src/substream.rs b/protocols/rendezvous/src/substream.rs new file mode 100644 index 00000000000..9e044127fc6 --- /dev/null +++ b/protocols/rendezvous/src/substream.rs @@ -0,0 +1,68 @@ +use std::mem; +use std::task::{Context, Poll}; + +#[derive(Debug)] +pub enum SubstreamState { + /// There is no substream. + None, + /// The substream is in an active state. + Active(S), + /// Something went seriously wrong. + Poisoned, +} + +/// Advances a state machine. +pub trait Advance: Sized { + type Event; + + fn advance(self, cx: &mut Context<'_>) -> Next; +} + +/// Defines the results of advancing a state machine. +pub enum Next { + /// Return from the `poll` function, either because are `Ready` or there is no more work to do (`Pending`). + Return { poll: Poll, next_state: S }, + /// Continue with polling the state. + Continue { next_state: S }, + /// The state machine finished gracefully. + Done { event: Option }, +} + +impl SubstreamState +where + S: Advance, +{ + pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll { + loop { + let next = match mem::replace(self, SubstreamState::Poisoned) { + SubstreamState::None => { + *self = SubstreamState::None; + return Poll::Pending; + } + SubstreamState::Active(state) => state.advance(cx), + SubstreamState::Poisoned => { + unreachable!("reached poisoned state") + } + }; + + match next { + Next::Continue { next_state } => { + *self = SubstreamState::Active(next_state); + continue; + } + Next::Return { poll, next_state } => { + *self = SubstreamState::Active(next_state); + return poll; + } + Next::Done { event: final_event } => { + *self = SubstreamState::None; + if let Some(final_event) = final_event { + return Poll::Ready(final_event); + } + + return Poll::Pending; + } + } + } + } +} From 1f3156f6cfa013578176b50f2ab9556dc0abdabb Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 18:05:21 +1000 Subject: [PATCH 129/242] Allow passing cookies to a discover request --- protocols/rendezvous/src/behaviour.rs | 4 ++-- protocols/rendezvous/src/handler.rs | 6 +++--- protocols/rendezvous/tests/rendezvous.rs | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 372fba3c7d8..eaf35637bbc 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -70,11 +70,11 @@ impl Rendezvous { }); } - pub fn discover(&mut self, ns: Option, rendezvous_node: PeerId) { + pub fn discover(&mut self, ns: Option, cookie: Option, rendezvous_node: PeerId) { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: InEvent::DiscoverRequest { namespace: ns }, + event: InEvent::DiscoverRequest { namespace: ns, cookie }, handler: NotifyHandler::Any, }); } diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index ca9d856fe10..a48e38f4d74 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -45,8 +45,8 @@ pub enum InEvent { }, DiscoverRequest { namespace: Option, + cookie: Option // TODO limit: Option - // TODO cookie: Option }, RegisterResponse { ttl: i64, @@ -316,11 +316,11 @@ impl ProtocolsHandler for RendezvousHandler { inbound, SubstreamState::Active(Outbound::Start(Message::Unregister { namespace })), ), - (InEvent::DiscoverRequest { namespace }, inbound, SubstreamState::None) => ( + (InEvent::DiscoverRequest { namespace, cookie }, inbound, SubstreamState::None) => ( inbound, SubstreamState::Active(Outbound::Start(Message::Discover { namespace, - cookie: None, + cookie, })), ), ( diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 28fd8cb219f..42b32263238 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -24,6 +24,7 @@ async fn given_successful_registration_then_successful_discovery() { test.discovery_swarm.behaviour_mut().discover( Some(namespace.clone()), + None, test.rendezvous_swarm.local_peer_id().clone(), ); @@ -55,6 +56,7 @@ async fn given_successful_registration_then_refresh_ttl() { test.discovery_swarm.behaviour_mut().discover( Some(namespace.clone()), + None, test.rendezvous_swarm.local_peer_id().clone(), ); @@ -76,6 +78,7 @@ async fn given_successful_registration_then_refresh_ttl() { test.discovery_swarm.behaviour_mut().discover( Some(namespace.clone()), + None, test.rendezvous_swarm.local_peer_id().clone(), ); From 79a49dde4d1ffb962136198e0155e93c4ad5dd4d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 18:10:05 +1000 Subject: [PATCH 130/242] Exhaustively match result on polling close and explain handling --- protocols/rendezvous/src/handler.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index a48e38f4d74..b3d573b341e 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -45,8 +45,7 @@ pub enum InEvent { }, DiscoverRequest { namespace: Option, - cookie: Option - // TODO limit: Option + cookie: Option, // TODO limit: Option }, RegisterResponse { ttl: i64, @@ -169,7 +168,8 @@ impl Advance for Inbound { }, }, Inbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => Next::Done { event: None }, + Poll::Ready(Ok(())) => Next::Done { event: None }, + Poll::Ready(Err(_)) => Next::Done { event: None }, // there is nothing we can do about an error during close Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Inbound::PendingClose(substream), @@ -254,7 +254,8 @@ impl Advance for Outbound { }, }, Outbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(..) => Next::Done { event: None }, + Poll::Ready(Ok(())) => Next::Done { event: None }, + Poll::Ready(Err(_)) => Next::Done { event: None }, // there is nothing we can do about an error during close Poll::Pending => Next::Return { poll: Poll::Pending, next_state: Outbound::PendingClose(substream), @@ -318,10 +319,7 @@ impl ProtocolsHandler for RendezvousHandler { ), (InEvent::DiscoverRequest { namespace, cookie }, inbound, SubstreamState::None) => ( inbound, - SubstreamState::Active(Outbound::Start(Message::Discover { - namespace, - cookie, - })), + SubstreamState::Active(Outbound::Start(Message::Discover { namespace, cookie })), ), ( InEvent::RegisterResponse { ttl }, From f59cdcfc095cac13eeb22132152d43f32a7ae45b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 18:25:17 +1000 Subject: [PATCH 131/242] Group events into client and server side --- protocols/rendezvous/src/behaviour.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index eaf35637bbc..0ee66f48821 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -95,10 +95,6 @@ pub enum Event { registrations: HashMap<(String, PeerId), Registration>, cookie: Cookie, }, - AnsweredDiscoverRequest { - enquirer: PeerId, - registrations: Vec, - }, FailedToDiscover { rendezvous_node: PeerId, err_code: ErrorCode, @@ -113,6 +109,10 @@ pub enum Event { err_code: ErrorCode, // TODO: get the namespace in as well, needs association between the registration request and the response }, + AnsweredDiscoverRequest { + enquirer: PeerId, + registrations: Vec, + }, DeclinedRegisterRequest { peer: PeerId, // TODO: get the namespace in as well, needs association between the registration request and the response From 3ae1cd959dcc6bb83801f018a48f7c3232bec7b0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 18:38:11 +1000 Subject: [PATCH 132/242] Rename event variants for consistency and add documentation --- protocols/rendezvous/src/behaviour.rs | 50 +++++++++++++++--------- protocols/rendezvous/src/codec.rs | 2 +- protocols/rendezvous/tests/rendezvous.rs | 4 +- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 0ee66f48821..d449a7b0127 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -70,11 +70,19 @@ impl Rendezvous { }); } - pub fn discover(&mut self, ns: Option, cookie: Option, rendezvous_node: PeerId) { + pub fn discover( + &mut self, + ns: Option, + cookie: Option, + rendezvous_node: PeerId, + ) { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: InEvent::DiscoverRequest { namespace: ns, cookie }, + event: InEvent::DiscoverRequest { + namespace: ns, + cookie, + }, handler: NotifyHandler::Any, }); } @@ -90,41 +98,44 @@ pub enum RegisterError { #[derive(Debug)] pub enum Event { + /// We successfully discovered other nodes with using the contained rendezvous node. Discovered { rendezvous_node: PeerId, registrations: HashMap<(String, PeerId), Registration>, cookie: Cookie, }, + /// We failed to discover other nodes on the contained rendezvous node. FailedToDiscover { rendezvous_node: PeerId, err_code: ErrorCode, }, - RegisteredWithRendezvousNode { + /// We successfully registered with the contained rendezvous node. + Registered { rendezvous_node: PeerId, ttl: i64, // TODO: get the namespace in as well, needs association between the registration request and the response }, - FailedToRegisterWithRendezvousNode { + /// We failed to register with the contained rendezvous node. + FailedToRegister { rendezvous_node: PeerId, err_code: ErrorCode, // TODO: get the namespace in as well, needs association between the registration request and the response }, + /// We successfully served a discover request from a peer. AnsweredDiscoverRequest { enquirer: PeerId, registrations: Vec, }, + /// We declined a registration from a peer. DeclinedRegisterRequest { peer: PeerId, // TODO: get the namespace in as well, needs association between the registration request and the response }, - PeerRegistered { - peer: PeerId, - namespace: String, - }, - PeerUnregistered { - peer: PeerId, - namespace: String, - }, + /// A peer successfully registered with us. + // TODO: Include registration here + PeerRegistered { peer: PeerId, namespace: String }, + /// A peer successfully unregistered with us. + PeerUnregistered { peer: PeerId, namespace: String }, } impl NetworkBehaviour for Rendezvous { @@ -192,14 +203,15 @@ impl NetworkBehaviour for Rendezvous { self.events.extend(events); } - Message::RegisterResponse { ttl } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::RegisteredWithRendezvousNode { - rendezvous_node: peer_id, - ttl, - }), - ), + Message::RegisterResponse { ttl } => { + self.events + .push_back(NetworkBehaviourAction::GenerateEvent(Event::Registered { + rendezvous_node: peer_id, + ttl, + })) + } Message::FailedToRegister { error } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::FailedToRegisterWithRendezvousNode { + NetworkBehaviourAction::GenerateEvent(Event::FailedToRegister { rendezvous_node: peer_id, err_code: error, }), diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 14dccbcab71..dc8c657cd6c 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -464,7 +464,7 @@ impl ConversionError { ConversionError::BadCookie(_) => ErrorCode::InvalidCookie, ConversionError::MissingTtl => ErrorCode::InvalidTtl, ConversionError::InconsistentWireMessage => ErrorCode::InternalError, - ConversionError::BadStatusCode => ErrorCode::InternalError + ConversionError::BadStatusCode => ErrorCode::InternalError, } } } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 42b32263238..e1e1e52ae61 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -106,7 +106,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { match await_events_or_timeout(test.rendezvous_swarm.next(), test.registration_swarm.next()).await { ( Event::DeclinedRegisterRequest { .. }, - Event::FailedToRegisterWithRendezvousNode { err_code, .. }, + Event::FailedToRegister { err_code, .. }, ) => { assert_eq!(err_code, ErrorCode::InvalidTtl); } @@ -161,7 +161,7 @@ impl RendezvousTest { match await_events_or_timeout(self.rendezvous_swarm.next(), self.registration_swarm.next()).await { ( Event::PeerRegistered { peer, namespace }, - Event::RegisteredWithRendezvousNode { rendezvous_node, ttl}, + Event::Registered { rendezvous_node, ttl}, ) => { assert_eq!(&peer, self.registration_swarm.local_peer_id()); assert_eq!(&rendezvous_node, self.rendezvous_swarm.local_peer_id()); From a461c58def3c89bb4ece9202ae9bd5712ffd1c14 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 18:39:23 +1000 Subject: [PATCH 133/242] Rename `DeclinedRegisterRequest` to `PeerNotRegistered` for consistency --- protocols/rendezvous/src/behaviour.rs | 10 ++++------ protocols/rendezvous/tests/rendezvous.rs | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index d449a7b0127..c9d366613bf 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -126,14 +126,12 @@ pub enum Event { enquirer: PeerId, registrations: Vec, }, - /// We declined a registration from a peer. - DeclinedRegisterRequest { - peer: PeerId, - // TODO: get the namespace in as well, needs association between the registration request and the response - }, /// A peer successfully registered with us. // TODO: Include registration here PeerRegistered { peer: PeerId, namespace: String }, + /// We declined a registration from a peer. + // TODO: get the namespace in as well, needs association between the registration request and the response + PeerNotRegistered { peer: PeerId }, /// A peer successfully unregistered with us. PeerUnregistered { peer: PeerId, namespace: String }, } @@ -194,7 +192,7 @@ impl NetworkBehaviour for Rendezvous { error: ErrorCode::InvalidTtl, }, }, - NetworkBehaviourAction::GenerateEvent(Event::DeclinedRegisterRequest { + NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { peer: peer_id, }), ] diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index e1e1e52ae61..24045dec0a5 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -105,7 +105,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { match await_events_or_timeout(test.rendezvous_swarm.next(), test.registration_swarm.next()).await { ( - Event::DeclinedRegisterRequest { .. }, + Event::PeerNotRegistered { .. }, Event::FailedToRegister { err_code, .. }, ) => { assert_eq!(err_code, ErrorCode::InvalidTtl); From 55be51fec62d565f4c4b17bb2c8b8eac73b79cd5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 18:43:20 +1000 Subject: [PATCH 134/242] Rename event variants to be more event-y Instead of being a sentence like `FailedToDiscover`, we say `DiscoverFailed` which means that the DISCOVER request failed. --- protocols/rendezvous/src/behaviour.rs | 29 +++++++++++------------- protocols/rendezvous/tests/rendezvous.rs | 4 ++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index c9d366613bf..9159c03babc 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -105,24 +105,21 @@ pub enum Event { cookie: Cookie, }, /// We failed to discover other nodes on the contained rendezvous node. - FailedToDiscover { + DiscoverFailed { rendezvous_node: PeerId, - err_code: ErrorCode, + error: ErrorCode, }, /// We successfully registered with the contained rendezvous node. - Registered { - rendezvous_node: PeerId, - ttl: i64, - // TODO: get the namespace in as well, needs association between the registration request and the response - }, + // TODO: get the namespace in as well, needs association between the registration request and the response + Registered { rendezvous_node: PeerId, ttl: i64 }, /// We failed to register with the contained rendezvous node. - FailedToRegister { + // TODO: get the namespace in as well, needs association between the registration request and the response + RegisterFailed { rendezvous_node: PeerId, - err_code: ErrorCode, - // TODO: get the namespace in as well, needs association between the registration request and the response + error: ErrorCode, }, /// We successfully served a discover request from a peer. - AnsweredDiscoverRequest { + DiscoverServed { enquirer: PeerId, registrations: Vec, }, @@ -209,9 +206,9 @@ impl NetworkBehaviour for Rendezvous { })) } Message::FailedToRegister { error } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::FailedToRegister { + NetworkBehaviourAction::GenerateEvent(Event::RegisterFailed { rendezvous_node: peer_id, - err_code: error, + error, }), ), Message::Unregister { namespace } => { @@ -236,7 +233,7 @@ impl NetworkBehaviour for Rendezvous { }, }); self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::AnsweredDiscoverRequest { + Event::DiscoverServed { enquirer: peer_id, registrations: discovered, }, @@ -256,9 +253,9 @@ impl NetworkBehaviour for Rendezvous { cookie, })), Message::FailedToDiscover { error } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::FailedToDiscover { + NetworkBehaviourAction::GenerateEvent(Event::DiscoverFailed { rendezvous_node: peer_id, - err_code: error, + error: error, }), ), } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 24045dec0a5..e91e3d41bfc 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -106,7 +106,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { match await_events_or_timeout(test.rendezvous_swarm.next(), test.registration_swarm.next()).await { ( Event::PeerNotRegistered { .. }, - Event::FailedToRegister { err_code, .. }, + Event::RegisterFailed { error: err_code, .. }, ) => { assert_eq!(err_code, ErrorCode::InvalidTtl); } @@ -184,7 +184,7 @@ impl RendezvousTest { match await_events_or_timeout(self.rendezvous_swarm.next(), self.discovery_swarm.next()) .await { - (Event::AnsweredDiscoverRequest { .. }, Event::Discovered { registrations, .. }) => { + (Event::DiscoverServed { .. }, Event::Discovered { registrations, .. }) => { if let Some(reg) = registrations.get(&(expected_namespace.clone(), expected_peer_id)) { From 4da0f5353b4664911f317f0baf4a8e5360ecdc2c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 19:23:04 +1000 Subject: [PATCH 135/242] Return namespace in result events of behaviour This allows us to associate a successful / failed register or discover response with the initial request. To achieve this, we need to keep track of the message we sent on a given substream. This introduces some duplication in the event modelling because we can no longer just pass up the message we read from a substream to the behaviour. Interestingly though, modelling this out better has an interesting benefit in that we can now fully validate, that the incoming message fits to what we originally sent. Previously, a rouge rendezvous node could respond with a complete garbage message (like a successful registration on a DISCOVER msg) and we would have processed it as if it would have been expected. --- protocols/rendezvous/src/behaviour.rs | 52 ++++--- protocols/rendezvous/src/codec.rs | 18 +-- protocols/rendezvous/src/handler.rs | 174 ++++++++++++++++------- protocols/rendezvous/tests/rendezvous.rs | 7 +- 4 files changed, 168 insertions(+), 83 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 9159c03babc..7b8e178b1a7 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,6 +1,6 @@ -use crate::codec::{Cookie, ErrorCode, Message, NewRegistration, Registration}; +use crate::codec::{Cookie, ErrorCode, NewRegistration, Registration}; use crate::handler; -use crate::handler::{InEvent, RendezvousHandler}; +use crate::handler::{InEvent, OutEvent, RendezvousHandler}; use libp2p_core::connection::ConnectionId; use libp2p_core::identity::error::SigningError; use libp2p_core::identity::Keypair; @@ -107,15 +107,19 @@ pub enum Event { /// 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. - // TODO: get the namespace in as well, needs association between the registration request and the response - Registered { rendezvous_node: PeerId, ttl: i64 }, + Registered { + rendezvous_node: PeerId, + ttl: i64, + namespace: String, + }, /// We failed to register with the contained rendezvous node. - // TODO: get the namespace in as well, needs association between the registration request and the response RegisterFailed { rendezvous_node: PeerId, + namespace: String, error: ErrorCode, }, /// We successfully served a discover request from a peer. @@ -127,8 +131,11 @@ pub enum Event { // TODO: Include registration here PeerRegistered { peer: PeerId, namespace: String }, /// We declined a registration from a peer. - // TODO: get the namespace in as well, needs association between the registration request and the response - PeerNotRegistered { peer: PeerId }, + PeerNotRegistered { + peer: PeerId, + namespace: String, + error: ErrorCode, + }, /// A peer successfully unregistered with us. PeerUnregistered { peer: PeerId, namespace: String }, } @@ -160,10 +167,10 @@ impl NetworkBehaviour for Rendezvous { &mut self, peer_id: PeerId, _connection: ConnectionId, - message: handler::OutEvent, + event: handler::OutEvent, ) { - match message { - Message::Register(new_registration) => { + match event { + OutEvent::RegistrationRequested(new_registration) => { let namespace = new_registration.namespace.clone(); let events = match self.registrations.add(new_registration) { @@ -181,16 +188,18 @@ impl NetworkBehaviour for Rendezvous { ] } Err(TtlTooLong { .. }) => { + let error = ErrorCode::InvalidTtl; + vec![ NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::Any, - event: InEvent::DeclineRegisterRequest { - error: ErrorCode::InvalidTtl, - }, + event: InEvent::DeclineRegisterRequest { error }, }, NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { peer: peer_id, + namespace, + error, }), ] } @@ -198,24 +207,26 @@ impl NetworkBehaviour for Rendezvous { self.events.extend(events); } - Message::RegisterResponse { ttl } => { + OutEvent::Registered { namespace, ttl } => { self.events .push_back(NetworkBehaviourAction::GenerateEvent(Event::Registered { rendezvous_node: peer_id, ttl, + namespace, })) } - Message::FailedToRegister { error } => self.events.push_back( + OutEvent::RegisterFailed { namespace, error } => self.events.push_back( NetworkBehaviourAction::GenerateEvent(Event::RegisterFailed { rendezvous_node: peer_id, + namespace, error, }), ), - Message::Unregister { namespace } => { + OutEvent::UnregisterRequested { namespace } => { self.registrations.remove(namespace, peer_id); // TODO: Should send unregister response? } - Message::Discover { namespace, cookie } => { + OutEvent::DiscoverRequested { namespace, cookie } => { let (registrations, cookie) = self .registrations .get(namespace, cookie) @@ -239,7 +250,7 @@ impl NetworkBehaviour for Rendezvous { }, )); } - Message::DiscoverResponse { + OutEvent::Discovered { registrations, cookie, } => self @@ -252,10 +263,11 @@ impl NetworkBehaviour for Rendezvous { .collect(), cookie, })), - Message::FailedToDiscover { error } => self.events.push_back( + OutEvent::DiscoverFailed { namespace, error } => self.events.push_back( NetworkBehaviourAction::GenerateEvent(Event::DiscoverFailed { rendezvous_node: peer_id, - error: error, + namespace, + error, }), ), } diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index dc8c657cd6c..2c14043d86e 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -23,10 +23,7 @@ pub enum Message { // TODO limit: Option cookie: Option, }, - DiscoverResponse { - registrations: Vec, - cookie: Cookie, - }, + DiscoverResponse(Vec, Cookie), FailedToDiscover { error: ErrorCode, }, @@ -279,10 +276,7 @@ impl From for wire::Message { unregister: None, discover_response: None, }, - Message::DiscoverResponse { - registrations, - cookie, - } => wire::Message { + Message::DiscoverResponse(registrations, cookie) => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), discover_response: Some(DiscoverResponse { registrations: registrations @@ -374,8 +368,8 @@ impl TryFrom for Message { .. }), .. - } => Message::DiscoverResponse { - registrations: registrations + } => Message::DiscoverResponse( + registrations .into_iter() .map(|reggo| { Ok(Registration { @@ -392,8 +386,8 @@ impl TryFrom for Message { }) }) .collect::, ConversionError>>()?, - cookie: Cookie::from_wire_encoding(cookie)?, - }, + Cookie::from_wire_encoding(cookie)?, + ), wire::Message { r#type: Some(1), register_response: diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index b3d573b341e..89a7b7277ce 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -29,8 +29,34 @@ impl RendezvousHandler { } } -pub type OutEvent = Message; - +#[derive(Debug, Clone)] +pub enum OutEvent { + RegistrationRequested(NewRegistration), + Registered { + namespace: String, + ttl: i64, + }, + RegisterFailed { + namespace: String, + error: ErrorCode, + }, + UnregisterRequested { + namespace: String, + }, + DiscoverRequested { + namespace: Option, + // TODO limit: Option + cookie: Option, + }, + Discovered { + registrations: Vec, + cookie: Cookie, + }, + DiscoverFailed { + namespace: Option, + error: ErrorCode, + }, +} #[derive(Debug)] pub enum InEvent { RegisterRequest { @@ -77,11 +103,20 @@ enum Outbound { /// We've requested a substream and are waiting for it to be set up. PendingSubstream, /// We got the substream, now we need to send the message. - PendingSend(Framed, Message), + PendingSend { + substream: Framed, + to_send: Message, + }, /// We sent the message, now we need to flush the data out. - PendingFlush(Framed), + PendingFlush { + substream: Framed, + sent_message: Message, + }, /// We are waiting for the response from the remote. - PendingRemote(Framed), + PendingRemote { + substream: Framed, + sent_message: Message, + }, /// We are closing down the substream. PendingClose(Framed), } @@ -106,21 +141,26 @@ impl Advance for Inbound { match self { Inbound::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { - // TODO: Lift these patterns out once or-patterns hits stable (1.53) - match msg { - Message::Register(..) - | Message::Discover { .. } - | Message::Unregister { .. } => Next::Return { - poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), - next_state: Inbound::PendingBehaviour(substream), - }, - // receiving these messages on an inbound substream is a protocol violation - m @ Message::DiscoverResponse { .. } - | m @ Message::RegisterResponse { .. } - | m @ Message::FailedToDiscover { .. } - | m @ Message::FailedToRegister { .. } => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::BadMessage(m))), - }, + let event = match msg { + Message::Register(new_registration) => { + OutEvent::RegistrationRequested(new_registration) + } + Message::Discover { cookie, namespace } => { + OutEvent::DiscoverRequested { cookie, namespace } + } + Message::Unregister { namespace } => { + OutEvent::UnregisterRequested { namespace } + } + other => { + return Next::Done { + event: Some(ProtocolsHandlerEvent::Close(Error::BadMessage(other))), + } + } + }; + + Next::Return { + poll: Poll::Ready(ProtocolsHandlerEvent::Custom(event)), + next_state: Inbound::PendingBehaviour(substream), } } Poll::Ready(Some(Err(e))) => Next::Done { @@ -194,10 +234,16 @@ impl Advance for Outbound { poll: Poll::Pending, next_state: Outbound::PendingSubstream, }, - Outbound::PendingSend(mut substream, message) => match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { + Outbound::PendingSend { + mut substream, + to_send: message, + } => match substream.poll_ready_unpin(cx) { + Poll::Ready(Ok(())) => match substream.start_send_unpin(message.clone()) { Ok(()) => Next::Continue { - next_state: Outbound::PendingFlush(substream), + next_state: Outbound::PendingFlush { + substream, + sent_message: message, + }, }, Err(e) => Next::Done { event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), @@ -208,38 +254,67 @@ impl Advance for Outbound { }, Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: Outbound::PendingSend(substream, message), + next_state: Outbound::PendingSend { + substream, + to_send: message, + }, }, }, - Outbound::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { + Outbound::PendingFlush { + mut substream, + sent_message, + } => match substream.poll_flush_unpin(cx) { Poll::Ready(Ok(())) => Next::Continue { - next_state: Outbound::PendingRemote(substream), + next_state: Outbound::PendingRemote { + substream, + sent_message, + }, }, Poll::Ready(Err(e)) => Next::Done { event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), }, Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: Outbound::PendingFlush(substream), + next_state: Outbound::PendingFlush { + substream, + sent_message, + }, }, }, - Outbound::PendingRemote(mut substream) => match substream.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(msg))) => { - // TODO: Lift these patterns out once or-patterns hits stable (1.53) - match msg { - Message::DiscoverResponse { .. } - | Message::RegisterResponse { .. } - | Message::FailedToDiscover { .. } - | Message::FailedToRegister { .. } => Next::Return { - poll: Poll::Ready(ProtocolsHandlerEvent::Custom(msg)), - next_state: Outbound::PendingClose(substream), + Outbound::PendingRemote { + mut substream, + sent_message, + } => match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(received_message))) => { + use Message::*; + use OutEvent::*; + + let event = match (sent_message, received_message) { + (Register(registration), RegisterResponse { ttl }) => Registered { + namespace: registration.namespace, + ttl, }, - // receiving these messages on an outbound substream is a protocol violation - m @ Message::Register(_) - | m @ Message::Unregister { .. } - | m @ Message::Discover { .. } => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::BadMessage(m))), + (Register(registration), FailedToRegister { error }) => RegisterFailed { + namespace: registration.namespace, + error, }, + (Discover { .. }, DiscoverResponse(registrations, cookie)) => Discovered { + registrations, + cookie, + }, + (Discover { namespace, .. }, FailedToDiscover { error }) => { + DiscoverFailed { namespace, error } + } + (_, other) => { + return Next::Done { + event: Some(ProtocolsHandlerEvent::Close(Error::BadMessage(other))), + } + } + }; + + Next::Return { + poll: Poll::Ready(ProtocolsHandlerEvent::Custom(event)), + next_state: Outbound::PendingClose(substream), } } Poll::Ready(Some(Err(e))) => Next::Done { @@ -250,7 +325,10 @@ impl Advance for Outbound { }, Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: Outbound::PendingRemote(substream), + next_state: Outbound::PendingRemote { + substream, + sent_message, + }, }, }, Outbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { @@ -296,7 +374,10 @@ impl ProtocolsHandler for RendezvousHandler { msg: Self::OutboundOpenInfo, ) { if let SubstreamState::Active(Outbound::PendingSubstream) = self.outbound { - self.outbound = SubstreamState::Active(Outbound::PendingSend(substream, msg)); + self.outbound = SubstreamState::Active(Outbound::PendingSend { + substream: substream, + to_send: msg, + }); } else { unreachable!("Invalid outbound state") } @@ -350,10 +431,7 @@ impl ProtocolsHandler for RendezvousHandler { ) => ( SubstreamState::Active(Inbound::PendingSend( substream, - Message::DiscoverResponse { - registrations: discovered, - cookie, - }, + Message::DiscoverResponse(discovered, cookie), )), outbound, ), diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index e91e3d41bfc..f9a53757d1b 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -160,12 +160,13 @@ impl RendezvousTest { ) { match await_events_or_timeout(self.rendezvous_swarm.next(), self.registration_swarm.next()).await { ( - Event::PeerRegistered { peer, namespace }, - Event::Registered { rendezvous_node, ttl}, + Event::PeerRegistered { peer, namespace: rendezvous_node_namespace }, + Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }, ) => { assert_eq!(&peer, self.registration_swarm.local_peer_id()); assert_eq!(&rendezvous_node, self.rendezvous_swarm.local_peer_id()); - assert_eq!(namespace, expected_namespace); + assert_eq!(rendezvous_node_namespace, expected_namespace); + assert_eq!(register_node_namespace, expected_namespace); assert_eq!(ttl, expected_ttl); } (rendezvous_swarm_event, registration_swarm_event) => panic!( From 43f14f99c3206f501dd2ecdd9fb735587b3b6856 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 19:28:58 +1000 Subject: [PATCH 136/242] Remove TODOs that were erroneously copied over --- protocols/rendezvous/src/handler.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 89a7b7277ce..214f755ee78 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -45,7 +45,6 @@ pub enum OutEvent { }, DiscoverRequested { namespace: Option, - // TODO limit: Option cookie: Option, }, Discovered { @@ -67,11 +66,10 @@ pub enum InEvent { }, UnregisterRequest { namespace: String, - // TODO: what is the `id` field here in the PB message }, DiscoverRequest { namespace: Option, - cookie: Option, // TODO limit: Option + cookie: Option, }, RegisterResponse { ttl: i64, From 82b5d7838a3da58136defb6e0ffb7ddbba308a65 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 19:38:42 +1000 Subject: [PATCH 137/242] Model success and error message variants using `Result` This reduces the number of variants. --- protocols/rendezvous/src/codec.rs | 56 ++++++++++++++--------------- protocols/rendezvous/src/handler.rs | 23 ++++++------ 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 2c14043d86e..bb7537a0ca8 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -5,15 +5,12 @@ use std::time::SystemTime; use unsigned_varint::codec::UviBytes; use uuid::Uuid; +pub type Ttl = i64; + #[derive(Debug, Clone)] pub enum Message { Register(NewRegistration), - RegisterResponse { - ttl: i64, - }, - FailedToRegister { - error: ErrorCode, - }, + RegisterResponse(Result), Unregister { namespace: String, // TODO: what is the `id` field here in the PB message @@ -23,10 +20,7 @@ pub enum Message { // TODO limit: Option cookie: Option, }, - DiscoverResponse(Vec, Cookie), - FailedToDiscover { - error: ErrorCode, - }, + DiscoverResponse(Result<(Vec, Cookie), ErrorCode>), } #[derive(Debug, Eq, PartialEq, Hash, Clone)] @@ -229,7 +223,7 @@ impl From for wire::Message { discover: None, discover_response: None, }, - Message::RegisterResponse { ttl } => wire::Message { + Message::RegisterResponse(Ok(ttl)) => wire::Message { r#type: Some(MessageType::RegisterResponse.into()), register_response: Some(RegisterResponse { status: Some(ResponseStatus::Ok.into()), @@ -241,7 +235,7 @@ impl From for wire::Message { unregister: None, discover_response: None, }, - Message::FailedToRegister { error } => wire::Message { + Message::RegisterResponse(Err(error)) => wire::Message { r#type: Some(MessageType::RegisterResponse.into()), register_response: Some(RegisterResponse { status: Some(ResponseStatus::from(error).into()), @@ -276,7 +270,7 @@ impl From for wire::Message { unregister: None, discover_response: None, }, - Message::DiscoverResponse(registrations, cookie) => wire::Message { + Message::DiscoverResponse(Ok((registrations, cookie))) => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), discover_response: Some(DiscoverResponse { registrations: registrations @@ -298,7 +292,7 @@ impl From for wire::Message { unregister: None, register_response: None, }, - Message::FailedToDiscover { error } => wire::Message { + Message::DiscoverResponse(Err(error)) => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), discover_response: Some(DiscoverResponse { registrations: Vec::new(), @@ -347,9 +341,7 @@ impl TryFrom for Message { .. }), .. - } => Message::RegisterResponse { - ttl: ttl.ok_or(ConversionError::MissingTtl)?, - }, + } => Message::RegisterResponse(Ok(ttl.ok_or(ConversionError::MissingTtl)?)), wire::Message { r#type: Some(3), discover: Some(Discover { ns, .. }), @@ -368,8 +360,8 @@ impl TryFrom for Message { .. }), .. - } => Message::DiscoverResponse( - registrations + } => { + let registrations = registrations .into_iter() .map(|reggo| { Ok(Registration { @@ -385,9 +377,11 @@ impl TryFrom for Message { timestamp: SystemTime::now(), }) }) - .collect::, ConversionError>>()?, - Cookie::from_wire_encoding(cookie)?, - ), + .collect::, ConversionError>>()?; + let cookie = Cookie::from_wire_encoding(cookie)?; + + Message::DiscoverResponse(Ok((registrations, cookie))) + } wire::Message { r#type: Some(1), register_response: @@ -396,11 +390,12 @@ impl TryFrom for Message { .. }), .. - } => Message::FailedToRegister { - error: wire::message::ResponseStatus::from_i32(error_code) + } => { + let error = wire::message::ResponseStatus::from_i32(error_code) .ok_or(ConversionError::BadStatusCode)? - .try_into()?, - }, + .try_into()?; + Message::RegisterResponse(Err(error)) + } wire::Message { r#type: Some(2), unregister: Some(Unregister { ns, .. }), @@ -416,11 +411,12 @@ impl TryFrom for Message { .. }), .. - } => Message::FailedToDiscover { - error: wire::message::ResponseStatus::from_i32(error_code) + } => { + let error = wire::message::ResponseStatus::from_i32(error_code) .ok_or(ConversionError::BadStatusCode)? - .try_into()?, - }, + .try_into()?; + Message::DiscoverResponse(Err(error)) + } _ => return Err(ConversionError::InconsistentWireMessage), }; diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 214f755ee78..61fc59a2e2f 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -56,6 +56,7 @@ pub enum OutEvent { error: ErrorCode, }, } + #[derive(Debug)] pub enum InEvent { RegisterRequest { @@ -288,19 +289,21 @@ impl Advance for Outbound { use OutEvent::*; let event = match (sent_message, received_message) { - (Register(registration), RegisterResponse { ttl }) => Registered { + (Register(registration), RegisterResponse(Ok(ttl))) => Registered { namespace: registration.namespace, ttl, }, - (Register(registration), FailedToRegister { error }) => RegisterFailed { + (Register(registration), RegisterResponse(Err(error))) => RegisterFailed { namespace: registration.namespace, error, }, - (Discover { .. }, DiscoverResponse(registrations, cookie)) => Discovered { - registrations, - cookie, - }, - (Discover { namespace, .. }, FailedToDiscover { error }) => { + (Discover { .. }, DiscoverResponse(Ok((registrations, cookie)))) => { + Discovered { + registrations, + cookie, + } + } + (Discover { namespace, .. }, DiscoverResponse(Err(error))) => { DiscoverFailed { namespace, error } } (_, other) => { @@ -407,7 +410,7 @@ impl ProtocolsHandler for RendezvousHandler { ) => ( SubstreamState::Active(Inbound::PendingSend( substream, - Message::RegisterResponse { ttl }, + Message::RegisterResponse(Ok(ttl)), )), outbound, ), @@ -418,7 +421,7 @@ impl ProtocolsHandler for RendezvousHandler { ) => ( SubstreamState::Active(Inbound::PendingSend( substream, - Message::FailedToRegister { error }, + Message::RegisterResponse(Err(error)), )), outbound, ), @@ -429,7 +432,7 @@ impl ProtocolsHandler for RendezvousHandler { ) => ( SubstreamState::Active(Inbound::PendingSend( substream, - Message::DiscoverResponse(discovered, cookie), + Message::DiscoverResponse(Ok((discovered, cookie))), )), outbound, ), From 4ccfbad2f4f961d93d6ca31b1dde81f144ba1e2c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 11 Jun 2021 19:40:19 +1000 Subject: [PATCH 138/242] Add TODOs --- protocols/rendezvous/src/handler.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 61fc59a2e2f..40e5af3be1f 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -365,7 +365,7 @@ impl ProtocolsHandler for RendezvousHandler { if let SubstreamState::None = self.inbound { self.inbound = SubstreamState::Active(Inbound::Reading(substream)); } else { - unreachable!("Invalid inbound state") + unreachable!("Invalid inbound state") // TODO: this unreachable is not correct I believe } } @@ -380,7 +380,7 @@ impl ProtocolsHandler for RendezvousHandler { to_send: msg, }); } else { - unreachable!("Invalid outbound state") + unreachable!("Invalid outbound state") // TODO: this unreachable is not correct I believe } } @@ -436,7 +436,7 @@ impl ProtocolsHandler for RendezvousHandler { )), outbound, ), - _ => unreachable!("Handler in invalid state"), + _ => unreachable!("Handler in invalid state"), // TODO: this unreachable is not correct I believe }; self.inbound = inbound; From 56c92c288e34ffbe6c8de7d37f4eed472bab45af Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 15 Jun 2021 10:15:10 +1000 Subject: [PATCH 139/242] Update protobuf files to latest version --- core/src/envelope.proto | 28 +++++++++++++++++++++++----- core/src/peer_record.proto | 27 ++++++++++++++++----------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/core/src/envelope.proto b/core/src/envelope.proto index 509899a899c..9ab3e6fd256 100644 --- a/core/src/envelope.proto +++ b/core/src/envelope.proto @@ -1,12 +1,30 @@ -syntax = "proto2"; +syntax = "proto3"; package envelope_proto; import "keys.proto"; +// Envelope encloses a signed payload produced by a peer, along with the public +// key of the keypair it was signed with so that it can be statelessly validated +// by the receiver. +// +// The payload is prefixed with a byte string that determines the type, so it +// can be deserialized deterministically. Often, this byte string is a +// multicodec. message Envelope { - required keys_proto.PublicKey public_key = 1; - required bytes payload_type = 2; - required bytes payload = 3; - required bytes signature = 5; + // public_key is the public key of the keypair the enclosed payload was + // signed with. + keys_proto.PublicKey public_key = 1; + + // payload_type encodes the type of payload, so that it can be deserialized + // deterministically. + bytes payload_type = 2; + + // payload is the actual payload carried inside this envelope. + bytes payload = 3; + + // signature is the signature produced by the private key corresponding to + // the enclosed public key, over the payload, prefixing a domain string for + // additional security. + bytes signature = 5; } diff --git a/core/src/peer_record.proto b/core/src/peer_record.proto index 34a8c9abc1b..69bb345e02f 100644 --- a/core/src/peer_record.proto +++ b/core/src/peer_record.proto @@ -1,22 +1,27 @@ -syntax = "proto2"; +syntax = "proto3"; package peer_record_proto; -// PeerRecord contains the listen addresses for a peer at a particular point in time. +// PeerRecord messages contain information that is useful to share with other peers. +// Currently, a PeerRecord contains the public listen addresses for a peer, but this +// is expected to expand to include other information in the future. +// +// PeerRecords are designed to be serialized to bytes and placed inside of +// SignedEnvelopes before sharing with other peers. message PeerRecord { - // AddressInfo wraps a multiaddr. In the future, it may be extended to - // contain additional metadata, such as "routability" (whether an address is - // local or global, etc). + + // AddressInfo is a wrapper around a binary multiaddr. It is defined as a + // separate message to allow us to add per-address metadata in the future. message AddressInfo { - required bytes multiaddr = 1; + bytes multiaddr = 1; } - // the peer id of the subject of the record (who these addresses belong to). - required bytes peer_id = 1; + // peer_id contains a libp2p peer id in its binary representation. + bytes peer_id = 1; - // A monotonically increasing sequence number, used for record ordering. - required uint64 seq = 2; + // seq contains a monotonically-increasing sequence counter to order PeerRecords in time. + uint64 seq = 2; - // All current listen addresses + // addresses is a list of public listen addresses for the peer. repeated AddressInfo addresses = 3; } From 316154792ac71f2043024d86fea7852b2e2ebfa5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 15 Jun 2021 11:56:49 +1000 Subject: [PATCH 140/242] Fix compile errors from new protobuf description --- core/src/signed_envelope.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index 2d48097edc2..4f9dc006f02 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -72,7 +72,7 @@ impl SignedEnvelope { use prost::Message; let envelope = crate::envelope_proto::Envelope { - public_key: self.key.into(), + public_key: Some(self.key.into()), payload_type: self.payload_type, payload: self.payload, signature: self.signature, @@ -93,7 +93,7 @@ impl SignedEnvelope { let envelope = crate::envelope_proto::Envelope::decode(bytes)?; Ok(Self { - key: envelope.public_key.try_into()?, + key: envelope.public_key.ok_or(DecodingError::MissingPublicKey)?.try_into()?, payload_type: envelope.payload_type, payload: envelope.payload, signature: envelope.signature, @@ -139,6 +139,8 @@ pub enum DecodingError { InvalidEnvelope(prost::DecodeError), /// The public key in the envelope could not be converted to our internal public key type. InvalidPublicKey(identity::error::DecodingError), + /// The public key in the envelope could not be converted to our internal public key type. + MissingPublicKey, } impl From for DecodingError { @@ -158,6 +160,7 @@ impl fmt::Display for DecodingError { match self { Self::InvalidEnvelope(_) => write!(f, "Failed to decode envelope"), Self::InvalidPublicKey(_) => write!(f, "Failed to convert public key"), + Self::MissingPublicKey => write!(f, "Public key is missing from protobuf struct") } } } @@ -167,6 +170,7 @@ impl std::error::Error for DecodingError { match self { Self::InvalidEnvelope(inner) => Some(inner), Self::InvalidPublicKey(inner) => Some(inner), + Self::MissingPublicKey => None } } } From 912fcec6429d4756d33640b01fd13f36410cfac9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 15 Jun 2021 11:58:05 +1000 Subject: [PATCH 141/242] Add failing test for expiring registration --- protocols/rendezvous/src/behaviour.rs | 48 +++++++++++++++++++++++---- protocols/rendezvous/src/codec.rs | 2 +- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 7b8e178b1a7..0fa7756bb1f 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -275,7 +275,7 @@ impl NetworkBehaviour for Rendezvous { fn poll( &mut self, - _cx: &mut Context<'_>, + _: &mut Context<'_>, poll_params: &mut impl PollParameters, ) -> Poll< NetworkBehaviourAction< @@ -433,6 +433,10 @@ impl Registrations { Ok((registrations, new_cookie)) } + + pub fn poll(&mut self, _: &mut Context<'_>) -> Poll<()> { + todo!() + } } // TODO: Be more specific in what the bad combination was? @@ -444,6 +448,7 @@ pub struct CookieNamespaceMismatch; mod tests { use super::*; use libp2p_core::identity; + use tokio::time::Instant; #[test] fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { @@ -489,9 +494,11 @@ mod tests { let alice = identity::Keypair::generate_ed25519(); let mut registrations = Registrations::new(7200); registrations - .add(new_registration("foo", alice.clone())) + .add(new_registration("foo", alice.clone(), None)) + .unwrap(); + registrations + .add(new_registration("foo", alice, None)) .unwrap(); - registrations.add(new_registration("foo", alice)).unwrap(); let (discover, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); @@ -529,17 +536,46 @@ mod tests { assert!(matches!(result, Err(CookieNamespaceMismatch))) } + #[tokio::test] + async fn registrations_expire() { + let mut registrations = Registrations::new(7200); + registrations + .add(new_dummy_registration_with_ttl("foo", 5)) + .unwrap(); + + let start_time = Instant::now(); + let event = futures::future::poll_fn(|cx| registrations.poll(cx)).await; + let duration = start_time.elapsed(); + + let (mut discovered, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); + + assert_eq!(event, ()); // TODO: proper "RegistrationExpired" event + assert!(duration.as_secs() > 5); + assert!(duration.as_secs() < 6); + assert_eq!(discovered.next(), None) + } + fn new_dummy_registration(namespace: &str) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); - new_registration(namespace, identity) + new_registration(namespace, identity, None) + } + + fn new_dummy_registration_with_ttl(namespace: &str, ttl: i64) -> NewRegistration { + let identity = identity::Keypair::generate_ed25519(); + + new_registration(namespace, identity, Some(ttl)) } - fn new_registration(namespace: &str, identity: identity::Keypair) -> NewRegistration { + fn new_registration( + namespace: &str, + identity: identity::Keypair, + ttl: Option, + ) -> NewRegistration { NewRegistration::new( namespace.to_owned(), PeerRecord::new(identity, vec!["/ip4/127.0.0.1/tcp/1234".parse().unwrap()]).unwrap(), - None, + ttl, ) } } diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index bb7537a0ca8..c8ee4a4114f 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -115,7 +115,7 @@ impl NewRegistration { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Registration { pub namespace: String, pub record: PeerRecord, From 11647a0680a1935d13e528967ec2c65a9d5353d8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 16 Jun 2021 11:58:20 +1000 Subject: [PATCH 142/242] Extend `Advance` with arbitrary params This allows us to store the history of sent messages outside the state which simplifies the actual state definition. --- protocols/rendezvous/src/handler.rs | 87 ++++++++++++--------------- protocols/rendezvous/src/substream.rs | 7 ++- 2 files changed, 42 insertions(+), 52 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 40e5af3be1f..ab56c960087 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -17,6 +17,7 @@ use void::Void; pub struct RendezvousHandler { outbound: SubstreamState, + outbound_history: Vec, inbound: SubstreamState, } @@ -24,6 +25,7 @@ impl RendezvousHandler { pub fn new() -> Self { Self { outbound: SubstreamState::None, + outbound_history: vec![], inbound: SubstreamState::None, } } @@ -107,15 +109,9 @@ enum Outbound { to_send: Message, }, /// We sent the message, now we need to flush the data out. - PendingFlush { - substream: Framed, - sent_message: Message, - }, + PendingFlush(Framed), /// We are waiting for the response from the remote. - PendingRemote { - substream: Framed, - sent_message: Message, - }, + PendingRemote(Framed), /// We are closing down the substream. PendingClose(Framed), } @@ -135,8 +131,9 @@ pub enum Error { impl Advance for Inbound { type Event = ProtocolsHandlerEvent; + type Params = (); - fn advance(self, cx: &mut Context<'_>) -> Next { + fn advance(self, cx: &mut Context<'_>, _: &mut Self::Params) -> Next { match self { Inbound::Reading(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(msg))) => { @@ -220,8 +217,9 @@ impl Advance for Inbound { impl Advance for Outbound { type Event = ProtocolsHandlerEvent; + type Params = Vec; - fn advance(self, cx: &mut Context<'_>) -> Next { + fn advance(self, cx: &mut Context<'_>, history: &mut Vec) -> Next { match self { Outbound::Start(msg) => Next::Return { poll: Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { @@ -238,12 +236,12 @@ impl Advance for Outbound { to_send: message, } => match substream.poll_ready_unpin(cx) { Poll::Ready(Ok(())) => match substream.start_send_unpin(message.clone()) { - Ok(()) => Next::Continue { - next_state: Outbound::PendingFlush { - substream, - sent_message: message, - }, - }, + Ok(()) => { + history.push(message); + Next::Continue { + next_state: Outbound::PendingFlush(substream), + } + } Err(e) => Next::Done { event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), }, @@ -259,52 +257,45 @@ impl Advance for Outbound { }, }, }, - Outbound::PendingFlush { - mut substream, - sent_message, - } => match substream.poll_flush_unpin(cx) { + Outbound::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { Poll::Ready(Ok(())) => Next::Continue { - next_state: Outbound::PendingRemote { - substream, - sent_message, - }, + next_state: Outbound::PendingRemote(substream), }, Poll::Ready(Err(e)) => Next::Done { event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), }, Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: Outbound::PendingFlush { - substream, - sent_message, - }, + next_state: Outbound::PendingFlush(substream), }, }, - Outbound::PendingRemote { - mut substream, - sent_message, - } => match substream.poll_next_unpin(cx) { + Outbound::PendingRemote(mut substream) => match substream.poll_next_unpin(cx) { Poll::Ready(Some(Ok(received_message))) => { use Message::*; use OutEvent::*; - let event = match (sent_message, received_message) { - (Register(registration), RegisterResponse(Ok(ttl))) => Registered { - namespace: registration.namespace, + let event = match (history.as_slice(), received_message) { + ([.., Register(registration)], RegisterResponse(Ok(ttl))) => Registered { + namespace: registration.namespace.to_owned(), ttl, }, - (Register(registration), RegisterResponse(Err(error))) => RegisterFailed { - namespace: registration.namespace, - error, - }, - (Discover { .. }, DiscoverResponse(Ok((registrations, cookie)))) => { + ([.., Register(registration)], RegisterResponse(Err(error))) => { + RegisterFailed { + namespace: registration.namespace.to_owned(), + error, + } + } + ([.., Discover { .. }], DiscoverResponse(Ok((registrations, cookie)))) => { Discovered { registrations, cookie, } } - (Discover { namespace, .. }, DiscoverResponse(Err(error))) => { - DiscoverFailed { namespace, error } + ([.., Discover { namespace, .. }], DiscoverResponse(Err(error))) => { + DiscoverFailed { + namespace: namespace.to_owned(), + error, + } } (_, other) => { return Next::Done { @@ -326,10 +317,7 @@ impl Advance for Outbound { }, Poll::Pending => Next::Return { poll: Poll::Pending, - next_state: Outbound::PendingRemote { - substream, - sent_message, - }, + next_state: Outbound::PendingRemote(substream), }, }, Outbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { @@ -376,9 +364,10 @@ impl ProtocolsHandler for RendezvousHandler { ) { if let SubstreamState::Active(Outbound::PendingSubstream) = self.outbound { self.outbound = SubstreamState::Active(Outbound::PendingSend { - substream: substream, + substream, to_send: msg, }); + self.outbound_history.clear(); } else { unreachable!("Invalid outbound state") // TODO: this unreachable is not correct I believe } @@ -466,11 +455,11 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { - if let Poll::Ready(event) = self.inbound.poll(cx) { + if let Poll::Ready(event) = self.inbound.poll(cx, &mut ()) { return Poll::Ready(event); } - if let Poll::Ready(event) = self.outbound.poll(cx) { + if let Poll::Ready(event) = self.outbound.poll(cx, &mut self.outbound_history) { return Poll::Ready(event); } diff --git a/protocols/rendezvous/src/substream.rs b/protocols/rendezvous/src/substream.rs index 9e044127fc6..4b8642ed587 100644 --- a/protocols/rendezvous/src/substream.rs +++ b/protocols/rendezvous/src/substream.rs @@ -14,8 +14,9 @@ pub enum SubstreamState { /// Advances a state machine. pub trait Advance: Sized { type Event; + type Params; - fn advance(self, cx: &mut Context<'_>) -> Next; + fn advance(self, cx: &mut Context<'_>, params: &mut Self::Params) -> Next; } /// Defines the results of advancing a state machine. @@ -32,14 +33,14 @@ impl SubstreamState where S: Advance, { - pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll { + pub fn poll(&mut self, cx: &mut Context<'_>, params: &mut S::Params) -> Poll { loop { let next = match mem::replace(self, SubstreamState::Poisoned) { SubstreamState::None => { *self = SubstreamState::None; return Poll::Pending; } - SubstreamState::Active(state) => state.advance(cx), + SubstreamState::Active(state) => state.advance(cx, params), SubstreamState::Poisoned => { unreachable!("reached poisoned state") } From 756fdb5f5c9a1f45dbe8d0ec10dacf540dc812ab Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 17 Jun 2021 16:25:03 +1000 Subject: [PATCH 143/242] Refactor `Advance` to make use of Result This allows us to use `?` inside the state transitions. --- protocols/rendezvous/src/handler.rs | 229 ++++++++++++-------------- protocols/rendezvous/src/substream.rs | 76 ++++++--- 2 files changed, 162 insertions(+), 143 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index ab56c960087..a5f106e1d74 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -130,147 +130,147 @@ pub enum Error { } impl Advance for Inbound { - type Event = ProtocolsHandlerEvent; + type Event = OutEvent; type Params = (); + type Error = Error; + type Protocol = SubstreamProtocol; - fn advance(self, cx: &mut Context<'_>, _: &mut Self::Params) -> Next { - match self { - Inbound::Reading(mut substream) => match substream.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(msg))) => { - let event = match msg { - Message::Register(new_registration) => { - OutEvent::RegistrationRequested(new_registration) - } - Message::Discover { cookie, namespace } => { - OutEvent::DiscoverRequested { cookie, namespace } - } - Message::Unregister { namespace } => { - OutEvent::UnregisterRequested { namespace } - } - other => { - return Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::BadMessage(other))), + fn advance( + self, + cx: &mut Context<'_>, + _: &mut Self::Params, + ) -> Result, Self::Error> { + Ok(match self { + Inbound::Reading(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 } => { + OutEvent::DiscoverRequested { cookie, namespace } + } + Message::Unregister { namespace } => { + OutEvent::UnregisterRequested { namespace } + } + other => return Err(Error::BadMessage(other)), + }; - Next::Return { - poll: Poll::Ready(ProtocolsHandlerEvent::Custom(event)), - next_state: Inbound::PendingBehaviour(substream), + Next::EmitEvent { + event, + next_state: Inbound::PendingBehaviour(substream), + } } + Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), + Poll::Pending => Next::Pending { + next_state: Inbound::Reading(substream), + }, } - Poll::Ready(Some(Err(e))) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::ReadMessage(e))), - }, - Poll::Ready(None) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::UnexpectedEndOfStream)), - }, - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: Inbound::Reading(substream), - }, - }, - Inbound::PendingBehaviour(substream) => Next::Return { - poll: Poll::Pending, + } + Inbound::PendingBehaviour(substream) => Next::Pending { next_state: Inbound::PendingBehaviour(substream), }, - Inbound::PendingSend(mut substream, message) => match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => match substream.start_send_unpin(message) { - Ok(()) => Next::Continue { + Inbound::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: Inbound::PendingFlush(substream), - }, - Err(e) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), - }, - }, - Poll::Ready(Err(e)) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), - }, - Poll::Pending => Next::Return { - poll: Poll::Pending, + } + } + Poll::Pending => Next::Pending { next_state: Inbound::PendingSend(substream, message), }, }, - Inbound::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => Next::Continue { - next_state: Inbound::PendingClose(substream), - }, - Poll::Ready(Err(e)) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), - }, - Poll::Pending => Next::Return { - poll: Poll::Pending, - next_state: Inbound::PendingFlush(substream), - }, - }, + Inbound::PendingFlush(mut substream) => { + match substream + .poll_flush_unpin(cx) + .map_err(Error::WriteMessage)? + { + Poll::Ready(()) => Next::Continue { + next_state: Inbound::PendingClose(substream), + }, + Poll::Pending => Next::Pending { + next_state: Inbound::PendingFlush(substream), + }, + } + } Inbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(Ok(())) => Next::Done { event: None }, - Poll::Ready(Err(_)) => Next::Done { event: None }, // there is nothing we can do about an error during close - Poll::Pending => Next::Return { - poll: Poll::Pending, + 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: Inbound::PendingClose(substream), }, }, - } + }) } } impl Advance for Outbound { - type Event = ProtocolsHandlerEvent; + type Event = OutEvent; type Params = Vec; + type Error = Error; + type Protocol = SubstreamProtocol; - fn advance(self, cx: &mut Context<'_>, history: &mut Vec) -> Next { - match self { - Outbound::Start(msg) => Next::Return { - poll: Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(protocol::new(), msg), - }), + fn advance( + self, + cx: &mut Context<'_>, + history: &mut Vec, + ) -> Result, Self::Error> { + Ok(match self { + Outbound::Start(msg) => Next::OpenSubstream { + protocol: SubstreamProtocol::new(protocol::new(), msg), next_state: Outbound::PendingSubstream, }, - Outbound::PendingSubstream => Next::Return { - poll: Poll::Pending, + Outbound::PendingSubstream => Next::Pending { next_state: Outbound::PendingSubstream, }, Outbound::PendingSend { mut substream, to_send: message, - } => match substream.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => match substream.start_send_unpin(message.clone()) { - Ok(()) => { - history.push(message); - Next::Continue { - next_state: Outbound::PendingFlush(substream), - } + } => match substream + .poll_ready_unpin(cx) + .map_err(Error::WriteMessage)? + { + Poll::Ready(()) => { + substream + .start_send_unpin(message.clone()) + .map_err(Error::WriteMessage)?; + history.push(message); + + Next::Continue { + next_state: Outbound::PendingFlush(substream), } - Err(e) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), - }, - }, - Poll::Ready(Err(e)) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), - }, - Poll::Pending => Next::Return { - poll: Poll::Pending, + } + Poll::Pending => Next::Pending { next_state: Outbound::PendingSend { substream, to_send: message, }, }, }, - Outbound::PendingFlush(mut substream) => match substream.poll_flush_unpin(cx) { - Poll::Ready(Ok(())) => Next::Continue { + Outbound::PendingFlush(mut substream) => match substream + .poll_flush_unpin(cx) + .map_err(Error::WriteMessage)? + { + Poll::Ready(()) => Next::Continue { next_state: Outbound::PendingRemote(substream), }, - Poll::Ready(Err(e)) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::WriteMessage(e))), - }, - Poll::Pending => Next::Return { - poll: Poll::Pending, + Poll::Pending => Next::Pending { next_state: Outbound::PendingFlush(substream), }, }, - Outbound::PendingRemote(mut substream) => match substream.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(received_message))) => { + Outbound::PendingRemote(mut substream) => match substream + .poll_next_unpin(cx) + .map_err(Error::ReadMessage)? + { + Poll::Ready(Some(received_message)) => { use Message::*; use OutEvent::*; @@ -297,38 +297,27 @@ impl Advance for Outbound { error, } } - (_, other) => { - return Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::BadMessage(other))), - } - } + (_, other) => return Err(Error::BadMessage(other)), }; - Next::Return { - poll: Poll::Ready(ProtocolsHandlerEvent::Custom(event)), + Next::EmitEvent { + event, next_state: Outbound::PendingClose(substream), } } - Poll::Ready(Some(Err(e))) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::ReadMessage(e))), - }, - Poll::Ready(None) => Next::Done { - event: Some(ProtocolsHandlerEvent::Close(Error::UnexpectedEndOfStream)), - }, - Poll::Pending => Next::Return { - poll: Poll::Pending, + Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), + Poll::Pending => Next::Pending { next_state: Outbound::PendingRemote(substream), }, }, Outbound::PendingClose(mut substream) => match substream.poll_close_unpin(cx) { - Poll::Ready(Ok(())) => Next::Done { event: None }, - Poll::Ready(Err(_)) => Next::Done { event: None }, // there is nothing we can do about an error during close - Poll::Pending => Next::Return { - poll: Poll::Pending, + 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: Outbound::PendingClose(substream), }, }, - } + }) } } diff --git a/protocols/rendezvous/src/substream.rs b/protocols/rendezvous/src/substream.rs index 4b8642ed587..db25a1f2b9b 100644 --- a/protocols/rendezvous/src/substream.rs +++ b/protocols/rendezvous/src/substream.rs @@ -1,3 +1,4 @@ +use libp2p_swarm::{ProtocolsHandlerEvent, SubstreamProtocol}; use std::mem; use std::task::{Context, Poll}; @@ -11,58 +12,87 @@ pub enum SubstreamState { Poisoned, } -/// Advances a state machine. +/// Advances a substream state machine. +/// +/// pub trait Advance: Sized { type Event; type Params; + type Error; + type Protocol; - fn advance(self, cx: &mut Context<'_>, params: &mut Self::Params) -> Next; + fn advance( + self, + cx: &mut Context<'_>, + params: &mut Self::Params, + ) -> Result, Self::Error>; } /// Defines the results of advancing a state machine. -pub enum Next { - /// Return from the `poll` function, either because are `Ready` or there is no more work to do (`Pending`). - Return { poll: Poll, next_state: S }, - /// Continue with polling the state. - Continue { next_state: S }, - /// The state machine finished gracefully. - Done { event: Option }, +pub enum Next { + /// Return from the `poll` function to emit `event`. Set the state machine to `next_state`. + EmitEvent { event: TEvent, next_state: TState }, + /// Return from the `poll` function because we cannot do any more work. Set the state machine to `next_state`. + Pending { next_state: TState }, + /// Return from the `poll` function to open a new substream. Set the state machine to `next_state`. + OpenSubstream { + protocol: TProtocol, + next_state: TState, + }, + /// Continue with advancing the state machine. + Continue { next_state: TState }, + /// The state machine finished. + Done, } -impl SubstreamState +impl SubstreamState where - S: Advance, + TState: Advance, Error = TError>, { - pub fn poll(&mut self, cx: &mut Context<'_>, params: &mut S::Params) -> Poll { + pub fn poll( + &mut self, + cx: &mut Context<'_>, + params: &mut TState::Params, + ) -> Poll> { loop { - let next = match mem::replace(self, SubstreamState::Poisoned) { + let state = match mem::replace(self, SubstreamState::Poisoned) { SubstreamState::None => { *self = SubstreamState::None; return Poll::Pending; } - SubstreamState::Active(state) => state.advance(cx, params), + SubstreamState::Active(state) => state, SubstreamState::Poisoned => { unreachable!("reached poisoned state") } }; - match next { - Next::Continue { next_state } => { + match state.advance(cx, params) { + Ok(Next::Continue { next_state }) => { *self = SubstreamState::Active(next_state); continue; } - Next::Return { poll, next_state } => { + Ok(Next::EmitEvent { event, next_state }) => { *self = SubstreamState::Active(next_state); - return poll; + return Poll::Ready(ProtocolsHandlerEvent::Custom(event)); } - Next::Done { event: final_event } => { + Ok(Next::Pending { next_state }) => { + *self = SubstreamState::Active(next_state); + return Poll::Pending; + } + Ok(Next::OpenSubstream { + protocol, + next_state, + }) => { + *self = SubstreamState::Active(next_state); + return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { + protocol, + }); + } + Ok(Next::Done) => { *self = SubstreamState::None; - if let Some(final_event) = final_event { - return Poll::Ready(final_event); - } - return Poll::Pending; } + Err(e) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), } } } From 28c0cfbd14f8a3447f649ae5d92095b8c29e610b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 17 Jun 2021 19:09:42 +1000 Subject: [PATCH 144/242] Make sure we always talk to the same handler Our substreams are stateful, we need to make sure we send the event to the right one. --- protocols/rendezvous/src/behaviour.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 0fa7756bb1f..ca11a6f134d 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -166,7 +166,7 @@ impl NetworkBehaviour for Rendezvous { fn inject_event( &mut self, peer_id: PeerId, - _connection: ConnectionId, + connection: ConnectionId, event: handler::OutEvent, ) { match event { @@ -178,7 +178,7 @@ impl NetworkBehaviour for Rendezvous { vec![ NetworkBehaviourAction::NotifyHandler { peer_id, - handler: NotifyHandler::Any, + handler: NotifyHandler::One(connection), event: InEvent::RegisterResponse { ttl: effective_ttl }, }, NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { @@ -193,7 +193,7 @@ impl NetworkBehaviour for Rendezvous { vec![ NetworkBehaviourAction::NotifyHandler { peer_id, - handler: NotifyHandler::Any, + handler: NotifyHandler::One(connection), event: InEvent::DeclineRegisterRequest { error }, }, NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { @@ -237,7 +237,7 @@ impl NetworkBehaviour for Rendezvous { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id, - handler: NotifyHandler::Any, + handler: NotifyHandler::One(connection), event: InEvent::DiscoverResponse { discovered: discovered.clone(), cookie, From a23829c1d3670367a7876fa6f5382b64dfc64714 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 16 Jun 2021 12:04:04 +1000 Subject: [PATCH 145/242] Add implementation of draft PoW proposal --- protocols/rendezvous/Cargo.toml | 2 + protocols/rendezvous/src/behaviour.rs | 92 +++++- protocols/rendezvous/src/codec.rs | 136 ++++++++- protocols/rendezvous/src/handler.rs | 326 +++++++++++++++++----- protocols/rendezvous/src/lib.rs | 1 + protocols/rendezvous/src/pow.rs | 192 +++++++++++++ protocols/rendezvous/src/rpc.proto | 11 + protocols/rendezvous/src/substream.rs | 19 +- protocols/rendezvous/tests/harness/mod.rs | 2 +- protocols/rendezvous/tests/rendezvous.rs | 25 +- 10 files changed, 701 insertions(+), 105 deletions(-) create mode 100644 protocols/rendezvous/src/pow.rs diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index fd14acd1820..31eb96a0aac 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -20,6 +20,8 @@ futures = { version = "0.3", default-features = false, features = ["std"] } thiserror = "1" # TODO: REMOVE FOR PRODUCTION USE unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } uuid = { version = "0.8", features = ["v4"] } +sha2 = "0.9" +rand = "0.8" [dev-dependencies] libp2p-tcp = { path = "../../transports/tcp" } diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index ca11a6f134d..cfc96050678 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,6 +1,8 @@ +pub use crate::pow::Difficulty; + use crate::codec::{Cookie, ErrorCode, NewRegistration, Registration}; use crate::handler; -use crate::handler::{InEvent, OutEvent, RendezvousHandler}; +use crate::handler::{DeclineReason, InEvent, OutEvent, RendezvousHandler}; use libp2p_core::connection::ConnectionId; use libp2p_core::identity::error::SigningError; use libp2p_core::identity::Keypair; @@ -20,15 +22,23 @@ pub struct Rendezvous { registrations: Registrations, key_pair: Keypair, external_addresses: Vec, + + /// The maximum PoW difficulty we are willing to accept from a rendezvous point. + max_accepted_difficulty: Difficulty, } impl Rendezvous { - pub fn new(key_pair: Keypair, ttl_upper_board: i64) -> Self { + pub fn new( + key_pair: Keypair, + ttl_upper_board: i64, + max_accepted_difficulty: Difficulty, + ) -> Self { Self { events: Default::default(), registrations: Registrations::new(ttl_upper_board), key_pair, external_addresses: vec![], + max_accepted_difficulty, } } @@ -145,8 +155,7 @@ impl NetworkBehaviour for Rendezvous { type OutEvent = Event; fn new_handler(&mut self) -> Self::ProtocolsHandler { - debug!("spawning protocol handler"); - RendezvousHandler::new() + RendezvousHandler::new(self.max_accepted_difficulty) } fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { @@ -170,10 +179,27 @@ impl NetworkBehaviour for Rendezvous { event: handler::OutEvent, ) { match event { - OutEvent::RegistrationRequested(new_registration) => { - let namespace = new_registration.namespace.clone(); + OutEvent::RegistrationRequested { + registration, + pow_difficulty: provided_difficulty, + } => { + let expected_difficulty = self.registrations.expected_pow(®istration); + + if expected_difficulty > provided_difficulty { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: InEvent::DeclineRegisterRequest(DeclineReason::PowRequired { + target: expected_difficulty, + }), + }); + return; + } - let events = match self.registrations.add(new_registration) { + let namespace = registration.namespace.clone(); + + let events = match self.registrations.add(registration) { Ok(effective_ttl) => { vec![ NetworkBehaviourAction::NotifyHandler { @@ -194,7 +220,9 @@ impl NetworkBehaviour for Rendezvous { NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: InEvent::DeclineRegisterRequest { error }, + event: InEvent::DeclineRegisterRequest( + DeclineReason::BadRegistration(error), + ), }, NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { peer: peer_id, @@ -363,6 +391,7 @@ impl Registrations { timestamp: SystemTime::now(), }, ); + Ok(ttl) } @@ -434,11 +463,34 @@ impl Registrations { Ok((registrations, new_cookie)) } + pub fn expected_pow(&self, registration: &NewRegistration) -> Difficulty { + let peer = registration.record.peer_id(); + + let num_registrations = self + .registrations_for_peer + .keys() + .filter(|(candidate, _)| candidate == &peer) + .count(); + + difficulty_from_num_registrations(num_registrations) + } + pub fn poll(&mut self, _: &mut Context<'_>) -> Poll<()> { todo!() } } +fn difficulty_from_num_registrations(existing_registrations: usize) -> Difficulty { + if existing_registrations == 0 { + return Difficulty::ZERO; + } + + let new_registrations = existing_registrations + 1; + + Difficulty::from_u32(((new_registrations) as f32 / 2f32).round() as u32) + .unwrap_or(Difficulty::MAX) +} + // TODO: Be more specific in what the bad combination was? #[derive(Debug, thiserror::Error, Eq, PartialEq)] #[error("The provided cookie is not valid for a DISCOVER request for the given namespace")] @@ -555,6 +607,30 @@ mod tests { assert_eq!(discovered.next(), None) } + #[test] + fn first_registration_is_free() { + let required = difficulty_from_num_registrations(0); + let expected = Difficulty::ZERO; + + assert_eq!(required, expected) + } + + #[test] + fn second_registration_is_not_free() { + let required = difficulty_from_num_registrations(1); + let expected = Difficulty::from_u32(1).unwrap(); + + assert_eq!(required, expected) + } + + #[test] + fn fourth_registration_requires_two() { + let required = difficulty_from_num_registrations(3); + let expected = Difficulty::from_u32(2).unwrap(); + + assert_eq!(required, expected) + } + fn new_dummy_registration(namespace: &str) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index c8ee4a4114f..645e5311bde 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -1,5 +1,7 @@ +use crate::pow::Difficulty; use asynchronous_codec::{Bytes, BytesMut, Decoder, Encoder}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; +use rand::RngCore; use std::convert::{TryFrom, TryInto}; use std::time::SystemTime; use unsigned_varint::codec::UviBytes; @@ -10,7 +12,7 @@ pub type Ttl = i64; #[derive(Debug, Clone)] pub enum Message { Register(NewRegistration), - RegisterResponse(Result), + RegisterResponse(Result), Unregister { namespace: String, // TODO: what is the `id` field here in the PB message @@ -21,6 +23,39 @@ pub enum Message { cookie: Option, }, DiscoverResponse(Result<(Vec, Cookie), ErrorCode>), + ProofOfWork { + hash: [u8; 32], + nonce: i64, + }, +} + +#[derive(Debug, Clone)] +pub enum RegisterErrorResponse { + Failed(ErrorCode), // TODO: `Failed` is a bad name, find a better one. + /// We are declining the registration because PoW is required. + PowRequired { + challenge: Challenge, + target: Difficulty, + }, +} + +/// A challenge that needs to be incorporated into the PoW hash by the client. +/// +/// Sending a challenge to the client ensures the PoW is unique to the registration and the client cannot pre-compute or reuse an existing hash. +#[derive(Debug, Clone)] +pub struct Challenge(Vec); + +impl Challenge { + pub fn new(rng: &mut impl RngCore) -> Self { + let mut bytes = [0u8; 32]; + rng.fill_bytes(&mut bytes); + + Self(bytes.to_vec()) + } + + pub fn as_bytes(&self) -> &[u8] { + self.0.as_slice() + } } #[derive(Debug, Eq, PartialEq, Hash, Clone)] @@ -91,9 +126,9 @@ pub struct InvalidCookie; #[derive(Debug, Clone)] pub struct NewRegistration { - pub namespace: String, + pub namespace: String, // TODO: Create new-type for namespace to enforce max-length requirement. pub record: PeerRecord, - pub ttl: Option, + pub ttl: Option, // TODO: Create new-type for ttl to be more descriptive. } /// If unspecified, rendezvous nodes should assume a TTL of 2h. @@ -222,6 +257,7 @@ impl From for wire::Message { unregister: None, discover: None, discover_response: None, + proof_of_work: None, }, Message::RegisterResponse(Ok(ttl)) => wire::Message { r#type: Some(MessageType::RegisterResponse.into()), @@ -229,23 +265,47 @@ impl From for wire::Message { status: Some(ResponseStatus::Ok.into()), status_text: None, ttl: Some(ttl), + challenge: None, + target_difficulty: None, }), register: None, discover: None, unregister: None, discover_response: None, + proof_of_work: None, }, - Message::RegisterResponse(Err(error)) => wire::Message { + Message::RegisterResponse(Err(RegisterErrorResponse::Failed(error))) => wire::Message { r#type: Some(MessageType::RegisterResponse.into()), register_response: Some(RegisterResponse { status: Some(ResponseStatus::from(error).into()), status_text: None, ttl: None, + challenge: None, + target_difficulty: None, + }), + register: None, + discover: None, + unregister: None, + discover_response: None, + proof_of_work: None, + }, + Message::RegisterResponse(Err(RegisterErrorResponse::PowRequired { + challenge, + target, + })) => wire::Message { + r#type: Some(MessageType::RegisterResponse.into()), + register_response: Some(RegisterResponse { + status: Some(ResponseStatus::EPowRequired.into()), + status_text: None, + ttl: None, + challenge: Some(challenge.0), + target_difficulty: Some(target.to_u32()), }), register: None, discover: None, unregister: None, discover_response: None, + proof_of_work: None, }, Message::Unregister { namespace } => wire::Message { r#type: Some(MessageType::Unregister.into()), @@ -257,6 +317,7 @@ impl From for wire::Message { register_response: None, discover: None, discover_response: None, + proof_of_work: None, }, Message::Discover { namespace, cookie } => wire::Message { r#type: Some(MessageType::Discover.into()), @@ -269,6 +330,7 @@ impl From for wire::Message { register_response: None, unregister: None, discover_response: None, + proof_of_work: None, }, Message::DiscoverResponse(Ok((registrations, cookie))) => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), @@ -291,6 +353,7 @@ impl From for wire::Message { discover: None, unregister: None, register_response: None, + proof_of_work: None, }, Message::DiscoverResponse(Err(error)) => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), @@ -304,6 +367,19 @@ impl From for wire::Message { discover: None, unregister: None, register_response: None, + proof_of_work: None, + }, + Message::ProofOfWork { hash, nonce } => wire::Message { + r#type: Some(MessageType::ProofOfWork.into()), + proof_of_work: Some(ProofOfWork { + hash: Some(hash.to_vec()), + nonce: Some(nonce), + }), + register: None, + discover: None, + discover_response: None, + unregister: None, + register_response: None, }, } } @@ -387,14 +463,30 @@ impl TryFrom for Message { register_response: Some(RegisterResponse { status: Some(error_code), + challenge: challenge_bytes, + target_difficulty, .. }), .. } => { let error = wire::message::ResponseStatus::from_i32(error_code) - .ok_or(ConversionError::BadStatusCode)? - .try_into()?; - Message::RegisterResponse(Err(error)) + .ok_or(ConversionError::BadStatusCode)?; + + let error_response = match (error, challenge_bytes, target_difficulty) { + ( + wire::message::ResponseStatus::EPowRequired, + Some(bytes), + Some(target_difficulty), + ) => RegisterErrorResponse::PowRequired { + challenge: Challenge(bytes), + target: Difficulty::from_u32(target_difficulty) + .ok_or(ConversionError::PoWDifficultyOutOfRange)?, + }, + (code, None, None) => RegisterErrorResponse::Failed(code.try_into()?), + _ => return Err(ConversionError::InconsistentWireMessage), + }; + + Message::RegisterResponse(Err(error_response)) } wire::Message { r#type: Some(2), @@ -417,6 +509,18 @@ impl TryFrom for Message { .try_into()?; Message::DiscoverResponse(Err(error)) } + wire::Message { + r#type: Some(5), + proof_of_work: + Some(ProofOfWork { + hash: Some(hash), + nonce: Some(nonce), + }), + .. + } => Message::ProofOfWork { + hash: hash.try_into().map_err(|_| ConversionError::BadPoWHash)?, + nonce, + }, _ => return Err(ConversionError::InconsistentWireMessage), }; @@ -442,6 +546,10 @@ pub enum ConversionError { BadSignedPeerRecord(#[from] peer_record::FromEnvelopeError), #[error(transparent)] BadCookie(#[from] InvalidCookie), + #[error("The requested PoW difficulty is out of range")] + PoWDifficultyOutOfRange, + #[error("The provided PoW hash is not 32 bytes long")] + BadPoWHash, } impl ConversionError { @@ -455,18 +563,20 @@ impl ConversionError { ConversionError::MissingTtl => ErrorCode::InvalidTtl, ConversionError::InconsistentWireMessage => ErrorCode::InternalError, ConversionError::BadStatusCode => ErrorCode::InternalError, + ConversionError::PoWDifficultyOutOfRange => ErrorCode::InternalError, + ConversionError::BadPoWHash => ErrorCode::InternalError, } } } impl TryFrom for ErrorCode { - type Error = NotAnError; + type Error = UnmappableStatusCode; fn try_from(value: wire::message::ResponseStatus) -> Result { use wire::message::ResponseStatus::*; let code = match value { - Ok => return Err(NotAnError), + Ok | EPowRequired | EDifficultyTooLow => return Err(UnmappableStatusCode(value)), EInvalidNamespace => ErrorCode::InvalidNamespace, EInvalidSignedPeerRecord => ErrorCode::InvalidSignedPeerRecord, EInvalidTtl => ErrorCode::InvalidTtl, @@ -496,15 +606,15 @@ impl From for wire::message::ResponseStatus { } } -impl From for ConversionError { - fn from(_: NotAnError) -> Self { +impl From for ConversionError { + fn from(_: UnmappableStatusCode) -> Self { ConversionError::InconsistentWireMessage } } #[derive(Debug, thiserror::Error)] -#[error("The provided response code is not an error code")] -pub struct NotAnError; +#[error("The response code ({0:?}) cannot be mapped to our ErrorCode enum")] +pub struct UnmappableStatusCode(wire::message::ResponseStatus); mod wire { include!(concat!(env!("OUT_DIR"), "/rendezvous.pb.rs")); diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index a5f106e1d74..d0b82e365c5 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,8 +1,10 @@ use crate::codec::{ - self, Cookie, ErrorCode, Message, NewRegistration, Registration, RendezvousCodec, + self, Challenge, Cookie, ErrorCode, Message, NewRegistration, RegisterErrorResponse, + Registration, RendezvousCodec, }; -use crate::protocol; +use crate::pow::Difficulty; use crate::substream::{Advance, Next, SubstreamState}; +use crate::{pow, protocol}; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; use libp2p_core::{InboundUpgrade, OutboundUpgrade}; @@ -12,28 +14,51 @@ use libp2p_swarm::{ }; use std::fmt::Debug; use std::mem; +use std::sync::mpsc::TryRecvError; use std::task::{Context, Poll}; use void::Void; pub struct RendezvousHandler { outbound: SubstreamState, - outbound_history: Vec, + outbound_history: MessageHistory, inbound: SubstreamState, + inbound_history: MessageHistory, + max_difficulty: Difficulty, } impl RendezvousHandler { - pub fn new() -> Self { + pub fn new(max_difficulty: Difficulty) -> Self { Self { outbound: SubstreamState::None, - outbound_history: vec![], + outbound_history: Default::default(), + max_difficulty, inbound: SubstreamState::None, + inbound_history: Default::default(), } } } +#[derive(Default)] +struct MessageHistory { + sent: Vec, + received: Vec, +} + +impl MessageHistory { + fn clear(&mut self) { + self.sent.clear(); + self.received.clear(); + } +} + #[derive(Debug, Clone)] pub enum OutEvent { - RegistrationRequested(NewRegistration), + /// A peer wants to store registration with us. + RegistrationRequested { + registration: NewRegistration, + /// The PoW that was supplied with the registration. + pow_difficulty: Difficulty, + }, Registered { namespace: String, ttl: i64, @@ -64,9 +89,7 @@ pub enum InEvent { RegisterRequest { request: NewRegistration, }, - DeclineRegisterRequest { - error: ErrorCode, - }, + DeclineRegisterRequest(DeclineReason), UnregisterRequest { namespace: String, }, @@ -83,16 +106,22 @@ pub enum InEvent { }, } +#[derive(Debug)] +pub enum DeclineReason { + BadRegistration(ErrorCode), + PowRequired { target: Difficulty }, +} + /// The state of an inbound substream (i.e. the remote node opened it). enum Inbound { /// We are in the process of reading a message from the substream. - Reading(Framed), + 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 started sending and are currently flushing the data out. - PendingFlush(Framed), + /// We started sending and are currently flushing the data out, afterwards we will go and read the next message. + PendingFlushThenRead(Framed), /// We've sent the message and are now closing down the substream. PendingClose(Framed), } @@ -110,6 +139,11 @@ enum Outbound { }, /// We sent the message, now we need to flush the data out. PendingFlush(Framed), + /// We are waiting for our PoW thread to finish. + PendingPoW { + substream: Framed, + channel: std::sync::mpsc::Receiver>, + }, /// We are waiting for the response from the remote. PendingRemote(Framed), /// We are closing down the substream. @@ -127,47 +161,94 @@ pub enum Error { ReadMessage(#[source] codec::Error), #[error("Substream ended unexpectedly mid-protocol")] UnexpectedEndOfStream, + #[error("Failed to compute proof of work")] + PowFailed(#[from] pow::ExhaustedNonceSpace), + #[error("Failed to verify Proof of Work")] + BadPoWSupplied(#[from] pow::VerifyError), + #[error("Rendezvous point requested difficulty {requested} but we are only willing to produce {limit}")] + MaxDifficultyExceeded { + requested: Difficulty, + limit: Difficulty, + }, +} + +struct InboundPollParams<'handler> { + history: &'handler mut MessageHistory, } -impl Advance for Inbound { +impl<'handler> Advance<'handler> for Inbound { type Event = OutEvent; - type Params = (); + type Params = InboundPollParams<'handler>; type Error = Error; type Protocol = SubstreamProtocol; fn advance( self, cx: &mut Context<'_>, - _: &mut Self::Params, + InboundPollParams { history }: &mut Self::Params, ) -> Result, Self::Error> { Ok(match self { - Inbound::Reading(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 } => { - OutEvent::DiscoverRequested { cookie, namespace } - } - Message::Unregister { namespace } => { - OutEvent::UnregisterRequested { namespace } - } - other => return Err(Error::BadMessage(other)), - }; + Inbound::PendingRead(mut substream) => match substream + .poll_next_unpin(cx) + .map_err(Error::ReadMessage)? + { + Poll::Ready(Some(msg)) => { + let event = match ( + history.received.as_slice(), + history.sent.as_slice(), + msg.clone(), + ) { + (.., Message::Register(registration)) => OutEvent::RegistrationRequested { + registration, + pow_difficulty: Difficulty::ZERO, // initial Register has no PoW + }, + // this next pattern matches if: + // 1. the first message we received from this peer was `Register` + // 2. the last message we sent to them was `PowRequired` + // 3. the message we just received is `ProofOfWork` + ( + [Message::Register(registration), ..], + [.., Message::RegisterResponse(Err(RegisterErrorResponse::PowRequired { + challenge, + target: target_difficulty, + }))], + Message::ProofOfWork { hash, nonce }, + ) => { + pow::verify( + challenge.as_bytes(), + registration.namespace.as_str(), + registration.record.to_signed_envelope(), + *target_difficulty, + hash, + nonce, + )?; - Next::EmitEvent { - event, - next_state: Inbound::PendingBehaviour(substream), + OutEvent::RegistrationRequested { + registration: registration.clone(), + pow_difficulty: pow::difficulty_of(&hash), + } + } + (.., Message::Discover { cookie, namespace }) => { + OutEvent::DiscoverRequested { cookie, namespace } } + (.., Message::Unregister { namespace }) => { + OutEvent::UnregisterRequested { namespace } + } + (.., other) => return Err(Error::BadMessage(other)), + }; + + history.received.push(msg); + + Next::EmitEvent { + event, + next_state: Inbound::PendingBehaviour(substream), } - Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), - Poll::Pending => Next::Pending { - next_state: Inbound::Reading(substream), - }, } - } + Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), + Poll::Pending => Next::Pending { + next_state: Inbound::PendingRead(substream), + }, + }, Inbound::PendingBehaviour(substream) => Next::Pending { next_state: Inbound::PendingBehaviour(substream), }, @@ -177,27 +258,40 @@ impl Advance for Inbound { { Poll::Ready(()) => { substream - .start_send_unpin(message) + .start_send_unpin(message.clone()) .map_err(Error::WriteMessage)?; - Next::Continue { - next_state: Inbound::PendingFlush(substream), - } + let next = match message { + // In case we requested PoW from the client, we need to wait for the response and hence go to `PendingFlushThenRead` afterwards + Message::RegisterResponse(Err(RegisterErrorResponse::PowRequired { + .. + })) => Next::Continue { + next_state: Inbound::PendingFlushThenRead(substream), + }, + // In case of any other message, just close the stream (that implies flushing) + _ => Next::Continue { + next_state: Inbound::PendingClose(substream), + }, + }; + + history.sent.push(message); + + next } Poll::Pending => Next::Pending { next_state: Inbound::PendingSend(substream, message), }, }, - Inbound::PendingFlush(mut substream) => { + Inbound::PendingFlushThenRead(mut substream) => { match substream .poll_flush_unpin(cx) .map_err(Error::WriteMessage)? { Poll::Ready(()) => Next::Continue { - next_state: Inbound::PendingClose(substream), + next_state: Inbound::PendingRead(substream), }, Poll::Pending => Next::Pending { - next_state: Inbound::PendingFlush(substream), + next_state: Inbound::PendingFlushThenRead(substream), }, } } @@ -212,16 +306,24 @@ impl Advance for Inbound { } } -impl Advance for Outbound { +struct OutboundPollParams<'handler> { + history: &'handler mut MessageHistory, + max_difficulty: Difficulty, +} + +impl<'handler> Advance<'handler> for Outbound { type Event = OutEvent; - type Params = Vec; + type Params = OutboundPollParams<'handler>; type Error = Error; type Protocol = SubstreamProtocol; fn advance( self, cx: &mut Context<'_>, - history: &mut Vec, + OutboundPollParams { + history, + max_difficulty, + }: &mut OutboundPollParams, ) -> Result, Self::Error> { Ok(match self { Outbound::Start(msg) => Next::OpenSubstream { @@ -242,7 +344,7 @@ impl Advance for Outbound { substream .start_send_unpin(message.clone()) .map_err(Error::WriteMessage)?; - history.push(message); + history.sent.push(message); Next::Continue { next_state: Outbound::PendingFlush(substream), @@ -274,30 +376,76 @@ impl Advance for Outbound { use Message::*; use OutEvent::*; - let event = match (history.as_slice(), received_message) { - ([.., Register(registration)], RegisterResponse(Ok(ttl))) => Registered { + // Absolutely amazing Rust pattern matching ahead! + // We match against the slice of historical messages and the received message. + // [, ..] effectively matches against the first message that we sent on this substream + let event = match (history.sent.as_slice(), received_message) { + ([Register(registration), ..], RegisterResponse(Ok(ttl))) => Registered { namespace: registration.namespace.to_owned(), ttl, }, - ([.., Register(registration)], RegisterResponse(Err(error))) => { - RegisterFailed { - namespace: registration.namespace.to_owned(), - error, - } - } - ([.., Discover { .. }], DiscoverResponse(Ok((registrations, cookie)))) => { + ( + [Register(registration), ..], + RegisterResponse(Err(RegisterErrorResponse::Failed(error))), + ) => RegisterFailed { + namespace: registration.namespace.to_owned(), + error, + }, + ([Discover { .. }, ..], DiscoverResponse(Ok((registrations, cookie)))) => { Discovered { registrations, cookie, } } - ([.., Discover { namespace, .. }], DiscoverResponse(Err(error))) => { + ([Discover { namespace, .. }, ..], DiscoverResponse(Err(error))) => { DiscoverFailed { namespace: namespace.to_owned(), error, } } - (_, other) => return Err(Error::BadMessage(other)), + ( + [Register(registration), ..], + RegisterResponse(Err(RegisterErrorResponse::PowRequired { + challenge, + target: target_difficulty, + })), + ) => { + if target_difficulty > *max_difficulty { + return Err(Error::MaxDifficultyExceeded { + requested: target_difficulty, + limit: *max_difficulty, + }); + } + + let (sender, receiver) = std::sync::mpsc::channel(); + + // do the PoW on a separate thread to not block the networking tasks + std::thread::spawn({ + let waker = cx.waker().clone(); + let registration = registration.clone(); + + move || { + let result = pow::run( + challenge.as_bytes(), + ®istration.namespace, + registration.record.to_signed_envelope(), + target_difficulty, + ); + + waker.wake(); // let the runtime know that we are ready, this should get us polled + + let _ = sender.send(result); + } + }); + + return Ok(Next::Continue { + next_state: Outbound::PendingPoW { + substream, + channel: receiver, + }, + }); + } + (.., other) => return Err(Error::BadMessage(other)), }; Next::EmitEvent { @@ -310,6 +458,26 @@ impl Advance for Outbound { next_state: Outbound::PendingRemote(substream), }, }, + Outbound::PendingPoW { substream, channel } => { + let (hash, nonce) = match channel.try_recv() { + Ok(result) => result?, + Err(TryRecvError::Empty) => { + return Ok(Next::Pending { + next_state: Outbound::PendingPoW { substream, channel }, + }) + } + Err(TryRecvError::Disconnected) => { + unreachable!("sender is never dropped") + } + }; + + return Ok(Next::Continue { + next_state: Outbound::PendingSend { + substream, + to_send: Message::ProofOfWork { hash, nonce }, + }, + }); + } Outbound::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 @@ -340,7 +508,8 @@ impl ProtocolsHandler for RendezvousHandler { _msg: Self::InboundOpenInfo, ) { if let SubstreamState::None = self.inbound { - self.inbound = SubstreamState::Active(Inbound::Reading(substream)); + self.inbound = SubstreamState::Active(Inbound::PendingRead(substream)); + self.inbound_history.clear(); } else { unreachable!("Invalid inbound state") // TODO: this unreachable is not correct I believe } @@ -393,13 +562,29 @@ impl ProtocolsHandler for RendezvousHandler { outbound, ), ( - InEvent::DeclineRegisterRequest { error }, + InEvent::DeclineRegisterRequest(DeclineReason::BadRegistration(error)), + SubstreamState::Active(Inbound::PendingBehaviour(substream)), + outbound, + ) => ( + SubstreamState::Active(Inbound::PendingSend( + substream, + Message::RegisterResponse(Err(RegisterErrorResponse::Failed(error))), + )), + outbound, + ), + ( + InEvent::DeclineRegisterRequest(DeclineReason::PowRequired { + target: target_difficulty, + }), SubstreamState::Active(Inbound::PendingBehaviour(substream)), outbound, ) => ( SubstreamState::Active(Inbound::PendingSend( substream, - Message::RegisterResponse(Err(error)), + Message::RegisterResponse(Err(RegisterErrorResponse::PowRequired { + challenge: Challenge::new(&mut rand::thread_rng()), + target: target_difficulty, + })), )), outbound, ), @@ -444,11 +629,22 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { - if let Poll::Ready(event) = self.inbound.poll(cx, &mut ()) { + if let Poll::Ready(event) = self.inbound.poll( + cx, + &mut InboundPollParams { + history: &mut self.inbound_history, + }, + ) { return Poll::Ready(event); } - if let Poll::Ready(event) = self.outbound.poll(cx, &mut self.outbound_history) { + if let Poll::Ready(event) = self.outbound.poll( + cx, + &mut OutboundPollParams { + history: &mut self.outbound_history, + max_difficulty: self.max_difficulty, + }, + ) { return Poll::Ready(event); } diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 8d5432fd806..83d89c8111e 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,5 +1,6 @@ pub mod behaviour; pub mod codec; mod handler; +mod pow; mod protocol; mod substream; diff --git a/protocols/rendezvous/src/pow.rs b/protocols/rendezvous/src/pow.rs new file mode 100644 index 00000000000..54bb8a62f7f --- /dev/null +++ b/protocols/rendezvous/src/pow.rs @@ -0,0 +1,192 @@ +use libp2p_core::SignedEnvelope; +use sha2::{Digest, Sha256}; +use std::fmt; + +const DOMAIN_TAG: &'static str = "libp2p-rendezvous-pow"; + +/// Run the Proof of Work algorithm for a registration at a rendezvous node. +/// +/// Returns the calculated hash together with the computed nonce. +/// In the odd case that the nonce space is exhausted without meeting the difficulty requirement, the function fails with [`ExhaustedNonceSpace`]. +pub fn run( + challenge: &[u8], + namespace: &str, + envelope: SignedEnvelope, + target_difficulty: Difficulty, +) -> Result<([u8; 32], i64), ExhaustedNonceSpace> { + let envelope = envelope.into_protobuf_encoding(); + + for i in i64::MIN..i64::MAX { + let hash = make_hash(challenge, namespace, envelope.as_slice(), i); + + if difficulty_of(&hash) < target_difficulty { + continue; + } + + return Ok((hash, i)); + } + + Err(ExhaustedNonceSpace) +} + +/// Verifies a PoW hash against the given parameters and requested difficulty. +pub fn verify( + challenge: &[u8], + namespace: &str, + envelope: SignedEnvelope, + requested_difficulty: Difficulty, + hash: [u8; 32], + nonce: i64, +) -> Result<(), VerifyError> { + if difficulty_of(&hash) < requested_difficulty { + return Err(VerifyError::DifficultyTooLow); + } + + let actual_hash = make_hash( + challenge, + namespace, + envelope.into_protobuf_encoding().as_slice(), + nonce, + ); + + if actual_hash != hash { + return Err(VerifyError::NotTheSameHash); + } + + Ok(()) +} + +/// The difficulty of a PoW hash. +/// +/// Measured as the number of leading `0`s at the front of the hash in big-endian representation. +#[derive(Debug, PartialEq, Clone, Copy, PartialOrd)] +pub struct Difficulty(u32); + +impl fmt::Display for Difficulty { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Difficulty { + pub const ZERO: Difficulty = Difficulty(0); + pub const MAX: Difficulty = Difficulty(32); + + pub fn from_u32(value: u32) -> Option { + if value > 32 { + return None; + } + + Some(Difficulty(value)) + } + + pub fn to_u32(&self) -> u32 { + self.0 + } +} + +#[derive(Debug)] +pub struct ExhaustedNonceSpace; + +impl fmt::Display for ExhaustedNonceSpace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Exhausted nonce space while computing proof of work") + } +} + +impl std::error::Error for ExhaustedNonceSpace {} + +#[derive(Debug, PartialEq)] +pub enum VerifyError { + DifficultyTooLow, + NotTheSameHash, +} + +impl fmt::Display for VerifyError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VerifyError::DifficultyTooLow => { + write!(f, "The difficulty of the provided hash is too low") + } + VerifyError::NotTheSameHash => write!(f, "The hash differs unexpectedly"), + } + } +} + +impl std::error::Error for VerifyError {} + +/// Returns the difficulty of the provided hash. +pub fn difficulty_of(hash: &[u8; 32]) -> Difficulty { + for i in 0..32 { + if hash[i as usize] != 0 { + return Difficulty(i); + } + } + + return Difficulty(32); +} + +fn make_hash(challenge: &[u8], namespace: &str, envelope: &[u8], nonce: i64) -> [u8; 32] { + let mut digest = Sha256::new(); + + digest.update(DOMAIN_TAG.as_bytes()); + digest.update(challenge); + digest.update(namespace.as_bytes()); + digest.update(envelope); + digest.update(&nonce.to_le_bytes()); + + digest.finalize().into() +} + +#[cfg(test)] +mod tests { + use super::*; + use libp2p_core::{identity, PeerRecord}; + + #[test] + fn pow_verifies() { + let challenge = b"foobar"; + let namespace = "baz"; + let envelope = PeerRecord::new(identity::Keypair::generate_ed25519(), vec![]) + .unwrap() + .into_signed_envelope(); + + let (hash, nonce) = run(challenge, namespace, envelope.clone(), Difficulty(1)).unwrap(); + let result = verify(challenge, namespace, envelope, Difficulty(1), hash, nonce); + + assert_eq!(result, Ok(())) + } + + #[test] + fn meets_difficulty_test_cases() { + let test_cases = &[ + ( + [ + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, + ], + Difficulty(5), + ), + ( + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, + ], + Difficulty(0), + ), + ( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + Difficulty(32), + ), + ]; + + for (hash, expected_result) in test_cases { + let actual = difficulty_of(&hash); + + assert_eq!(actual, *expected_result); + } + } +} diff --git a/protocols/rendezvous/src/rpc.proto b/protocols/rendezvous/src/rpc.proto index f3cbd9505ff..a80d8d4a015 100644 --- a/protocols/rendezvous/src/rpc.proto +++ b/protocols/rendezvous/src/rpc.proto @@ -9,6 +9,7 @@ message Message { UNREGISTER = 2; DISCOVER = 3; DISCOVER_RESPONSE = 4; + PROOF_OF_WORK = 5; } enum ResponseStatus { @@ -17,6 +18,8 @@ message Message { E_INVALID_SIGNED_PEER_RECORD = 101; E_INVALID_TTL = 102; E_INVALID_COOKIE = 103; + E_POW_REQUIRED = 104; + E_DIFFICULTY_TOO_LOW = 105; E_NOT_AUTHORIZED = 200; E_INTERNAL_ERROR = 300; E_UNAVAILABLE = 400; @@ -32,6 +35,8 @@ message Message { optional ResponseStatus status = 1; optional string statusText = 2; optional int64 ttl = 3; // in seconds + optional bytes challenge = 4; + optional uint32 target_difficulty = 5; } message Unregister { @@ -52,10 +57,16 @@ message Message { optional string statusText = 4; } + message ProofOfWork { + optional bytes hash = 1; + optional int64 nonce = 2; + } + optional MessageType type = 1; optional Register register = 2; optional RegisterResponse registerResponse = 3; optional Unregister unregister = 4; optional Discover discover = 5; optional DiscoverResponse discoverResponse = 6; + optional ProofOfWork proofOfWork = 7; } diff --git a/protocols/rendezvous/src/substream.rs b/protocols/rendezvous/src/substream.rs index db25a1f2b9b..249809cf7f7 100644 --- a/protocols/rendezvous/src/substream.rs +++ b/protocols/rendezvous/src/substream.rs @@ -15,7 +15,7 @@ pub enum SubstreamState { /// Advances a substream state machine. /// /// -pub trait Advance: Sized { +pub trait Advance<'handler>: Sized { type Event; type Params; type Error; @@ -45,15 +45,20 @@ pub enum Next { Done, } -impl SubstreamState -where - TState: Advance, Error = TError>, -{ - pub fn poll( +impl SubstreamState { + pub fn poll<'handler, TEvent, TUpgrade, TInfo, TError>( &mut self, cx: &mut Context<'_>, params: &mut TState::Params, - ) -> Poll> { + ) -> Poll> + where + TState: Advance< + 'handler, + Event = TEvent, + Protocol = SubstreamProtocol, + Error = TError, + >, + { loop { let state = match mem::replace(self, SubstreamState::Poisoned) { SubstreamState::None => { diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index b887fad260a..ded0888f422 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -73,7 +73,7 @@ where B: Debug, { tokio::time::timeout( - Duration::from_secs(10), + Duration::from_secs(30), future::join( async { let e1 = swarm_1_event.await; diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index f9a53757d1b..a3c724f3ba3 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -2,9 +2,9 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use libp2p_core::PeerId; -use libp2p_rendezvous::behaviour::{Event, Rendezvous}; +use libp2p_rendezvous::behaviour::{Difficulty, Event, Rendezvous}; use libp2p_rendezvous::codec::{ErrorCode, DEFAULT_TTL}; -use libp2p_swarm::Swarm; +use libp2p_swarm::{Swarm, SwarmEvent}; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { @@ -127,16 +127,19 @@ const DEFAULT_TTL_UPPER_BOUND: i64 = 56_000; impl RendezvousTest { pub async fn setup() -> Self { - let mut registration_swarm = - new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); + let mut registration_swarm = new_swarm(|_, identity| { + Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND, Difficulty::from_u32(2).expect("2 < 32")) + }); registration_swarm.listen_on_random_memory_address().await; - let mut discovery_swarm = - new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); + let mut discovery_swarm = new_swarm(|_, identity| { + Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND, Difficulty::from_u32(2).expect("2 < 32")) + }); discovery_swarm.listen_on_random_memory_address().await; - let mut rendezvous_swarm = - new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); + let mut rendezvous_swarm = new_swarm(|_, identity| { + Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND, Difficulty::from_u32(2).expect("2 < 32")) + }); rendezvous_swarm.listen_on_random_memory_address().await; registration_swarm @@ -158,10 +161,10 @@ impl RendezvousTest { expected_namespace: String, expected_ttl: i64, ) { - match await_events_or_timeout(self.rendezvous_swarm.next(), self.registration_swarm.next()).await { + match await_events_or_timeout(self.rendezvous_swarm.next_event(), self.registration_swarm.next_event()).await { ( - Event::PeerRegistered { peer, namespace: rendezvous_node_namespace }, - Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }, + SwarmEvent::Behaviour(Event::PeerRegistered { peer, namespace: rendezvous_node_namespace }), + SwarmEvent::Behaviour(Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), ) => { assert_eq!(&peer, self.registration_swarm.local_peer_id()); assert_eq!(&rendezvous_node, self.rendezvous_swarm.local_peer_id()); From a582ea9cc23495625aafb850cedb7d6489a64eb2 Mon Sep 17 00:00:00 2001 From: rishflab Date: Wed, 16 Jun 2021 10:42:51 +1000 Subject: [PATCH 146/242] Remove expired registrations --- protocols/rendezvous/src/behaviour.rs | 63 ++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index cfc96050678..a44f9ea1579 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -14,7 +14,7 @@ use log::debug; use std::collections::hash_map::Entry; use std::collections::{HashMap, VecDeque}; use std::task::{Context, Poll}; -use std::time::SystemTime; +use std::time::{Duration, SystemTime}; use uuid::Uuid; pub struct Rendezvous { @@ -332,6 +332,9 @@ impl RegistrationId { } } +#[derive(Debug, PartialEq)] +pub struct RegistrationsExpired(Vec); + pub struct Registrations { registrations_for_peer: HashMap<(PeerId, String), RegistrationId>, registrations: HashMap, @@ -475,8 +478,37 @@ impl Registrations { difficulty_from_num_registrations(num_registrations) } - pub fn poll(&mut self, _: &mut Context<'_>) -> Poll<()> { - todo!() + pub fn poll(&mut self, _: &mut Context<'_>) -> Poll { + let expired = self + .registrations + .iter() + .filter(|(id, r)| { + let absolute_expiry = r.timestamp + Duration::from_secs(r.ttl as u64); + debug!("absolute expiry: {:?}", absolute_expiry); + let now = SystemTime::now(); + debug!("system time: {:?}", now); + if now > absolute_expiry { + true + } else { + false + } + }) + .map(|(id, _r)| *id) + .collect::>(); + + let expired = expired + .iter() + .map(|id| self.registrations.remove(id)) + .filter(|r| r.is_some()) + //todo!:remove unrwap + .map(|r| r.unwrap()) + .collect::>(); + + if expired.is_empty() { + Poll::Pending + } else { + Poll::Ready(RegistrationsExpired(expired)) + } } } @@ -590,21 +622,28 @@ mod tests { #[tokio::test] async fn registrations_expire() { + env_logger::init(); let mut registrations = Registrations::new(7200); registrations - .add(new_dummy_registration_with_ttl("foo", 5)) + .add(new_dummy_registration_with_ttl("foo", 2)) .unwrap(); - let start_time = Instant::now(); - let event = futures::future::poll_fn(|cx| registrations.poll(cx)).await; - let duration = start_time.elapsed(); + let start_time = SystemTime::now(); + let event = futures::future::poll_fn(|cx| loop { + if let Poll::Ready(reg) = registrations.poll(cx) { + return Poll::Ready(reg); + } + }) + .await; + let elapsed = start_time.elapsed().unwrap(); - let (mut discovered, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); + //let (mut discovered, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); - assert_eq!(event, ()); // TODO: proper "RegistrationExpired" event - assert!(duration.as_secs() > 5); - assert!(duration.as_secs() < 6); - assert_eq!(discovered.next(), None) + debug!("elapsed: {}", elapsed.as_secs()); + assert_eq!(event.0.iter().next().unwrap().namespace, "foo"); // TODO: proper "RegistrationExpired" event + assert!(elapsed.as_secs() >= 2); + assert!(elapsed.as_secs() < 3); + //assert_eq!(discovered.next(), None) } #[test] From 992369882e0e214f0fc321e136fe8b7d2213dd3d Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 18 Jun 2021 09:37:38 +1000 Subject: [PATCH 147/242] Clear registrations_for_peer upon expiry Use bidirectional hashmap to allow efficient removal of registrations_for_peer upon expiry. Add proper registrations expired event. --- protocols/rendezvous/Cargo.toml | 1 + protocols/rendezvous/src/behaviour.rs | 51 +++++++++++++++------------ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 31eb96a0aac..599bf7c368a 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -20,6 +20,7 @@ futures = { version = "0.3", default-features = false, features = ["std"] } thiserror = "1" # TODO: REMOVE FOR PRODUCTION USE unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } uuid = { version = "0.8", features = ["v4"] } +bimap = "0.6.1" sha2 = "0.9" rand = "0.8" diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index a44f9ea1579..b32126920e5 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -3,6 +3,7 @@ pub use crate::pow::Difficulty; use crate::codec::{Cookie, ErrorCode, NewRegistration, Registration}; use crate::handler; use crate::handler::{DeclineReason, InEvent, OutEvent, RendezvousHandler}; +use bimap::BiMap; use libp2p_core::connection::ConnectionId; use libp2p_core::identity::error::SigningError; use libp2p_core::identity::Keypair; @@ -11,7 +12,6 @@ use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; use log::debug; -use std::collections::hash_map::Entry; use std::collections::{HashMap, VecDeque}; use std::task::{Context, Poll}; use std::time::{Duration, SystemTime}; @@ -336,7 +336,7 @@ impl RegistrationId { pub struct RegistrationsExpired(Vec); pub struct Registrations { - registrations_for_peer: HashMap<(PeerId, String), RegistrationId>, + registrations_for_peer: BiMap<(PeerId, String), RegistrationId>, registrations: HashMap, cookies: HashMap>, ttl_upper_bound: i64, @@ -373,15 +373,16 @@ impl Registrations { match self .registrations_for_peer - .entry((new_registration.record.peer_id(), namespace.clone())) + .get_by_left(&(new_registration.record.peer_id(), namespace.clone())) { - Entry::Occupied(mut occupied) => { - let old_registration = occupied.insert(registration_id); - - self.registrations.remove(&old_registration); + Some(old_registration) => { + self.registrations.remove(old_registration); } - Entry::Vacant(vacant) => { - vacant.insert(registration_id); + None => { + self.registrations_for_peer.insert( + (new_registration.record.peer_id(), namespace.clone()), + registration_id, + ); } } @@ -399,9 +400,11 @@ impl Registrations { } pub fn remove(&mut self, namespace: String, peer_id: PeerId) { - let reggo_to_remove = self.registrations_for_peer.remove(&(peer_id, namespace)); + let reggo_to_remove = self + .registrations_for_peer + .remove_by_left(&(peer_id, namespace)); - if let Some(reggo_to_remove) = reggo_to_remove { + if let Some((_, reggo_to_remove)) = reggo_to_remove { self.registrations.remove(®go_to_remove); } } @@ -471,7 +474,7 @@ impl Registrations { let num_registrations = self .registrations_for_peer - .keys() + .left_values() .filter(|(candidate, _)| candidate == &peer) .count(); @@ -479,10 +482,11 @@ impl Registrations { } pub fn poll(&mut self, _: &mut Context<'_>) -> Poll { + // find ids of expired registrations let expired = self .registrations .iter() - .filter(|(id, r)| { + .filter(|(_id, r)| { let absolute_expiry = r.timestamp + Duration::from_secs(r.ttl as u64); debug!("absolute expiry: {:?}", absolute_expiry); let now = SystemTime::now(); @@ -496,18 +500,19 @@ impl Registrations { .map(|(id, _r)| *id) .collect::>(); - let expired = expired + //remove expired registrations + let expired_registrations = expired .iter() - .map(|id| self.registrations.remove(id)) - .filter(|r| r.is_some()) - //todo!:remove unrwap - .map(|r| r.unwrap()) + .filter_map(|id| { + self.registrations_for_peer.remove_by_right(id); + self.registrations.remove(id) + }) .collect::>(); - if expired.is_empty() { + if expired_registrations.is_empty() { Poll::Pending } else { - Poll::Ready(RegistrationsExpired(expired)) + Poll::Ready(RegistrationsExpired(expired_registrations)) } } } @@ -637,13 +642,13 @@ mod tests { .await; let elapsed = start_time.elapsed().unwrap(); - //let (mut discovered, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); + let (mut discovered, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); debug!("elapsed: {}", elapsed.as_secs()); - assert_eq!(event.0.iter().next().unwrap().namespace, "foo"); // TODO: proper "RegistrationExpired" event + assert_eq!(event.0.iter().next().unwrap().namespace, "foo"); assert!(elapsed.as_secs() >= 2); assert!(elapsed.as_secs() < 3); - //assert_eq!(discovered.next(), None) + assert_eq!(discovered.next(), None) } #[test] From 1b36d747d5e5b71594a0fa47d3f103ab7245c461 Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 18 Jun 2021 09:52:01 +1000 Subject: [PATCH 148/242] Extend expiry test Create 2 registrations. One expires while the other remains alive. --- protocols/rendezvous/src/behaviour.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index b32126920e5..f66bbb25950 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -537,7 +537,6 @@ pub struct CookieNamespaceMismatch; mod tests { use super::*; use libp2p_core::identity; - use tokio::time::Instant; #[test] fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { @@ -626,29 +625,38 @@ mod tests { } #[tokio::test] - async fn registrations_expire() { + async fn given_two_registration_ttls_one_expires_one_lives() { env_logger::init(); let mut registrations = Registrations::new(7200); + registrations .add(new_dummy_registration_with_ttl("foo", 2)) .unwrap(); + registrations + .add(new_dummy_registration_with_ttl("bar", 4)) + .unwrap(); + let start_time = SystemTime::now(); + let event = futures::future::poll_fn(|cx| loop { if let Poll::Ready(reg) = registrations.poll(cx) { return Poll::Ready(reg); } }) .await; - let elapsed = start_time.elapsed().unwrap(); - let (mut discovered, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); + let elapsed = start_time.elapsed().unwrap(); - debug!("elapsed: {}", elapsed.as_secs()); assert_eq!(event.0.iter().next().unwrap().namespace, "foo"); assert!(elapsed.as_secs() >= 2); assert!(elapsed.as_secs() < 3); - assert_eq!(discovered.next(), None) + { + let (mut discovered_foo, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); + assert!(discovered_foo.next().is_none()); + } + let (mut discovered_bar, _) = registrations.get(Some("bar".to_owned()), None).unwrap(); + assert!(discovered_bar.next().is_some()); } #[test] From 6f0cc7ff514f4bad40a62e2362ae4f7edde3e76a Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 18 Jun 2021 11:52:36 +1000 Subject: [PATCH 149/242] Trigger cleanup of expired registrations using futures --- protocols/rendezvous/src/behaviour.rs | 96 +++++++++++++++------------ 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index f66bbb25950..fd02e5795be 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -4,6 +4,10 @@ use crate::codec::{Cookie, ErrorCode, NewRegistration, Registration}; use crate::handler; use crate::handler::{DeclineReason, InEvent, OutEvent, RendezvousHandler}; use bimap::BiMap; +use futures::future::BoxFuture; +use futures::ready; +use futures::stream::FuturesUnordered; +use futures::{FutureExt, StreamExt}; use libp2p_core::connection::ConnectionId; use libp2p_core::identity::error::SigningError; use libp2p_core::identity::Keypair; @@ -13,8 +17,10 @@ use libp2p_swarm::{ }; use log::debug; use std::collections::{HashMap, VecDeque}; +use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::{Duration, SystemTime}; +use tokio::time::sleep; use uuid::Uuid; pub struct Rendezvous { @@ -333,13 +339,15 @@ impl RegistrationId { } #[derive(Debug, PartialEq)] -pub struct RegistrationsExpired(Vec); +pub struct RegistrationExpired(Registration); pub struct Registrations { registrations_for_peer: BiMap<(PeerId, String), RegistrationId>, registrations: HashMap, + // todo: move cookie to registrations as a value cookies: HashMap>, ttl_upper_bound: i64, + next_expiry: FuturesUnordered>, } #[derive(Debug, thiserror::Error)] @@ -356,6 +364,7 @@ impl Registrations { registrations: Default::default(), ttl_upper_bound, cookies: Default::default(), + next_expiry: FuturesUnordered::from_iter(vec![futures::future::pending().boxed()]), } } @@ -386,6 +395,12 @@ impl Registrations { } } + let next_expiry = sleep(Duration::from_secs(ttl as u64)) + .map(move |()| registration_id) + .boxed(); + + self.next_expiry.push(next_expiry); + self.registrations.insert( registration_id, Registration { @@ -481,38 +496,16 @@ impl Registrations { difficulty_from_num_registrations(num_registrations) } - pub fn poll(&mut self, _: &mut Context<'_>) -> Poll { - // find ids of expired registrations - let expired = self - .registrations - .iter() - .filter(|(_id, r)| { - let absolute_expiry = r.timestamp + Duration::from_secs(r.ttl as u64); - debug!("absolute expiry: {:?}", absolute_expiry); - let now = SystemTime::now(); - debug!("system time: {:?}", now); - if now > absolute_expiry { - true - } else { - false - } - }) - .map(|(id, _r)| *id) - .collect::>(); - - //remove expired registrations - let expired_registrations = expired - .iter() - .filter_map(|id| { - self.registrations_for_peer.remove_by_right(id); - self.registrations.remove(id) - }) - .collect::>(); + pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll { + let registration_id = ready!(self.next_expiry.poll_next_unpin(cx)).expect( + "This stream should never finish because it is initialised with a pending future", + ); - if expired_registrations.is_empty() { - Poll::Pending - } else { - Poll::Ready(RegistrationsExpired(expired_registrations)) + self.registrations_for_peer + .remove_by_right(®istration_id); + match self.registrations.remove(®istration_id) { + None => unimplemented!(), + Some(registration) => Poll::Ready(RegistrationExpired(registration)), } } } @@ -630,7 +623,7 @@ mod tests { let mut registrations = Registrations::new(7200); registrations - .add(new_dummy_registration_with_ttl("foo", 2)) + .add(new_dummy_registration_with_ttl("foo", 1)) .unwrap(); registrations @@ -639,18 +632,14 @@ mod tests { let start_time = SystemTime::now(); - let event = futures::future::poll_fn(|cx| loop { - if let Poll::Ready(reg) = registrations.poll(cx) { - return Poll::Ready(reg); - } - }) - .await; + let event = futures::future::poll_fn(|cx| registrations.poll(cx)).await; let elapsed = start_time.elapsed().unwrap(); + assert!(elapsed.as_secs() >= 1); + assert!(elapsed.as_secs() < 2); + + assert_eq!(event.0.namespace, "foo"); - assert_eq!(event.0.iter().next().unwrap().namespace, "foo"); - assert!(elapsed.as_secs() >= 2); - assert!(elapsed.as_secs() < 3); { let (mut discovered_foo, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); assert!(discovered_foo.next().is_none()); @@ -659,6 +648,29 @@ mod tests { assert!(discovered_bar.next().is_some()); } + #[tokio::test] + async fn given_1_second_ttl() { + env_logger::init(); + let mut registrations = Registrations::new(7200); + + registrations + .add(new_dummy_registration_with_ttl("foo", 2)) + .unwrap(); + + let start_time = SystemTime::now(); + + let event = futures::future::poll_fn(|cx| registrations.poll(cx)).await; + + let elapsed = start_time.elapsed().unwrap(); + assert!(elapsed.as_secs() >= 2); + assert!(elapsed.as_secs() < 3); + + assert_eq!(event.0.namespace, "foo"); + + let (mut discovered_foo, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); + assert!(discovered_foo.next().is_none()); + } + #[test] fn first_registration_is_free() { let required = difficulty_from_num_registrations(0); From e2f779dfcf7710920fa9af8d8082de8d390d800e Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 18 Jun 2021 14:29:43 +1000 Subject: [PATCH 150/242] Test expiry event is not emitted if unregistered --- protocols/rendezvous/src/behaviour.rs | 33 +++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index fd02e5795be..cd289573ec3 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -504,7 +504,7 @@ impl Registrations { self.registrations_for_peer .remove_by_right(®istration_id); match self.registrations.remove(®istration_id) { - None => unimplemented!(), + None => self.poll(cx), Some(registration) => Poll::Ready(RegistrationExpired(registration)), } } @@ -619,7 +619,6 @@ mod tests { #[tokio::test] async fn given_two_registration_ttls_one_expires_one_lives() { - env_logger::init(); let mut registrations = Registrations::new(7200); registrations @@ -649,26 +648,21 @@ mod tests { } #[tokio::test] - async fn given_1_second_ttl() { - env_logger::init(); + async fn given_peer_unregisters_before_expiry_do_not_emit_registration_expired() { let mut registrations = Registrations::new(7200); - registrations - .add(new_dummy_registration_with_ttl("foo", 2)) - .unwrap(); + let dummy_registration = new_dummy_registration_with_ttl("foo", 2); - let start_time = SystemTime::now(); + let namespace = dummy_registration.namespace.clone(); + let peer_id = dummy_registration.record.peer_id(); - let event = futures::future::poll_fn(|cx| registrations.poll(cx)).await; + registrations.add(dummy_registration).unwrap(); - let elapsed = start_time.elapsed().unwrap(); - assert!(elapsed.as_secs() >= 2); - assert!(elapsed.as_secs() < 3); + assert_no_events_emitted_for_seconds(&mut registrations, 1).await; - assert_eq!(event.0.namespace, "foo"); + registrations.remove(namespace, peer_id); - let (mut discovered_foo, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); - assert!(discovered_foo.next().is_none()); + assert_no_events_emitted_for_seconds(&mut registrations, 3).await; } #[test] @@ -718,4 +712,13 @@ mod tests { ttl, ) } + + async fn assert_no_events_emitted_for_seconds(registrations: &mut Registrations, secs: u64) { + tokio::time::timeout( + Duration::from_secs(secs), + futures::future::poll_fn(|cx| registrations.poll(cx)), + ) + .await + .unwrap_err(); + } } From 73125923ca7493cf48aada928366afb20020b87f Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 18 Jun 2021 15:25:24 +1000 Subject: [PATCH 151/242] Ensure next_expiry.poll_next() cannot return None FuturesUnordered stop polling for ready futures when poll_next() is called until a None value is returned. To prevent the next_expiry future from going to "sleep", next_expiry is initialised with a future that always returns pending. This test ensures that FuturesUnordered does not stop polling for ready futures. --- protocols/rendezvous/src/behaviour.rs | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index cd289573ec3..db1ec08e203 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -665,6 +665,37 @@ mod tests { assert_no_events_emitted_for_seconds(&mut registrations, 3).await; } + /// FuturesUnordered stop polling for ready futures when poll_next() is called until a None + /// value is returned. To prevent the next_expiry future from going to "sleep", next_expiry + /// is initialised with a future that always returns pending. This test ensures that + /// FuturesUnordered does not stop polling for ready futures. + #[tokio::test] + async fn given_all_registrations_expired_then_succesfully_handle_new_registration_and_expiry() { + let mut registrations = Registrations::new(7200); + + let dummy_registration = new_dummy_registration_with_ttl("foo", 1); + + registrations.add(dummy_registration.clone()).unwrap(); + + tokio::time::timeout( + Duration::from_secs(2), + futures::future::poll_fn(|cx| registrations.poll(cx)), + ) + .await + .unwrap(); + + assert_no_events_emitted_for_seconds(&mut registrations, 1).await; + + registrations.add(dummy_registration).unwrap(); + + tokio::time::timeout( + Duration::from_secs(2), + futures::future::poll_fn(|cx| registrations.poll(cx)), + ) + .await + .unwrap(); + } + #[test] fn first_registration_is_free() { let required = difficulty_from_num_registrations(0); From e560e24f913cabea76870f8a2a52100ff06191fb Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 18 Jun 2021 17:40:46 +1000 Subject: [PATCH 152/242] Use tokio::test to fix broken tests 6f0cc7ff514f4bad40a62e2362ae4f7edde3e76a introduced tokio/async. tokio::test is now required for tests that use Registrations --- protocols/rendezvous/src/behaviour.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index db1ec08e203..f6ec0af62a5 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -531,8 +531,8 @@ mod tests { use super::*; use libp2p_core::identity; - #[test] - fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { + #[tokio::test] + async fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { let mut registrations = Registrations::new(7200); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -544,8 +544,8 @@ mod tests { assert_eq!(subsequent_discover.collect::>().len(), 0); } - #[test] - fn given_registrations_when_discover_all_then_all_are_returned() { + #[tokio::test] + async fn given_registrations_when_discover_all_then_all_are_returned() { let mut registrations = Registrations::new(7200); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -555,9 +555,9 @@ mod tests { assert_eq!(discover.collect::>().len(), 2); } - #[test] - fn given_registrations_when_discover_only_for_specific_namespace_then_only_those_are_returned() - { + #[tokio::test] + async fn given_registrations_when_discover_only_for_specific_namespace_then_only_those_are_returned( + ) { let mut registrations = Registrations::new(7200); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); @@ -570,8 +570,8 @@ mod tests { ); } - #[test] - fn given_reregistration_old_registration_is_discarded() { + #[tokio::test] + async fn given_reregistration_old_registration_is_discarded() { let alice = identity::Keypair::generate_ed25519(); let mut registrations = Registrations::new(7200); registrations @@ -589,8 +589,8 @@ mod tests { ); } - #[test] - fn given_cookie_from_2nd_discover_does_not_return_nodes_from_first_discover() { + #[tokio::test] + async fn given_cookie_from_2nd_discover_does_not_return_nodes_from_first_discover() { let mut registrations = Registrations::new(7200); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -605,8 +605,8 @@ mod tests { assert_eq!(subsequent_discover.collect::>().len(), 0); } - #[test] - fn cookie_from_different_discover_request_is_not_valid() { + #[tokio::test] + async fn cookie_from_different_discover_request_is_not_valid() { let mut registrations = Registrations::new(7200); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); From 69dfbee3118eaff3550896ab840072772166ca2c Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 18 Jun 2021 17:48:21 +1000 Subject: [PATCH 153/242] Fix bug where new registration was not being added The registration for a peer should be inserted regardless of whether an entry already exists. --- protocols/rendezvous/src/behaviour.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index f6ec0af62a5..8fd84f5d579 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -380,26 +380,17 @@ impl Registrations { let namespace = new_registration.namespace; let registration_id = RegistrationId::new(); - match self + if let Some(old_registration) = self .registrations_for_peer .get_by_left(&(new_registration.record.peer_id(), namespace.clone())) { - Some(old_registration) => { - self.registrations.remove(old_registration); - } - None => { - self.registrations_for_peer.insert( - (new_registration.record.peer_id(), namespace.clone()), - registration_id, - ); - } + self.registrations.remove(old_registration); } - let next_expiry = sleep(Duration::from_secs(ttl as u64)) - .map(move |()| registration_id) - .boxed(); - - self.next_expiry.push(next_expiry); + self.registrations_for_peer.insert( + (new_registration.record.peer_id(), namespace.clone()), + registration_id, + ); self.registrations.insert( registration_id, @@ -411,6 +402,12 @@ impl Registrations { }, ); + let next_expiry = sleep(Duration::from_secs(ttl as u64)) + .map(move |()| registration_id) + .boxed(); + + self.next_expiry.push(next_expiry); + Ok(ttl) } From bdc102e29ad1d79bb34307af414e9075cdc3860c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 18 Jun 2021 19:22:12 +1000 Subject: [PATCH 154/242] Make tests prettier by adding functions on `Registrations` --- protocols/rendezvous/src/behaviour.rs | 62 ++++++++++++--------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 8fd84f5d579..02fd4c8c07c 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -4,7 +4,7 @@ use crate::codec::{Cookie, ErrorCode, NewRegistration, Registration}; use crate::handler; use crate::handler::{DeclineReason, InEvent, OutEvent, RendezvousHandler}; use bimap::BiMap; -use futures::future::BoxFuture; +use futures::future::{poll_fn, BoxFuture}; use futures::ready; use futures::stream::FuturesUnordered; use futures::{FutureExt, StreamExt}; @@ -618,17 +618,16 @@ mod tests { async fn given_two_registration_ttls_one_expires_one_lives() { let mut registrations = Registrations::new(7200); + let start_time = SystemTime::now(); + registrations .add(new_dummy_registration_with_ttl("foo", 1)) .unwrap(); - registrations .add(new_dummy_registration_with_ttl("bar", 4)) .unwrap(); - let start_time = SystemTime::now(); - - let event = futures::future::poll_fn(|cx| registrations.poll(cx)).await; + let event = registrations.next_event().await; let elapsed = start_time.elapsed().unwrap(); assert!(elapsed.as_secs() >= 1); @@ -647,19 +646,15 @@ mod tests { #[tokio::test] async fn given_peer_unregisters_before_expiry_do_not_emit_registration_expired() { let mut registrations = Registrations::new(7200); - let dummy_registration = new_dummy_registration_with_ttl("foo", 2); - let namespace = dummy_registration.namespace.clone(); let peer_id = dummy_registration.record.peer_id(); registrations.add(dummy_registration).unwrap(); - - assert_no_events_emitted_for_seconds(&mut registrations, 1).await; - + registrations.no_event_for(1).await; registrations.remove(namespace, peer_id); - assert_no_events_emitted_for_seconds(&mut registrations, 3).await; + registrations.no_event_for(3).await } /// FuturesUnordered stop polling for ready futures when poll_next() is called until a None @@ -669,28 +664,15 @@ mod tests { #[tokio::test] async fn given_all_registrations_expired_then_succesfully_handle_new_registration_and_expiry() { let mut registrations = Registrations::new(7200); - let dummy_registration = new_dummy_registration_with_ttl("foo", 1); registrations.add(dummy_registration.clone()).unwrap(); + let _ = registrations.next_event_in_at_most(2).await; - tokio::time::timeout( - Duration::from_secs(2), - futures::future::poll_fn(|cx| registrations.poll(cx)), - ) - .await - .unwrap(); - - assert_no_events_emitted_for_seconds(&mut registrations, 1).await; + registrations.no_event_for(1).await; registrations.add(dummy_registration).unwrap(); - - tokio::time::timeout( - Duration::from_secs(2), - futures::future::poll_fn(|cx| registrations.poll(cx)), - ) - .await - .unwrap(); + let _ = registrations.next_event_in_at_most(2).await; } #[test] @@ -741,12 +723,24 @@ mod tests { ) } - async fn assert_no_events_emitted_for_seconds(registrations: &mut Registrations, secs: u64) { - tokio::time::timeout( - Duration::from_secs(secs), - futures::future::poll_fn(|cx| registrations.poll(cx)), - ) - .await - .unwrap_err(); + /// Defines utility functions that make the tests more readable. + impl Registrations { + async fn next_event(&mut self) -> RegistrationExpired { + poll_fn(|cx| self.poll(cx)).await + } + + /// Polls [`Registrations`] for `seconds` and panics if it returns a event during this time. + async fn no_event_for(&mut self, seconds: u64) { + tokio::time::timeout(Duration::from_secs(seconds), self.next_event()) + .await + .unwrap_err(); + } + + /// Polls [`Registrations`] for at most `seconds` and panics if doesn't return an event within that time. + async fn next_event_in_at_most(&mut self, seconds: u64) -> RegistrationExpired { + tokio::time::timeout(Duration::from_secs(seconds), self.next_event()) + .await + .unwrap() + } } } From b8a93c333991ffbe438ada03e62290c9262340eb Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 18 Jun 2021 19:30:07 +1000 Subject: [PATCH 155/242] Switch registrations in cookies to a HashSet They should be unique and a hashset is going to help us in tracking which elements we can remove. --- protocols/rendezvous/src/behaviour.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 02fd4c8c07c..0e37a8f7447 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -16,7 +16,7 @@ use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; use log::debug; -use std::collections::{HashMap, VecDeque}; +use std::collections::{HashMap, VecDeque, HashSet}; use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::{Duration, SystemTime}; @@ -345,7 +345,7 @@ pub struct Registrations { registrations_for_peer: BiMap<(PeerId, String), RegistrationId>, registrations: HashMap, // todo: move cookie to registrations as a value - cookies: HashMap>, + cookies: HashMap>, ttl_upper_bound: i64, next_expiry: FuturesUnordered>, } @@ -465,7 +465,7 @@ impl Registrations { .cloned() .collect::>(); - reggos_of_last_discover.extend_from_slice(&ids); + reggos_of_last_discover.extend(&ids); let new_cookie = discover_namespace .map(Cookie::for_namespace) From c4f802fccb7803daf9bdede0d76263c9282097a5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 09:27:19 +1000 Subject: [PATCH 156/242] Remove TODOs in favor of tasks on draft PR --- protocols/rendezvous/Cargo.toml | 2 +- protocols/rendezvous/src/behaviour.rs | 38 ++++++++++++++++++++------- protocols/rendezvous/src/codec.rs | 12 ++++----- protocols/rendezvous/src/handler.rs | 7 +++-- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 599bf7c368a..8b7524cd12c 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -17,7 +17,7 @@ prost = "0.7" void = "1" log = "0.4" futures = { version = "0.3", default-features = false, features = ["std"] } -thiserror = "1" # TODO: REMOVE FOR PRODUCTION USE +thiserror = "1" unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } uuid = { version = "0.8", features = ["v4"] } bimap = "0.6.1" diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 0e37a8f7447..45f300d3da3 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -16,7 +16,7 @@ use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; use log::debug; -use std::collections::{HashMap, VecDeque, HashSet}; +use std::collections::{HashMap, HashSet, VecDeque}; use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::{Duration, SystemTime}; @@ -48,7 +48,6 @@ impl Rendezvous { } } - // TODO: Make it possible to filter for specific external-addresses (like onion addresses-only f.e.) pub fn register( &mut self, namespace: String, @@ -144,7 +143,6 @@ pub enum Event { registrations: Vec, }, /// A peer successfully registered with us. - // TODO: Include registration here PeerRegistered { peer: PeerId, namespace: String }, /// We declined a registration from a peer. PeerNotRegistered { @@ -258,7 +256,6 @@ impl NetworkBehaviour for Rendezvous { ), OutEvent::UnregisterRequested { namespace } => { self.registrations.remove(namespace, peer_id); - // TODO: Should send unregister response? } OutEvent::DiscoverRequested { namespace, cookie } => { let (registrations, cookie) = self @@ -344,7 +341,6 @@ pub struct RegistrationExpired(Registration); pub struct Registrations { registrations_for_peer: BiMap<(PeerId, String), RegistrationId>, registrations: HashMap, - // todo: move cookie to registrations as a value cookies: HashMap>, ttl_upper_bound: i64, next_expiry: FuturesUnordered>, @@ -494,13 +490,21 @@ impl Registrations { } pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll { - let registration_id = ready!(self.next_expiry.poll_next_unpin(cx)).expect( + 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(®istration_id); - match self.registrations.remove(®istration_id) { + .remove_by_right(&expired_registration); + match self.registrations.remove(&expired_registration) { None => self.poll(cx), Some(registration) => Poll::Ready(RegistrationExpired(registration)), } @@ -518,7 +522,6 @@ fn difficulty_from_num_registrations(existing_registrations: usize) -> Difficult .unwrap_or(Difficulty::MAX) } -// TODO: Be more specific in what the bad combination was? #[derive(Debug, thiserror::Error, Eq, PartialEq)] #[error("The provided cookie is not valid for a DISCOVER request for the given namespace")] pub struct CookieNamespaceMismatch; @@ -675,6 +678,23 @@ mod tests { let _ = registrations.next_event_in_at_most(2).await; } + #[tokio::test] + async fn cookies_are_cleaned_up_if_registrations_expire() { + let mut registrations = Registrations::new(7200); + + registrations + .add(new_dummy_registration_with_ttl("foo", 2)) + .unwrap(); + let (registrations, cookie) = registrations.get(None, None).unwrap(); + + assert_eq!(registrations.cookies.len(), 1); + + registrations.no_event_for(1).await; + registrations.remove(namespace, peer_id); + + registrations.no_event_for(3).await + } + #[test] fn first_registration_is_free() { let required = difficulty_from_num_registrations(0); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 645e5311bde..8543d3eb4a1 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -15,11 +15,9 @@ pub enum Message { RegisterResponse(Result), Unregister { namespace: String, - // TODO: what is the `id` field here in the PB message }, Discover { namespace: Option, - // TODO limit: Option cookie: Option, }, DiscoverResponse(Result<(Vec, Cookie), ErrorCode>), @@ -31,7 +29,7 @@ pub enum Message { #[derive(Debug, Clone)] pub enum RegisterErrorResponse { - Failed(ErrorCode), // TODO: `Failed` is a bad name, find a better one. + Failed(ErrorCode), /// We are declining the registration because PoW is required. PowRequired { challenge: Challenge, @@ -126,9 +124,9 @@ pub struct InvalidCookie; #[derive(Debug, Clone)] pub struct NewRegistration { - pub namespace: String, // TODO: Create new-type for namespace to enforce max-length requirement. + pub namespace: String, pub record: PeerRecord, - pub ttl: Option, // TODO: Create new-type for ttl to be more descriptive. + pub ttl: Option, } /// If unspecified, rendezvous nodes should assume a TTL of 2h. @@ -177,7 +175,7 @@ pub struct RendezvousCodec { impl Default for RendezvousCodec { fn default() -> Self { let mut length_codec = UviBytes::default(); - length_codec.set_max_len(1024 * 1024); // 1MB TODO clarify with spec what the default should be + length_codec.set_max_len(1024 * 1024); // 1MB Self { length_codec } } @@ -229,7 +227,7 @@ pub enum Error { Encode(#[from] prost::EncodeError), #[error("Failed to decode message from bytes")] Decode(#[from] prost::DecodeError), - #[error("Failed to read/write")] // TODO: Better message + #[error("Failed to read/write")] Io(#[from] std::io::Error), #[error("Failed to convert wire message to internal data model")] ConversionError(#[from] ConversionError), diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index d0b82e365c5..9a71fd48cb5 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -511,7 +511,7 @@ impl ProtocolsHandler for RendezvousHandler { self.inbound = SubstreamState::Active(Inbound::PendingRead(substream)); self.inbound_history.clear(); } else { - unreachable!("Invalid inbound state") // TODO: this unreachable is not correct I believe + unreachable!("Invalid inbound state") } } @@ -527,7 +527,7 @@ impl ProtocolsHandler for RendezvousHandler { }); self.outbound_history.clear(); } else { - unreachable!("Invalid outbound state") // TODO: this unreachable is not correct I believe + unreachable!("Invalid outbound state") } } @@ -599,7 +599,7 @@ impl ProtocolsHandler for RendezvousHandler { )), outbound, ), - _ => unreachable!("Handler in invalid state"), // TODO: this unreachable is not correct I believe + _ => unreachable!("Handler in invalid state"), }; self.inbound = inbound; @@ -614,7 +614,6 @@ impl ProtocolsHandler for RendezvousHandler { } fn connection_keep_alive(&self) -> KeepAlive { - //todo: fix this KeepAlive::Yes } From d31ebffa0822cd7b5452d6eeb6ecf99d72a94f4e Mon Sep 17 00:00:00 2001 From: rishflab Date: Sun, 20 Jun 2021 11:03:34 +1000 Subject: [PATCH 157/242] Include Registration in PeerRegistered OutEvent --- protocols/rendezvous/src/behaviour.rs | 28 +++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 45f300d3da3..a1b6a137ea9 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -143,7 +143,11 @@ pub enum Event { registrations: Vec, }, /// A peer successfully registered with us. - PeerRegistered { peer: PeerId, namespace: String }, + PeerRegistered { + peer: PeerId, + namespace: String, + registration: Registration, + }, /// We declined a registration from a peer. PeerNotRegistered { peer: PeerId, @@ -202,9 +206,10 @@ impl NetworkBehaviour for Rendezvous { } let namespace = registration.namespace.clone(); + let record = registration.record.clone(); let events = match self.registrations.add(registration) { - Ok(effective_ttl) => { + Ok((effective_ttl, timestamp)) => { vec![ NetworkBehaviourAction::NotifyHandler { peer_id, @@ -213,7 +218,13 @@ impl NetworkBehaviour for Rendezvous { }, NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { peer: peer_id, - namespace, + namespace: namespace.clone(), + registration: Registration { + namespace, + record, + ttl: effective_ttl, + timestamp, + }, }), ] } @@ -364,7 +375,10 @@ impl Registrations { } } - pub fn add(&mut self, new_registration: NewRegistration) -> Result { + pub fn add( + &mut self, + new_registration: NewRegistration, + ) -> Result<(i64, SystemTime), TtlTooLong> { let ttl = new_registration.effective_ttl(); if ttl > self.ttl_upper_bound { return Err(TtlTooLong { @@ -388,13 +402,15 @@ impl Registrations { registration_id, ); + let timestamp = SystemTime::now(); + self.registrations.insert( registration_id, Registration { namespace: namespace.clone(), record: new_registration.record, ttl, - timestamp: SystemTime::now(), + timestamp, }, ); @@ -404,7 +420,7 @@ impl Registrations { self.next_expiry.push(next_expiry); - Ok(ttl) + Ok((ttl, timestamp)) } pub fn remove(&mut self, namespace: String, peer_id: PeerId) { From 1fe1e3d540dd345ef79f17eb52e0a71ed5cf5c12 Mon Sep 17 00:00:00 2001 From: rishflab Date: Mon, 21 Jun 2021 10:00:02 +1000 Subject: [PATCH 158/242] Remove redundant field from PeerRegistered Move tokio dependency to allow code to build. --- protocols/rendezvous/Cargo.toml | 2 +- protocols/rendezvous/src/behaviour.rs | 2 -- protocols/rendezvous/tests/rendezvous.rs | 22 +++++++++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 8b7524cd12c..6824388d2ff 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -23,6 +23,7 @@ uuid = { version = "0.8", features = ["v4"] } bimap = "0.6.1" sha2 = "0.9" rand = "0.8" +tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } # TODO: REMOVE FOR PRODUCTION USE [dev-dependencies] libp2p-tcp = { path = "../../transports/tcp" } @@ -32,7 +33,6 @@ libp2p-mplex = { path = "../../muxers/mplex" } rand = "0.8" async-std = "1.6.2" env_logger = "0.8" -tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } async-trait = "0.1" [build-dependencies] diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index a1b6a137ea9..361da52dd92 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -145,7 +145,6 @@ pub enum Event { /// A peer successfully registered with us. PeerRegistered { peer: PeerId, - namespace: String, registration: Registration, }, /// We declined a registration from a peer. @@ -218,7 +217,6 @@ impl NetworkBehaviour for Rendezvous { }, NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { peer: peer_id, - namespace: namespace.clone(), registration: Registration { namespace, record, diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index a3c724f3ba3..f0d4be2c527 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -128,17 +128,29 @@ const DEFAULT_TTL_UPPER_BOUND: i64 = 56_000; impl RendezvousTest { pub async fn setup() -> Self { let mut registration_swarm = new_swarm(|_, identity| { - Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND, Difficulty::from_u32(2).expect("2 < 32")) + Rendezvous::new( + identity, + DEFAULT_TTL_UPPER_BOUND, + Difficulty::from_u32(2).expect("2 < 32"), + ) }); registration_swarm.listen_on_random_memory_address().await; let mut discovery_swarm = new_swarm(|_, identity| { - Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND, Difficulty::from_u32(2).expect("2 < 32")) + Rendezvous::new( + identity, + DEFAULT_TTL_UPPER_BOUND, + Difficulty::from_u32(2).expect("2 < 32"), + ) }); discovery_swarm.listen_on_random_memory_address().await; let mut rendezvous_swarm = new_swarm(|_, identity| { - Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND, Difficulty::from_u32(2).expect("2 < 32")) + Rendezvous::new( + identity, + DEFAULT_TTL_UPPER_BOUND, + Difficulty::from_u32(2).expect("2 < 32"), + ) }); rendezvous_swarm.listen_on_random_memory_address().await; @@ -163,12 +175,12 @@ impl RendezvousTest { ) { match await_events_or_timeout(self.rendezvous_swarm.next_event(), self.registration_swarm.next_event()).await { ( - SwarmEvent::Behaviour(Event::PeerRegistered { peer, namespace: rendezvous_node_namespace }), + SwarmEvent::Behaviour(Event::PeerRegistered { peer, registration }), SwarmEvent::Behaviour(Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), ) => { assert_eq!(&peer, self.registration_swarm.local_peer_id()); assert_eq!(&rendezvous_node, self.rendezvous_swarm.local_peer_id()); - assert_eq!(rendezvous_node_namespace, expected_namespace); + assert_eq!(registration.namespace, expected_namespace); assert_eq!(register_node_namespace, expected_namespace); assert_eq!(ttl, expected_ttl); } From 476ddc7ae1f0ea1ab10e1b9701c8c22e693b480b Mon Sep 17 00:00:00 2001 From: rishflab Date: Mon, 21 Jun 2021 10:11:46 +1000 Subject: [PATCH 159/242] Remove unused dependency --- protocols/rendezvous/src/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 361da52dd92..286fbe8ab00 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -4,7 +4,7 @@ use crate::codec::{Cookie, ErrorCode, NewRegistration, Registration}; use crate::handler; use crate::handler::{DeclineReason, InEvent, OutEvent, RendezvousHandler}; use bimap::BiMap; -use futures::future::{poll_fn, BoxFuture}; +use futures::future::BoxFuture; use futures::ready; use futures::stream::FuturesUnordered; use futures::{FutureExt, StreamExt}; From 93846e11da9d0b21caecbbee3b980dba7b584ec9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 13:24:54 +1000 Subject: [PATCH 160/242] Apply clippy suggestions --- protocols/rendezvous/src/behaviour.rs | 4 ++-- protocols/rendezvous/src/codec.rs | 2 +- protocols/rendezvous/src/pow.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 286fbe8ab00..851126258dd 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -405,7 +405,7 @@ impl Registrations { self.registrations.insert( registration_id, Registration { - namespace: namespace.clone(), + namespace, record: new_registration.record, ttl, timestamp, @@ -479,7 +479,7 @@ impl Registrations { let new_cookie = discover_namespace .map(Cookie::for_namespace) - .unwrap_or_else(|| Cookie::for_all_namespaces()); + .unwrap_or_else(Cookie::for_all_namespaces); self.cookies .insert(new_cookie.clone(), reggos_of_last_discover); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 8543d3eb4a1..45f467337d1 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -98,7 +98,7 @@ impl Cookie { } let namespace = bytes.split_off(16); - let namespace = if namespace.len() == 0 { + let namespace = if namespace.is_empty() { None } else { Some(String::from_utf8(namespace).map_err(|_| InvalidCookie)?) diff --git a/protocols/rendezvous/src/pow.rs b/protocols/rendezvous/src/pow.rs index 54bb8a62f7f..a0004fd4d26 100644 --- a/protocols/rendezvous/src/pow.rs +++ b/protocols/rendezvous/src/pow.rs @@ -2,7 +2,7 @@ use libp2p_core::SignedEnvelope; use sha2::{Digest, Sha256}; use std::fmt; -const DOMAIN_TAG: &'static str = "libp2p-rendezvous-pow"; +const DOMAIN_TAG: &str = "libp2p-rendezvous-pow"; /// Run the Proof of Work algorithm for a registration at a rendezvous node. /// @@ -80,7 +80,7 @@ impl Difficulty { Some(Difficulty(value)) } - pub fn to_u32(&self) -> u32 { + pub fn to_u32(self) -> u32 { self.0 } } @@ -123,7 +123,7 @@ pub fn difficulty_of(hash: &[u8; 32]) -> Difficulty { } } - return Difficulty(32); + Difficulty(32) } fn make_hash(challenge: &[u8], namespace: &str, envelope: &[u8], nonce: i64) -> [u8; 32] { From 61f4dbba106bb4679f27b57073c1dd63d051b5d8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 13:25:11 +1000 Subject: [PATCH 161/242] Only depend on tokio features for prod where needed --- protocols/rendezvous/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 6824388d2ff..89ea741cdb6 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -23,7 +23,7 @@ uuid = { version = "0.8", features = ["v4"] } bimap = "0.6.1" sha2 = "0.9" rand = "0.8" -tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } # TODO: REMOVE FOR PRODUCTION USE +tokio = { version = "1", features = [ "time" ] } [dev-dependencies] libp2p-tcp = { path = "../../transports/tcp" } @@ -34,6 +34,7 @@ rand = "0.8" async-std = "1.6.2" env_logger = "0.8" async-trait = "0.1" +tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } # TODO: REMOVE FOR PRODUCTION USE [build-dependencies] prost-build = "0.7" From e39c3049adedebded064c3c843b911608c963544 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 14:52:54 +1000 Subject: [PATCH 162/242] Make tests actually compile --- protocols/rendezvous/src/behaviour.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 851126258dd..0a5c31dd795 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -699,14 +699,13 @@ mod tests { registrations .add(new_dummy_registration_with_ttl("foo", 2)) .unwrap(); - let (registrations, cookie) = registrations.get(None, None).unwrap(); + let (_, cookie) = registrations.get(None, None).unwrap(); assert_eq!(registrations.cookies.len(), 1); - registrations.no_event_for(1).await; - registrations.remove(namespace, peer_id); + let _ = registrations.next_event_in_at_most(3).await; - registrations.no_event_for(3).await + assert_eq!(registrations.cookies.len(), 0); } #[test] @@ -760,7 +759,7 @@ mod tests { /// Defines utility functions that make the tests more readable. impl Registrations { async fn next_event(&mut self) -> RegistrationExpired { - poll_fn(|cx| self.poll(cx)).await + futures::future::poll_fn(|cx| self.poll(cx)).await } /// Polls [`Registrations`] for `seconds` and panics if it returns a event during this time. From 85a07518d2dc0d97a2620c022afc91c56641d2a3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 14:57:55 +1000 Subject: [PATCH 163/242] Apply clippy suggestions to tests --- protocols/rendezvous/src/behaviour.rs | 14 +++++++------- protocols/rendezvous/tests/rendezvous.rs | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 0a5c31dd795..9d9c93b6939 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -552,10 +552,10 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); let (initial_discover, cookie) = registrations.get(None, None).unwrap(); - assert_eq!(initial_discover.collect::>().len(), 2); + assert_eq!(initial_discover.count(), 2); let (subsequent_discover, _) = registrations.get(None, Some(cookie)).unwrap(); - assert_eq!(subsequent_discover.collect::>().len(), 0); + assert_eq!(subsequent_discover.count(), 0); } #[tokio::test] @@ -566,7 +566,7 @@ mod tests { let (discover, _) = registrations.get(None, None).unwrap(); - assert_eq!(discover.collect::>().len(), 2); + assert_eq!(discover.count(), 2); } #[tokio::test] @@ -610,13 +610,13 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); let (initial_discover, cookie1) = registrations.get(None, None).unwrap(); - assert_eq!(initial_discover.collect::>().len(), 2); + assert_eq!(initial_discover.count(), 2); let (subsequent_discover, cookie2) = registrations.get(None, Some(cookie1)).unwrap(); - assert_eq!(subsequent_discover.collect::>().len(), 0); + assert_eq!(subsequent_discover.count(), 0); let (subsequent_discover, _) = registrations.get(None, Some(cookie2)).unwrap(); - assert_eq!(subsequent_discover.collect::>().len(), 0); + assert_eq!(subsequent_discover.count(), 0); } #[tokio::test] @@ -699,7 +699,7 @@ mod tests { registrations .add(new_dummy_registration_with_ttl("foo", 2)) .unwrap(); - let (_, cookie) = registrations.get(None, None).unwrap(); + let (_, _) = registrations.get(None, None).unwrap(); assert_eq!(registrations.cookies.len(), 1); diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index f0d4be2c527..044c2a3e050 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -15,7 +15,7 @@ async fn given_successful_registration_then_successful_discovery() { let _ = test.registration_swarm.behaviour_mut().register( namespace.clone(), - test.rendezvous_swarm.local_peer_id().clone(), + *test.rendezvous_swarm.local_peer_id(), None, ); @@ -25,13 +25,13 @@ async fn given_successful_registration_then_successful_discovery() { test.discovery_swarm.behaviour_mut().discover( Some(namespace.clone()), None, - test.rendezvous_swarm.local_peer_id().clone(), + *test.rendezvous_swarm.local_peer_id(), ); test.assert_successful_discovery( namespace.clone(), DEFAULT_TTL, - test.registration_swarm.local_peer_id().clone(), + *test.registration_swarm.local_peer_id(), ) .await; } @@ -47,7 +47,7 @@ async fn given_successful_registration_then_refresh_ttl() { let _ = test.registration_swarm.behaviour_mut().register( namespace.clone(), - test.rendezvous_swarm.local_peer_id().clone(), + *test.rendezvous_swarm.local_peer_id(), None, ); @@ -57,19 +57,19 @@ async fn given_successful_registration_then_refresh_ttl() { test.discovery_swarm.behaviour_mut().discover( Some(namespace.clone()), None, - test.rendezvous_swarm.local_peer_id().clone(), + *test.rendezvous_swarm.local_peer_id(), ); test.assert_successful_discovery( namespace.clone(), DEFAULT_TTL, - test.registration_swarm.local_peer_id().clone(), + *test.registration_swarm.local_peer_id(), ) .await; let _ = test.registration_swarm.behaviour_mut().register( namespace.clone(), - test.rendezvous_swarm.local_peer_id().clone(), + *test.rendezvous_swarm.local_peer_id(), Some(refesh_ttl), ); @@ -79,13 +79,13 @@ async fn given_successful_registration_then_refresh_ttl() { test.discovery_swarm.behaviour_mut().discover( Some(namespace.clone()), None, - test.rendezvous_swarm.local_peer_id().clone(), + *test.rendezvous_swarm.local_peer_id(), ); test.assert_successful_discovery( namespace.clone(), refesh_ttl, - test.registration_swarm.local_peer_id().clone(), + *test.registration_swarm.local_peer_id(), ) .await; } @@ -99,7 +99,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { let _ = test.registration_swarm.behaviour_mut().register( namespace.clone(), - test.rendezvous_swarm.local_peer_id().clone(), + *test.rendezvous_swarm.local_peer_id(), Some(100_000), ); From 2bf06890a3f114d5ae87f5541584c888d4c78ab0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 14:59:44 +1000 Subject: [PATCH 164/242] Don't panic on new inbound/outbound substream --- protocols/rendezvous/src/handler.rs | 34 ++++++++++++++++++----------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 9a71fd48cb5..978e8fa88e7 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -507,11 +507,14 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, _msg: Self::InboundOpenInfo, ) { - if let SubstreamState::None = self.inbound { - self.inbound = SubstreamState::Active(Inbound::PendingRead(substream)); - self.inbound_history.clear(); - } else { - unreachable!("Invalid inbound state") + match self.inbound { + SubstreamState::None => { + self.inbound = SubstreamState::Active(Inbound::PendingRead(substream)); + self.inbound_history.clear(); + } + _ => { + log::warn!("Ignoring new inbound substream because existing one is still active") + } } } @@ -520,14 +523,19 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, msg: Self::OutboundOpenInfo, ) { - if let SubstreamState::Active(Outbound::PendingSubstream) = self.outbound { - self.outbound = SubstreamState::Active(Outbound::PendingSend { - substream, - to_send: msg, - }); - self.outbound_history.clear(); - } else { - unreachable!("Invalid outbound state") + + + match self.outbound { + SubstreamState::Active(Outbound::PendingSubstream) => { + self.outbound = SubstreamState::Active(Outbound::PendingSend { + substream, + to_send: msg, + }); + self.outbound_history.clear(); + } + _ => { + log::warn!("Ignoring new outbound substream because existing one is still active") + } } } From b6a02b5f75783f3493541eb908face89c9b2ffcb Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 15:25:49 +1000 Subject: [PATCH 165/242] Align naming of substream states --- protocols/rendezvous/src/handler.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 978e8fa88e7..028fe3c917e 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -129,9 +129,9 @@ enum Inbound { /// The state of an outbound substream (i.e. we opened it). enum Outbound { /// We got a message to send from the behaviour. - Start(Message), - /// We've requested a substream and are waiting for it to be set up. - PendingSubstream, + PendingOpen(Message), + /// We've requested a substream and are waiting for it to be negotiated. + PendingNegotiate, /// We got the substream, now we need to send the message. PendingSend { substream: Framed, @@ -326,12 +326,12 @@ impl<'handler> Advance<'handler> for Outbound { }: &mut OutboundPollParams, ) -> Result, Self::Error> { Ok(match self { - Outbound::Start(msg) => Next::OpenSubstream { + Outbound::PendingOpen(msg) => Next::OpenSubstream { protocol: SubstreamProtocol::new(protocol::new(), msg), - next_state: Outbound::PendingSubstream, + next_state: Outbound::PendingNegotiate, }, - Outbound::PendingSubstream => Next::Pending { - next_state: Outbound::PendingSubstream, + Outbound::PendingNegotiate => Next::Pending { + next_state: Outbound::PendingNegotiate, }, Outbound::PendingSend { mut substream, @@ -526,7 +526,7 @@ impl ProtocolsHandler for RendezvousHandler { match self.outbound { - SubstreamState::Active(Outbound::PendingSubstream) => { + SubstreamState::Active(Outbound::PendingNegotiate) => { self.outbound = SubstreamState::Active(Outbound::PendingSend { substream, to_send: msg, @@ -548,15 +548,15 @@ impl ProtocolsHandler for RendezvousHandler { ) { (InEvent::RegisterRequest { request: reggo }, inbound, SubstreamState::None) => ( inbound, - SubstreamState::Active(Outbound::Start(Message::Register(reggo))), + SubstreamState::Active(Outbound::PendingOpen(Message::Register(reggo))), ), (InEvent::UnregisterRequest { namespace }, inbound, SubstreamState::None) => ( inbound, - SubstreamState::Active(Outbound::Start(Message::Unregister { namespace })), + SubstreamState::Active(Outbound::PendingOpen(Message::Unregister { namespace })), ), (InEvent::DiscoverRequest { namespace, cookie }, inbound, SubstreamState::None) => ( inbound, - SubstreamState::Active(Outbound::Start(Message::Discover { namespace, cookie })), + SubstreamState::Active(Outbound::PendingOpen(Message::Discover { namespace, cookie })), ), ( InEvent::RegisterResponse { ttl }, From bbeb6f399dcdf86e42ffc79e8842346b570028cd Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 15:38:08 +1000 Subject: [PATCH 166/242] Don't panic on unexpected event from behaviour --- protocols/rendezvous/src/handler.rs | 47 +++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 028fe3c917e..18151eb01f1 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -12,10 +12,9 @@ use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; -use std::fmt::Debug; -use std::mem; use std::sync::mpsc::TryRecvError; use std::task::{Context, Poll}; +use std::{fmt, mem}; use void::Void; pub struct RendezvousHandler { @@ -126,6 +125,18 @@ enum Inbound { PendingClose(Framed), } +impl fmt::Debug for Inbound { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Inbound::PendingRead(_) => write!(f, "Inbound::PendingRead"), + Inbound::PendingBehaviour(_) => write!(f, "Inbound::PendingBehaviour"), + Inbound::PendingSend(_, _) => write!(f, "Inbound::PendingSend"), + Inbound::PendingFlushThenRead(_) => write!(f, "Inbound::PendingFlushThenRead"), + Inbound::PendingClose(_) => write!(f, "Inbound::PendingClose"), + } + } +} + /// The state of an outbound substream (i.e. we opened it). enum Outbound { /// We got a message to send from the behaviour. @@ -150,6 +161,20 @@ enum Outbound { PendingClose(Framed), } +impl fmt::Debug for Outbound { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Outbound::PendingOpen(_) => write!(f, "Outbound::PendingOpen"), + Outbound::PendingNegotiate => write!(f, "Outbound::PendingNegotiate"), + Outbound::PendingSend { .. } => write!(f, "Outbound::PendingSend"), + Outbound::PendingFlush(_) => write!(f, "Outbound::PendingFlush"), + Outbound::PendingPoW { .. } => write!(f, "Outbound::PendingPoW"), + Outbound::PendingRemote(_) => write!(f, "Outbound::PendingRemote"), + Outbound::PendingClose(_) => write!(f, "Outbound::PendingClose"), + } + } +} + /// Errors that can occur while interacting with a substream. #[derive(Debug, thiserror::Error)] pub enum Error { @@ -523,8 +548,6 @@ impl ProtocolsHandler for RendezvousHandler { substream: >::Output, msg: Self::OutboundOpenInfo, ) { - - match self.outbound { SubstreamState::Active(Outbound::PendingNegotiate) => { self.outbound = SubstreamState::Active(Outbound::PendingSend { @@ -556,7 +579,10 @@ impl ProtocolsHandler for RendezvousHandler { ), (InEvent::DiscoverRequest { namespace, cookie }, inbound, SubstreamState::None) => ( inbound, - SubstreamState::Active(Outbound::PendingOpen(Message::Discover { namespace, cookie })), + SubstreamState::Active(Outbound::PendingOpen(Message::Discover { + namespace, + cookie, + })), ), ( InEvent::RegisterResponse { ttl }, @@ -607,7 +633,16 @@ impl ProtocolsHandler for RendezvousHandler { )), outbound, ), - _ => unreachable!("Handler in invalid state"), + (event, inbound, outbound) => { + log::warn!( + "Neither {:?} nor {:?} can handle {:?}", + inbound, + outbound, + event + ); + + (inbound, outbound) + } }; self.inbound = inbound; From 17924e7fe04062badd0cb5d5d5ac47856ccc3795 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 15:44:42 +1000 Subject: [PATCH 167/242] Adapt to latest master --- protocols/rendezvous/tests/harness/mod.rs | 7 ++++--- protocols/rendezvous/tests/rendezvous.rs | 13 +++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index ded0888f422..fabcd2c05b7 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -1,6 +1,7 @@ use async_trait::async_trait; use futures::future; use futures::Future; +use futures::StreamExt; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; use libp2p_core::transport::MemoryTransport; @@ -127,7 +128,7 @@ where let mut listener_done = false; loop { - let dialer_event_fut = self.next_event(); + let dialer_event_fut = self.select_next_some(); tokio::select! { dialer_event = dialer_event_fut => { @@ -143,7 +144,7 @@ where } } }, - listener_event = other.next_event() => { + listener_event = other.select_next_some() => { match listener_event { SwarmEvent::ConnectionEstablished { .. } => { listener_done = true; @@ -171,7 +172,7 @@ where // block until we are actually listening loop { - match self.next_event().await { + match self.select_next_some().await { SwarmEvent::NewListenAddr(addr) if addr == multiaddr => { break; } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 044c2a3e050..015bf93acb6 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -5,6 +5,7 @@ use libp2p_core::PeerId; use libp2p_rendezvous::behaviour::{Difficulty, Event, Rendezvous}; use libp2p_rendezvous::codec::{ErrorCode, DEFAULT_TTL}; use libp2p_swarm::{Swarm, SwarmEvent}; +use futures::StreamExt; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { @@ -103,10 +104,10 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { Some(100_000), ); - match await_events_or_timeout(test.rendezvous_swarm.next(), test.registration_swarm.next()).await { + match await_events_or_timeout(test.rendezvous_swarm.select_next_some(), test.registration_swarm.select_next_some()).await { ( - Event::PeerNotRegistered { .. }, - Event::RegisterFailed { error: err_code, .. }, + SwarmEvent::Behaviour(Event::PeerNotRegistered { .. }), + SwarmEvent::Behaviour(Event::RegisterFailed { error: err_code, .. }), ) => { assert_eq!(err_code, ErrorCode::InvalidTtl); } @@ -173,7 +174,7 @@ impl RendezvousTest { expected_namespace: String, expected_ttl: i64, ) { - match await_events_or_timeout(self.rendezvous_swarm.next_event(), self.registration_swarm.next_event()).await { + match await_events_or_timeout(self.rendezvous_swarm.select_next_some(), self.registration_swarm.select_next_some()).await { ( SwarmEvent::Behaviour(Event::PeerRegistered { peer, registration }), SwarmEvent::Behaviour(Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), @@ -197,10 +198,10 @@ impl RendezvousTest { expected_ttl: i64, expected_peer_id: PeerId, ) { - match await_events_or_timeout(self.rendezvous_swarm.next(), self.discovery_swarm.next()) + match await_events_or_timeout(self.rendezvous_swarm.select_next_some(), self.discovery_swarm.select_next_some()) .await { - (Event::DiscoverServed { .. }, Event::Discovered { registrations, .. }) => { + (SwarmEvent::Behaviour(Event::DiscoverServed { .. }), SwarmEvent::Behaviour(Event::Discovered { registrations, .. })) => { if let Some(reg) = registrations.get(&(expected_namespace.clone(), expected_peer_id)) { From 46c058cfafe1a09562515edb5dd2503b86a0ffea Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 21 Jun 2021 16:01:18 +1000 Subject: [PATCH 168/242] Make use of new `Stream` interface of `Swarm` --- protocols/rendezvous/tests/harness/mod.rs | 35 +++++++++-------------- protocols/rendezvous/tests/rendezvous.rs | 7 ++--- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index fabcd2c05b7..e628daefb5e 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -1,7 +1,8 @@ use async_trait::async_trait; -use futures::future; +use futures::stream::FusedStream; use futures::Future; use futures::StreamExt; +use futures::{future, Stream}; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::transport::upgrade::Version; use libp2p_core::transport::MemoryTransport; @@ -65,31 +66,23 @@ fn get_rand_memory_address() -> Multiaddr { addr } -pub async fn await_events_or_timeout( - swarm_1_event: impl Future, - swarm_2_event: impl Future, -) -> (A, B) +pub async fn await_events_or_timeout( + swarm_1: &mut (impl Stream> + FusedStream + Unpin), + swarm_2: &mut (impl Stream> + FusedStream + Unpin), +) -> (SwarmEvent, SwarmEvent) where - A: Debug, - B: Debug, + SwarmEvent: Debug, + SwarmEvent: Debug, { tokio::time::timeout( Duration::from_secs(30), future::join( - async { - let e1 = swarm_1_event.await; - - log::debug!("Got event1: {:?}", e1); - - e1 - }, - async { - let e2 = swarm_2_event.await; - - log::debug!("Got event2: {:?}", e2); - - e2 - }, + swarm_1 + .inspect(|event| log::debug!("Swarm1 emitted {:?}", event)) + .select_next_some(), + swarm_2 + .inspect(|event| log::debug!("Swarm2 emitted {:?}", event)) + .select_next_some(), ), ) .await diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 015bf93acb6..07b248e3a40 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -5,7 +5,6 @@ use libp2p_core::PeerId; use libp2p_rendezvous::behaviour::{Difficulty, Event, Rendezvous}; use libp2p_rendezvous::codec::{ErrorCode, DEFAULT_TTL}; use libp2p_swarm::{Swarm, SwarmEvent}; -use futures::StreamExt; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { @@ -104,7 +103,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { Some(100_000), ); - match await_events_or_timeout(test.rendezvous_swarm.select_next_some(), test.registration_swarm.select_next_some()).await { + match await_events_or_timeout(&mut test.rendezvous_swarm, &mut test.registration_swarm).await { ( SwarmEvent::Behaviour(Event::PeerNotRegistered { .. }), SwarmEvent::Behaviour(Event::RegisterFailed { error: err_code, .. }), @@ -174,7 +173,7 @@ impl RendezvousTest { expected_namespace: String, expected_ttl: i64, ) { - match await_events_or_timeout(self.rendezvous_swarm.select_next_some(), self.registration_swarm.select_next_some()).await { + match await_events_or_timeout(&mut self.rendezvous_swarm, &mut self.registration_swarm).await { ( SwarmEvent::Behaviour(Event::PeerRegistered { peer, registration }), SwarmEvent::Behaviour(Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), @@ -198,7 +197,7 @@ impl RendezvousTest { expected_ttl: i64, expected_peer_id: PeerId, ) { - match await_events_or_timeout(self.rendezvous_swarm.select_next_some(), self.discovery_swarm.select_next_some()) + match await_events_or_timeout(&mut self.rendezvous_swarm, &mut self.discovery_swarm) .await { (SwarmEvent::Behaviour(Event::DiscoverServed { .. }), SwarmEvent::Behaviour(Event::Discovered { registrations, .. })) => { From 6d4a915b625d102450f91fdf7f134660f0873728 Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 22 Jun 2021 11:12:54 +1000 Subject: [PATCH 169/242] Add examples for rendezvous and discovery --- .../rendezvous/examples/discovery_swarm.rs | 73 +++++++++++++++++++ .../rendezvous/examples/rendezvous_swarm.rs | 64 ++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 protocols/rendezvous/examples/discovery_swarm.rs create mode 100644 protocols/rendezvous/examples/rendezvous_swarm.rs diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discovery_swarm.rs new file mode 100644 index 00000000000..14abcc1a00a --- /dev/null +++ b/protocols/rendezvous/examples/discovery_swarm.rs @@ -0,0 +1,73 @@ +use async_std::task; +use futures::executor::block_on; +use futures::{future, FutureExt, StreamExt}; +use libp2p_core::multihash::Multihash; +use libp2p_core::muxing::StreamMuxerBox; +use libp2p_core::transport::MemoryTransport; +use libp2p_core::upgrade::{SelectUpgrade, Version}; +use libp2p_core::PeerId; +use libp2p_core::{identity, Transport}; +use libp2p_mplex::MplexConfig; +use libp2p_noise::NoiseConfig; +use libp2p_noise::{Keypair, X25519Spec}; +use libp2p_rendezvous::behaviour::{Difficulty, Event, Rendezvous}; +use libp2p_rendezvous::{behaviour, codec}; +use libp2p_swarm::SwarmEvent; +use libp2p_swarm::{Swarm, SwarmBuilder}; +use libp2p_tcp::TcpConfig; +use libp2p_yamux::YamuxConfig; +use std::error::Error; +use std::str::FromStr; +use std::task::Poll; +use std::time::Duration; +use Event::Discovered; + +fn main() { + let identity = identity::Keypair::generate_ed25519(); + let peer_id = PeerId::from(identity.public()); + + let dh_keys = Keypair::::new() + .into_authentic(&identity) + .expect("failed to create dh_keys"); + let noise_config = libp2p_noise::NoiseConfig::xx(dh_keys).into_authenticated(); + + let tcp_config = TcpConfig::new(); + let transport = tcp_config + .upgrade(Version::V1) + .authenticate(noise_config) + .multiplex(SelectUpgrade::new( + YamuxConfig::default(), + MplexConfig::new(), + )) + .timeout(Duration::from_secs(20)) + .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) + .boxed(); + + let difficulty = Difficulty::from_u32(1).unwrap(); + let behaviour = Rendezvous::new(identity, 1000, difficulty); + + let mut swarm = Swarm::new(transport, behaviour, peer_id); + + swarm.dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()); + + let server_peer_id = + PeerId::from_str("12D3KooWCagEX1wyQekJvzPGgBXuqt43LdaqxQj2VN64AyEh13vM").unwrap(); + + task::block_on(async move { + loop { + let event = swarm.next_event().await; + println!("swarm event: {:?}", event); + if let SwarmEvent::ConnectionEstablished { peer_id, .. } = event { + println!("connection establihed: {:?}", peer_id); + swarm.behaviour_mut().discover( + Some("rendezvous".to_string()), + None, + server_peer_id, + ); + }; + if let SwarmEvent::Behaviour(Discovered { registrations, .. }) = event { + println!("discovered: {:?}", registrations.values()); + }; + } + }) +} diff --git a/protocols/rendezvous/examples/rendezvous_swarm.rs b/protocols/rendezvous/examples/rendezvous_swarm.rs new file mode 100644 index 00000000000..c1709ed2e58 --- /dev/null +++ b/protocols/rendezvous/examples/rendezvous_swarm.rs @@ -0,0 +1,64 @@ +use futures::executor::block_on; +use futures::{future, StreamExt}; +use libp2p_core::muxing::StreamMuxerBox; +use libp2p_core::upgrade::{SelectUpgrade, Version}; +use libp2p_core::PeerId; +use libp2p_core::{identity, Transport}; +use libp2p_mplex::MplexConfig; +use libp2p_noise::{Keypair, X25519Spec}; +use libp2p_rendezvous::behaviour::{Difficulty, Rendezvous}; +use libp2p_swarm::Swarm; +use libp2p_tcp::TcpConfig; +use libp2p_yamux::YamuxConfig; +use std::task::Poll; +use std::time::Duration; + +fn main() { + let identity = identity::Keypair::generate_ed25519(); + let peer_id = PeerId::from(identity.public()); + + let dh_keys = Keypair::::new() + .into_authentic(&identity) + .expect("failed to create dh_keys"); + let noise_config = libp2p_noise::NoiseConfig::xx(dh_keys).into_authenticated(); + + let tcp_config = TcpConfig::new(); + let transport = tcp_config + .upgrade(Version::V1) + .authenticate(noise_config) + .multiplex(SelectUpgrade::new( + YamuxConfig::default(), + MplexConfig::new(), + )) + .timeout(Duration::from_secs(20)) + .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) + .boxed(); + + let difficulty = Difficulty::from_u32(1).unwrap(); + let behaviour = Rendezvous::new(identity, 1000, difficulty); + + let mut swarm = Swarm::new(transport, behaviour, peer_id); + + println!("peer id: {}", swarm.local_peer_id()); + + swarm + .listen_on("/ip4/0.0.0.0/tcp/62649".parse().unwrap()) + .unwrap(); + + let mut listening = false; + block_on(future::poll_fn(move |cx| loop { + match swarm.poll_next_unpin(cx) { + Poll::Ready(Some(event)) => println!("{:?}", event), + Poll::Ready(None) => return Poll::Ready(()), + Poll::Pending => { + if !listening { + for addr in Swarm::listeners(&swarm) { + println!("Listening on {}", addr); + listening = true; + } + } + return Poll::Pending; + } + } + })); +} From e87c5b89daee291f6b7ebd0043c5fcde2d6a0a6a Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 22 Jun 2021 12:09:04 +1000 Subject: [PATCH 170/242] Add rendezous swarm Node trying to register does not know its external address --- .../rendezvous/examples/discovery_swarm.rs | 11 ++-- .../rendezvous/examples/registration_swarm.rs | 65 +++++++++++++++++++ .../rendezvous/examples/rendezvous_swarm.rs | 32 ++++----- 3 files changed, 82 insertions(+), 26 deletions(-) create mode 100644 protocols/rendezvous/examples/registration_swarm.rs diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discovery_swarm.rs index 14abcc1a00a..c5fa37620ea 100644 --- a/protocols/rendezvous/examples/discovery_swarm.rs +++ b/protocols/rendezvous/examples/discovery_swarm.rs @@ -43,7 +43,7 @@ fn main() { .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let difficulty = Difficulty::from_u32(1).unwrap(); + let difficulty = Difficulty::from_u32(2).unwrap(); let behaviour = Rendezvous::new(identity, 1000, difficulty); let mut swarm = Swarm::new(transport, behaviour, peer_id); @@ -51,21 +51,20 @@ fn main() { swarm.dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()); let server_peer_id = - PeerId::from_str("12D3KooWCagEX1wyQekJvzPGgBXuqt43LdaqxQj2VN64AyEh13vM").unwrap(); + PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); task::block_on(async move { loop { - let event = swarm.next_event().await; + let event = swarm.next().await; println!("swarm event: {:?}", event); - if let SwarmEvent::ConnectionEstablished { peer_id, .. } = event { - println!("connection establihed: {:?}", peer_id); + if let Some(SwarmEvent::ConnectionEstablished { peer_id, .. }) = event { swarm.behaviour_mut().discover( Some("rendezvous".to_string()), None, server_peer_id, ); }; - if let SwarmEvent::Behaviour(Discovered { registrations, .. }) = event { + if let Some(SwarmEvent::Behaviour(Discovered { registrations, .. })) = event { println!("discovered: {:?}", registrations.values()); }; } diff --git a/protocols/rendezvous/examples/registration_swarm.rs b/protocols/rendezvous/examples/registration_swarm.rs new file mode 100644 index 00000000000..5ff7e7f64b6 --- /dev/null +++ b/protocols/rendezvous/examples/registration_swarm.rs @@ -0,0 +1,65 @@ +use async_std::task; +use futures::StreamExt; +use libp2p_core::muxing::StreamMuxerBox; +use libp2p_core::upgrade::{SelectUpgrade, Version}; +use libp2p_core::PeerId; +use libp2p_core::{identity, Transport}; +use libp2p_mplex::MplexConfig; +use libp2p_noise::{Keypair, X25519Spec}; +use libp2p_rendezvous::behaviour::{Difficulty, Event, Rendezvous}; +use libp2p_swarm::Swarm; +use libp2p_swarm::SwarmEvent; +use libp2p_tcp::TcpConfig; +use libp2p_yamux::YamuxConfig; +use std::str::FromStr; +use std::time::Duration; + +fn main() { + let identity = identity::Keypair::generate_ed25519(); + let peer_id = PeerId::from(identity.public()); + + let dh_keys = Keypair::::new() + .into_authentic(&identity) + .expect("failed to create dh_keys"); + let noise_config = libp2p_noise::NoiseConfig::xx(dh_keys).into_authenticated(); + + let tcp_config = TcpConfig::new(); + let transport = tcp_config + .upgrade(Version::V1) + .authenticate(noise_config) + .multiplex(SelectUpgrade::new( + YamuxConfig::default(), + MplexConfig::new(), + )) + .timeout(Duration::from_secs(20)) + .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) + .boxed(); + + let difficulty = Difficulty::from_u32(2).unwrap(); + let behaviour = Rendezvous::new(identity, 1000, difficulty); + + let mut swarm = Swarm::new(transport, behaviour, peer_id); + + swarm + .dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()) + .unwrap(); + + let server_peer_id = + PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); + + task::block_on(async move { + loop { + let event = swarm.next().await; + println!("swarm event: {:?}", event); + if let Some(SwarmEvent::ConnectionEstablished { .. }) = event { + swarm + .behaviour_mut() + .register("rendezvous".to_string(), server_peer_id, None) + .unwrap(); + }; + if let Some(SwarmEvent::Behaviour(Event::Registered { namespace, .. })) = event { + println!("registered namespace: {:?}", namespace); + }; + } + }) +} diff --git a/protocols/rendezvous/examples/rendezvous_swarm.rs b/protocols/rendezvous/examples/rendezvous_swarm.rs index c1709ed2e58..fbd76ffb350 100644 --- a/protocols/rendezvous/examples/rendezvous_swarm.rs +++ b/protocols/rendezvous/examples/rendezvous_swarm.rs @@ -1,5 +1,5 @@ -use futures::executor::block_on; -use futures::{future, StreamExt}; +use async_std::task; +use futures::StreamExt; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::upgrade::{SelectUpgrade, Version}; use libp2p_core::PeerId; @@ -10,11 +10,13 @@ use libp2p_rendezvous::behaviour::{Difficulty, Rendezvous}; use libp2p_swarm::Swarm; use libp2p_tcp::TcpConfig; use libp2p_yamux::YamuxConfig; -use std::task::Poll; use std::time::Duration; fn main() { - let identity = identity::Keypair::generate_ed25519(); + let bytes = [0u8; 32]; + let key = identity::ed25519::SecretKey::from_bytes(bytes).expect("we always pass 32 bytes"); + let identity = identity::Keypair::Ed25519(key.into()); + let peer_id = PeerId::from(identity.public()); let dh_keys = Keypair::::new() @@ -34,7 +36,7 @@ fn main() { .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let difficulty = Difficulty::from_u32(1).unwrap(); + let difficulty = Difficulty::from_u32(2).unwrap(); let behaviour = Rendezvous::new(identity, 1000, difficulty); let mut swarm = Swarm::new(transport, behaviour, peer_id); @@ -45,20 +47,10 @@ fn main() { .listen_on("/ip4/0.0.0.0/tcp/62649".parse().unwrap()) .unwrap(); - let mut listening = false; - block_on(future::poll_fn(move |cx| loop { - match swarm.poll_next_unpin(cx) { - Poll::Ready(Some(event)) => println!("{:?}", event), - Poll::Ready(None) => return Poll::Ready(()), - Poll::Pending => { - if !listening { - for addr in Swarm::listeners(&swarm) { - println!("Listening on {}", addr); - listening = true; - } - } - return Poll::Pending; - } + task::block_on(async move { + loop { + let event = swarm.next().await; + println!("swarm event: {:?}", event); } - })); + }); } From bdd2cc4d57a560a5375d0eae932d91b69fa841ed Mon Sep 17 00:00:00 2001 From: rishflab Date: Tue, 22 Jun 2021 17:09:34 +1000 Subject: [PATCH 171/242] Registration is working --- core/src/network.rs | 383 ++++++++++-------- protocols/rendezvous/Cargo.toml | 3 + .../rendezvous/examples/discovery_swarm.rs | 18 +- .../rendezvous/examples/registration_swarm.rs | 71 +++- .../rendezvous/examples/rendezvous_swarm.rs | 60 ++- protocols/rendezvous/src/behaviour.rs | 98 +++-- 6 files changed, 398 insertions(+), 235 deletions(-) diff --git a/core/src/network.rs b/core/src/network.rs index 584e098f849..8affc3da52d 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -21,41 +21,29 @@ mod event; pub mod peer; -pub use crate::connection::{ConnectionLimits, ConnectionCounters}; -pub use event::{NetworkEvent, IncomingConnection}; +pub use crate::connection::{ConnectionCounters, ConnectionLimits}; +pub use event::{IncomingConnection, NetworkEvent}; pub use peer::Peer; use crate::{ - ConnectedPoint, - Executor, - Multiaddr, - PeerId, connection::{ - ConnectionId, - ConnectionLimit, - ConnectionHandler, - IntoConnectionHandler, - IncomingInfo, - OutgoingInfo, - ListenersEvent, - ListenerId, - ListenersStream, - PendingConnectionError, - Substream, manager::ManagerConfig, pool::{Pool, PoolEvent}, + ConnectionHandler, ConnectionId, ConnectionLimit, IncomingInfo, IntoConnectionHandler, + ListenerId, ListenersEvent, ListenersStream, OutgoingInfo, PendingConnectionError, + Substream, }, muxing::StreamMuxer, transport::{Transport, TransportError}, + ConnectedPoint, Executor, Multiaddr, PeerId, }; -use fnv::{FnvHashMap}; -use futures::{prelude::*, future}; +use fnv::FnvHashMap; +use futures::{future, prelude::*}; use smallvec::SmallVec; use std::{ collections::hash_map, convert::TryFrom as _, - error, - fmt, + error, fmt, num::NonZeroUsize, pin::Pin, task::{Context, Poll}, @@ -74,8 +62,13 @@ where listeners: ListenersStream, /// The nodes currently active. - pool: Pool::Error>, + pool: Pool< + TInEvent, + TOutEvent, + THandler, + TTrans::Error, + ::Error, + >, /// The ongoing dialing attempts. /// @@ -92,8 +85,8 @@ where dialing: FnvHashMap>, } -impl fmt::Debug for - Network +impl fmt::Debug + for Network where TTrans: fmt::Debug + Transport, THandler: fmt::Debug + ConnectionHandler, @@ -108,16 +101,14 @@ where } } -impl Unpin for - Network +impl Unpin for Network where TTrans: Transport, THandler: IntoConnectionHandler, { } -impl - Network +impl Network where TTrans: Transport, THandler: IntoConnectionHandler, @@ -128,22 +119,18 @@ where } } -impl - Network +impl Network where TTrans: Transport + Clone, TMuxer: StreamMuxer, THandler: IntoConnectionHandler + Send + 'static, - THandler::Handler: ConnectionHandler, InEvent = TInEvent, OutEvent = TOutEvent> + Send, + THandler::Handler: ConnectionHandler, InEvent = TInEvent, OutEvent = TOutEvent> + + Send, ::OutboundOpenInfo: Send, ::Error: error::Error + Send, { /// Creates a new node events stream. - pub fn new( - transport: TTrans, - local_peer_id: PeerId, - config: NetworkConfig, - ) -> Self { + pub fn new(transport: TTrans, local_peer_id: PeerId, config: NetworkConfig) -> Self { Network { local_peer_id, listeners: ListenersStream::new(transport), @@ -158,7 +145,10 @@ where } /// Start listening on the given multiaddress. - pub fn listen_on(&mut self, addr: Multiaddr) -> Result> { + pub fn listen_on( + &mut self, + addr: Multiaddr, + ) -> Result> { self.listeners.listen_on(addr) } @@ -186,14 +176,14 @@ where /// other than the peer who reported the `observed_addr`. /// /// The translation is transport-specific. See [`Transport::address_translation`]. - pub fn address_translation<'a>(&'a self, observed_addr: &'a Multiaddr) - -> Vec + pub fn address_translation<'a>(&'a self, observed_addr: &'a Multiaddr) -> Vec where TMuxer: 'a, THandler: 'a, { let transport = self.listeners.transport(); - let mut addrs: Vec<_> = self.listen_addrs() + let mut addrs: Vec<_> = self + .listen_addrs() .filter_map(move |server| transport.address_translation(server, observed_addr)) .collect(); @@ -215,8 +205,11 @@ where /// The given `handler` will be used to create the /// [`Connection`](crate::connection::Connection) upon success and the /// connection ID is returned. - pub fn dial(&mut self, address: &Multiaddr, handler: THandler) - -> Result + pub fn dial( + &mut self, + address: &Multiaddr, + handler: THandler, + ) -> Result where TTrans: Transport, TTrans::Error: Send + 'static, @@ -237,21 +230,29 @@ where address: address.clone(), handler, remaining: Vec::new(), - }) + }); } } // The address does not specify an expected peer, so just try to dial it as-is, // accepting any peer ID that the remote identifies as. - let info = OutgoingInfo { address, peer_id: None }; + let info = OutgoingInfo { + address, + peer_id: None, + }; match self.transport().clone().dial(address.clone()) { Ok(f) => { - let f = f.map_err(|err| PendingConnectionError::Transport(TransportError::Other(err))); - self.pool.add_outgoing(f, handler, info).map_err(DialError::ConnectionLimit) + let f = + f.map_err(|err| PendingConnectionError::Transport(TransportError::Other(err))); + self.pool + .add_outgoing(f, handler, info) + .map_err(DialError::ConnectionLimit) } Err(err) => { let f = future::err(PendingConnectionError::Transport(err)); - self.pool.add_outgoing(f, handler, info).map_err(DialError::ConnectionLimit) + self.pool + .add_outgoing(f, handler, info) + .map_err(DialError::ConnectionLimit) } } } @@ -273,14 +274,13 @@ where /// Returns the list of addresses we're currently dialing without knowing the `PeerId` of. pub fn unknown_dials(&self) -> impl Iterator { - self.pool.iter_pending_outgoing() - .filter_map(|info| { - if info.peer_id.is_none() { - Some(info.address) - } else { - None - } - }) + self.pool.iter_pending_outgoing().filter_map(|info| { + if info.peer_id.is_none() { + Some(info.address) + } else { + None + } + }) } /// Returns a list of all connected peers, i.e. peers to whom the `Network` @@ -312,9 +312,7 @@ where } /// Obtains a view of a [`Peer`] with the given ID in the network. - pub fn peer(&mut self, peer_id: PeerId) - -> Peer<'_, TTrans, TInEvent, TOutEvent, THandler> - { + pub fn peer(&mut self, peer_id: PeerId) -> Peer<'_, TTrans, TInEvent, TOutEvent, THandler> { Peer::new(self, peer_id) } @@ -337,8 +335,9 @@ where TTrans::Error: Send + 'static, TTrans::ListenerUpgrade: Send + 'static, { - let upgrade = connection.upgrade.map_err(|err| - PendingConnectionError::Transport(TransportError::Other(err))); + let upgrade = connection + .upgrade + .map_err(|err| PendingConnectionError::Transport(TransportError::Other(err))); let info = IncomingInfo { local_addr: &connection.local_addr, send_back_addr: &connection.send_back_addr, @@ -347,7 +346,10 @@ where } /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll<'a>(&'a mut self, cx: &mut Context<'_>) -> Poll> + pub fn poll<'a>( + &'a mut self, + cx: &mut Context<'_>, + ) -> Poll> where TTrans: Transport, TTrans::Error: Send + 'static, @@ -358,7 +360,12 @@ where TInEvent: Send + 'static, TOutEvent: Send + 'static, THandler: IntoConnectionHandler + Send + 'static, - THandler::Handler: ConnectionHandler, InEvent = TInEvent, OutEvent = TOutEvent> + Send + 'static, + THandler::Handler: ConnectionHandler< + Substream = Substream, + InEvent = TInEvent, + OutEvent = TOutEvent, + > + Send + + 'static, ::Error: error::Error + Send + 'static, { // Poll the listener(s) for new connections. @@ -368,7 +375,7 @@ where listener_id, upgrade, local_addr, - send_back_addr + send_back_addr, }) => { return Poll::Ready(NetworkEvent::IncomingConnection { listener_id, @@ -376,17 +383,37 @@ where upgrade, local_addr, send_back_addr, - } + }, }) } - Poll::Ready(ListenersEvent::NewAddress { listener_id, listen_addr }) => { - return Poll::Ready(NetworkEvent::NewListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::NewAddress { + listener_id, + listen_addr, + }) => { + return Poll::Ready(NetworkEvent::NewListenerAddress { + listener_id, + listen_addr, + }) } - Poll::Ready(ListenersEvent::AddressExpired { listener_id, listen_addr }) => { - return Poll::Ready(NetworkEvent::ExpiredListenerAddress { listener_id, listen_addr }) + Poll::Ready(ListenersEvent::AddressExpired { + listener_id, + listen_addr, + }) => { + return Poll::Ready(NetworkEvent::ExpiredListenerAddress { + listener_id, + listen_addr, + }) } - Poll::Ready(ListenersEvent::Closed { listener_id, addresses, reason }) => { - return Poll::Ready(NetworkEvent::ListenerClosed { listener_id, addresses, reason }) + Poll::Ready(ListenersEvent::Closed { + listener_id, + addresses, + reason, + }) => { + return Poll::Ready(NetworkEvent::ListenerClosed { + listener_id, + addresses, + reason, + }) } Poll::Ready(ListenersEvent::Error { listener_id, error }) => { return Poll::Ready(NetworkEvent::ListenerError { listener_id, error }) @@ -396,7 +423,10 @@ where // Poll the known peers. let event = match self.pool.poll(cx) { Poll::Pending => return Poll::Pending, - Poll::Ready(PoolEvent::ConnectionEstablished { connection, num_established }) => { + Poll::Ready(PoolEvent::ConnectionEstablished { + connection, + num_established, + }) => { if let hash_map::Entry::Occupied(mut e) = self.dialing.entry(connection.peer_id()) { e.get_mut().retain(|s| s.current.0 != connection.id()); if e.get().is_empty() { @@ -409,7 +439,14 @@ where num_established, } } - Poll::Ready(PoolEvent::PendingConnectionError { id, endpoint, error, handler, pool, .. }) => { + Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint, + error, + handler, + pool, + .. + }) => { let dialing = &mut self.dialing; let (next, event) = on_connection_failed(dialing, id, endpoint, error, handler); if let Some(dial) = next { @@ -420,35 +457,37 @@ where } event } - Poll::Ready(PoolEvent::ConnectionClosed { id, connected, error, num_established, .. }) => { - NetworkEvent::ConnectionClosed { - id, - connected, - num_established, - error, - } - } + Poll::Ready(PoolEvent::ConnectionClosed { + id, + connected, + error, + num_established, + .. + }) => NetworkEvent::ConnectionClosed { + id, + connected, + num_established, + error, + }, Poll::Ready(PoolEvent::ConnectionEvent { connection, event }) => { - NetworkEvent::ConnectionEvent { - connection, - event, - } - } - Poll::Ready(PoolEvent::AddressChange { connection, new_endpoint, old_endpoint }) => { - NetworkEvent::AddressChange { - connection, - new_endpoint, - old_endpoint, - } + NetworkEvent::ConnectionEvent { connection, event } } + Poll::Ready(PoolEvent::AddressChange { + connection, + new_endpoint, + old_endpoint, + }) => NetworkEvent::AddressChange { + connection, + new_endpoint, + old_endpoint, + }, }; Poll::Ready(event) } /// Initiates a connection attempt to a known peer. - fn dial_peer(&mut self, opts: DialingOpts) - -> Result + fn dial_peer(&mut self, opts: DialingOpts) -> Result where TTrans: Transport, TTrans::Dial: Send + 'static, @@ -458,7 +497,12 @@ where TInEvent: Send + 'static, TOutEvent: Send + 'static, { - dial_peer_impl(self.transport().clone(), &mut self.pool, &mut self.dialing, opts) + dial_peer_impl( + self.transport().clone(), + &mut self.pool, + &mut self.dialing, + opts, + ) } } @@ -474,20 +518,23 @@ struct DialingOpts { /// Standalone implementation of `Network::dial_peer` for more granular borrowing. fn dial_peer_impl( transport: TTrans, - pool: &mut Pool::Error>, + pool: &mut Pool< + TInEvent, + TOutEvent, + THandler, + TTrans::Error, + ::Error, + >, dialing: &mut FnvHashMap>, - opts: DialingOpts + opts: DialingOpts, ) -> Result where THandler: IntoConnectionHandler + Send + 'static, ::Error: error::Error + Send + 'static, ::OutboundOpenInfo: Send + 'static, - THandler::Handler: ConnectionHandler< - Substream = Substream, - InEvent = TInEvent, - OutEvent = TOutEvent, - > + Send + 'static, + THandler::Handler: ConnectionHandler, InEvent = TInEvent, OutEvent = TOutEvent> + + Send + + 'static, TTrans: Transport, TTrans::Dial: Send + 'static, TTrans::Error: error::Error + Send + 'static, @@ -504,23 +551,32 @@ where let result = match transport.dial(addr.clone()) { Ok(fut) => { let fut = fut.map_err(|e| PendingConnectionError::Transport(TransportError::Other(e))); - let info = OutgoingInfo { address: &addr, peer_id: Some(&opts.peer) }; - pool.add_outgoing(fut, opts.handler, info).map_err(DialError::ConnectionLimit) - }, + let info = OutgoingInfo { + address: &addr, + peer_id: Some(&opts.peer), + }; + pool.add_outgoing(fut, opts.handler, info) + .map_err(DialError::ConnectionLimit) + } Err(err) => { let fut = future::err(PendingConnectionError::Transport(err)); - let info = OutgoingInfo { address: &addr, peer_id: Some(&opts.peer) }; - pool.add_outgoing(fut, opts.handler, info).map_err(DialError::ConnectionLimit) - }, + let info = OutgoingInfo { + address: &addr, + peer_id: Some(&opts.peer), + }; + pool.add_outgoing(fut, opts.handler, info) + .map_err(DialError::ConnectionLimit) + } }; if let Ok(id) = &result { - dialing.entry(opts.peer).or_default().push( - peer::DialingState { + dialing + .entry(opts.peer) + .or_default() + .push(peer::DialingState { current: (*id, addr), remaining: opts.remaining, - }, - ); + }); } result @@ -537,22 +593,24 @@ fn on_connection_failed<'a, TTrans, TInEvent, TOutEvent, THandler>( endpoint: ConnectedPoint, error: PendingConnectionError, handler: Option, -) -> (Option>, NetworkEvent<'a, TTrans, TInEvent, TOutEvent, THandler>) +) -> ( + Option>, + NetworkEvent<'a, TTrans, TInEvent, TOutEvent, THandler>, +) where TTrans: Transport, THandler: IntoConnectionHandler, { // Check if the failed connection is associated with a dialing attempt. - let dialing_failed = dialing.iter_mut() - .find_map(|(peer, attempts)| { - if let Some(pos) = attempts.iter().position(|s| s.current.0 == id) { - let attempt = attempts.remove(pos); - let last = attempts.is_empty(); - Some((*peer, attempt, last)) - } else { - None - } - }); + let dialing_failed = dialing.iter_mut().find_map(|(peer, attempts)| { + if let Some(pos) = attempts.iter().position(|s| s.current.0 == id) { + let attempt = attempts.remove(pos); + let last = attempts.is_empty(); + Some((*peer, attempt, last)) + } else { + None + } + }); if let Some((peer_id, mut attempt, last)) = dialing_failed { if last { @@ -562,47 +620,56 @@ where let num_remain = u32::try_from(attempt.remaining.len()).unwrap(); let failed_addr = attempt.current.1.clone(); - let (opts, attempts_remaining) = - if num_remain > 0 { - if let Some(handler) = handler { - let next_attempt = attempt.remaining.remove(0); - let opts = DialingOpts { - peer: peer_id, - handler, - address: next_attempt, - remaining: attempt.remaining - }; - (Some(opts), num_remain) - } else { - // The error is "fatal" for the dialing attempt, since - // the handler was already consumed. All potential - // remaining connection attempts are thus void. - (None, 0) - } + let (opts, attempts_remaining) = if num_remain > 0 { + if let Some(handler) = handler { + let next_attempt = attempt.remaining.remove(0); + let opts = DialingOpts { + peer: peer_id, + handler, + address: next_attempt, + remaining: attempt.remaining, + }; + (Some(opts), num_remain) } else { + // The error is "fatal" for the dialing attempt, since + // the handler was already consumed. All potential + // remaining connection attempts are thus void. (None, 0) - }; + } + } else { + (None, 0) + }; - (opts, NetworkEvent::DialError { - attempts_remaining, - peer_id, - multiaddr: failed_addr, - error, - }) + ( + opts, + NetworkEvent::DialError { + attempts_remaining, + peer_id, + multiaddr: failed_addr, + error, + }, + ) } else { // A pending incoming connection or outgoing connection to an unknown peer failed. match endpoint { - ConnectedPoint::Dialer { address } => - (None, NetworkEvent::UnknownPeerDialError { + ConnectedPoint::Dialer { address } => ( + None, + NetworkEvent::UnknownPeerDialError { multiaddr: address, error, - }), - ConnectedPoint::Listener { local_addr, send_back_addr } => - (None, NetworkEvent::IncomingConnectionError { + }, + ), + ConnectedPoint::Listener { + local_addr, + send_back_addr, + } => ( + None, + NetworkEvent::IncomingConnectionError { local_addr, send_back_addr, - error - }) + error, + }, + ), } } } @@ -655,7 +722,7 @@ impl NetworkConfig { /// only if no executor has already been configured. pub fn or_else_with_executor(mut self, f: F) -> Self where - F: FnOnce() -> Option> + F: FnOnce() -> Option>, { self.manager_config.executor = self.manager_config.executor.or_else(f); self @@ -704,7 +771,7 @@ impl NetworkConfig { fn p2p_addr(peer: PeerId, addr: Multiaddr) -> Result { if let Some(multiaddr::Protocol::P2p(hash)) = addr.iter().last() { if &hash != peer.as_ref() { - return Err(addr) + return Err(addr); } Ok(addr) } else { @@ -729,7 +796,7 @@ mod tests { struct Dummy; impl Executor for Dummy { - fn exec(&self, _: Pin + Send>>) { } + fn exec(&self, _: Pin + Send>>) {} } #[test] diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 89ea741cdb6..47e78237720 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -30,6 +30,9 @@ libp2p-tcp = { path = "../../transports/tcp" } libp2p-noise = { path = "../../transports/noise" } libp2p-yamux = { path = "../../muxers/yamux" } libp2p-mplex = { path = "../../muxers/mplex" } +libp2p-identify = { path = "../../protocols/identify" } +libp2p-swarm-derive = {path = "../../swarm-derive"} +libp2p = {path = "../.."} rand = "0.8" async-std = "1.6.2" env_logger = "0.8" diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discovery_swarm.rs index c5fa37620ea..9a94bac214b 100644 --- a/protocols/rendezvous/examples/discovery_swarm.rs +++ b/protocols/rendezvous/examples/discovery_swarm.rs @@ -1,24 +1,17 @@ use async_std::task; -use futures::executor::block_on; -use futures::{future, FutureExt, StreamExt}; -use libp2p_core::multihash::Multihash; +use futures::StreamExt; use libp2p_core::muxing::StreamMuxerBox; -use libp2p_core::transport::MemoryTransport; use libp2p_core::upgrade::{SelectUpgrade, Version}; use libp2p_core::PeerId; use libp2p_core::{identity, Transport}; use libp2p_mplex::MplexConfig; -use libp2p_noise::NoiseConfig; use libp2p_noise::{Keypair, X25519Spec}; use libp2p_rendezvous::behaviour::{Difficulty, Event, Rendezvous}; -use libp2p_rendezvous::{behaviour, codec}; +use libp2p_swarm::Swarm; use libp2p_swarm::SwarmEvent; -use libp2p_swarm::{Swarm, SwarmBuilder}; use libp2p_tcp::TcpConfig; use libp2p_yamux::YamuxConfig; -use std::error::Error; use std::str::FromStr; -use std::task::Poll; use std::time::Duration; use Event::Discovered; @@ -44,11 +37,11 @@ fn main() { .boxed(); let difficulty = Difficulty::from_u32(2).unwrap(); - let behaviour = Rendezvous::new(identity, 1000, difficulty); + let behaviour = Rendezvous::new(identity, 10000, difficulty); let mut swarm = Swarm::new(transport, behaviour, peer_id); - swarm.dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()); + let _ = swarm.dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()); let server_peer_id = PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); @@ -56,8 +49,7 @@ fn main() { task::block_on(async move { loop { let event = swarm.next().await; - println!("swarm event: {:?}", event); - if let Some(SwarmEvent::ConnectionEstablished { peer_id, .. }) = event { + if let Some(SwarmEvent::ConnectionEstablished { .. }) = event { swarm.behaviour_mut().discover( Some("rendezvous".to_string()), None, diff --git a/protocols/rendezvous/examples/registration_swarm.rs b/protocols/rendezvous/examples/registration_swarm.rs index 5ff7e7f64b6..256aed66021 100644 --- a/protocols/rendezvous/examples/registration_swarm.rs +++ b/protocols/rendezvous/examples/registration_swarm.rs @@ -1,12 +1,14 @@ use async_std::task; use futures::StreamExt; +use libp2p::NetworkBehaviour; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::upgrade::{SelectUpgrade, Version}; use libp2p_core::PeerId; use libp2p_core::{identity, Transport}; +use libp2p_identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p_mplex::MplexConfig; use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::behaviour::{Difficulty, Event, Rendezvous}; +use libp2p_rendezvous::behaviour::{Difficulty, Event as RendezvousEvent, Rendezvous}; use libp2p_swarm::Swarm; use libp2p_swarm::SwarmEvent; use libp2p_tcp::TcpConfig; @@ -14,6 +16,32 @@ use libp2p_yamux::YamuxConfig; use std::str::FromStr; use std::time::Duration; +#[derive(Debug)] +enum MyEvent { + Rendezvous(RendezvousEvent), + Identify(IdentifyEvent), +} + +impl From for MyEvent { + fn from(event: RendezvousEvent) -> Self { + MyEvent::Rendezvous(event) + } +} + +impl From for MyEvent { + fn from(event: IdentifyEvent) -> Self { + MyEvent::Identify(event) + } +} + +#[derive(NetworkBehaviour)] +#[behaviour(event_process = false)] +#[behaviour(out_event = "MyEvent")] +struct MyBehaviour { + identify: Identify, + rendezvous: Rendezvous, +} + fn main() { let identity = identity::Keypair::generate_ed25519(); let peer_id = PeerId::from(identity.public()); @@ -35,10 +63,22 @@ fn main() { .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let difficulty = Difficulty::from_u32(2).unwrap(); - let behaviour = Rendezvous::new(identity, 1000, difficulty); + let identify = Identify::new(IdentifyConfig::new( + "rendezvous-example/1.0.0".to_string(), + identity.public(), + )); + let rendezvous = Rendezvous::new(identity, 10000, Difficulty::from_u32(2).unwrap()); - let mut swarm = Swarm::new(transport, behaviour, peer_id); + let mut swarm = Swarm::new( + transport, + MyBehaviour { + identify, + rendezvous, + }, + peer_id, + ); + + let _ = swarm.listen_on("/ip4/127.0.0.1/tcp/62343".parse().unwrap()); swarm .dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()) @@ -50,15 +90,20 @@ fn main() { task::block_on(async move { loop { let event = swarm.next().await; - println!("swarm event: {:?}", event); - if let Some(SwarmEvent::ConnectionEstablished { .. }) = event { - swarm - .behaviour_mut() - .register("rendezvous".to_string(), server_peer_id, None) - .unwrap(); - }; - if let Some(SwarmEvent::Behaviour(Event::Registered { namespace, .. })) = event { - println!("registered namespace: {:?}", namespace); + match event { + Some(SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { + .. + }))) => { + swarm + .behaviour_mut() + .rendezvous + .register("rendezvous".to_string(), server_peer_id, None) + .unwrap(); + } + Some(SwarmEvent::Behaviour(MyEvent::Rendezvous(event))) => { + println!("registered event: {:?}", event); + } + _ => {} }; } }) diff --git a/protocols/rendezvous/examples/rendezvous_swarm.rs b/protocols/rendezvous/examples/rendezvous_swarm.rs index fbd76ffb350..7262d70da1b 100644 --- a/protocols/rendezvous/examples/rendezvous_swarm.rs +++ b/protocols/rendezvous/examples/rendezvous_swarm.rs @@ -1,18 +1,46 @@ -use async_std::task; use futures::StreamExt; +use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; +use libp2p::NetworkBehaviour; use libp2p_core::muxing::StreamMuxerBox; use libp2p_core::upgrade::{SelectUpgrade, Version}; use libp2p_core::PeerId; use libp2p_core::{identity, Transport}; use libp2p_mplex::MplexConfig; use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::behaviour::{Difficulty, Rendezvous}; +use libp2p_rendezvous::behaviour::{Difficulty, Event as RendezvousEvent, Rendezvous}; use libp2p_swarm::Swarm; use libp2p_tcp::TcpConfig; use libp2p_yamux::YamuxConfig; use std::time::Duration; -fn main() { +#[derive(Debug)] +enum MyEvent { + Rendezvous(RendezvousEvent), + Identify(IdentifyEvent), +} + +impl From for MyEvent { + fn from(event: RendezvousEvent) -> Self { + MyEvent::Rendezvous(event) + } +} + +impl From for MyEvent { + fn from(event: IdentifyEvent) -> Self { + MyEvent::Identify(event) + } +} + +#[derive(NetworkBehaviour)] +#[behaviour(event_process = false)] +#[behaviour(out_event = "MyEvent")] +struct MyBehaviour { + identify: Identify, + rendezvous: Rendezvous, +} + +#[tokio::main] +async fn main() { let bytes = [0u8; 32]; let key = identity::ed25519::SecretKey::from_bytes(bytes).expect("we always pass 32 bytes"); let identity = identity::Keypair::Ed25519(key.into()); @@ -36,10 +64,20 @@ fn main() { .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let difficulty = Difficulty::from_u32(2).unwrap(); - let behaviour = Rendezvous::new(identity, 1000, difficulty); + let identify = Identify::new(IdentifyConfig::new( + "rendezvous-example/1.0.0".to_string(), + identity.public(), + )); + let rendezvous = Rendezvous::new(identity, 10000, Difficulty::from_u32(2).unwrap()); - let mut swarm = Swarm::new(transport, behaviour, peer_id); + let mut swarm = Swarm::new( + transport, + MyBehaviour { + identify, + rendezvous, + }, + peer_id, + ); println!("peer id: {}", swarm.local_peer_id()); @@ -47,10 +85,8 @@ fn main() { .listen_on("/ip4/0.0.0.0/tcp/62649".parse().unwrap()) .unwrap(); - task::block_on(async move { - loop { - let event = swarm.next().await; - println!("swarm event: {:?}", event); - } - }); + loop { + let event = swarm.next().await; + println!("swarm event: {:?}", event); + } } diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 9d9c93b6939..eac1131703d 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -27,8 +27,7 @@ pub struct Rendezvous { events: VecDeque>, registrations: Registrations, key_pair: Keypair, - external_addresses: Vec, - + pending_register_requests: Vec<(String, PeerId, Option)>, /// The maximum PoW difficulty we are willing to accept from a rendezvous point. max_accepted_difficulty: Difficulty, } @@ -43,7 +42,7 @@ impl Rendezvous { events: Default::default(), registrations: Registrations::new(ttl_upper_board), key_pair, - external_addresses: vec![], + pending_register_requests: vec![], max_accepted_difficulty, } } @@ -54,25 +53,8 @@ impl Rendezvous { rendezvous_node: PeerId, ttl: Option, ) -> Result<(), RegisterError> { - if self.external_addresses.is_empty() { - return Err(RegisterError::NoExternalAddresses); - } - - let peer_record = PeerRecord::new(self.key_pair.clone(), self.external_addresses.clone())?; - - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: rendezvous_node, - event: InEvent::RegisterRequest { - request: NewRegistration { - namespace, - record: peer_record, - ttl, - }, - }, - handler: NotifyHandler::Any, - }); - + self.pending_register_requests + .push((namespace, rendezvous_node, ttl)); Ok(()) } @@ -109,6 +91,12 @@ pub enum RegisterError { NoExternalAddresses, #[error("Failed to make a new PeerRecord")] FailedToMakeRecord(#[from] SigningError), + #[error("Failed to register with Rendezvous node")] + Remote { + rendezvous_node: PeerId, + namespace: String, + error: ErrorCode, + }, } #[derive(Debug)] @@ -132,11 +120,7 @@ pub enum Event { namespace: String, }, /// We failed to register with the contained rendezvous node. - RegisterFailed { - rendezvous_node: PeerId, - namespace: String, - error: ErrorCode, - }, + RegisterFailed(RegisterError), /// We successfully served a discover request from a peer. DiscoverServed { enquirer: PeerId, @@ -256,13 +240,15 @@ impl NetworkBehaviour for Rendezvous { namespace, })) } - OutEvent::RegisterFailed { namespace, error } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::RegisterFailed { - rendezvous_node: peer_id, - namespace, - error, - }), - ), + OutEvent::RegisterFailed { namespace, error } => { + self.events.push_back(NetworkBehaviourAction::GenerateEvent( + Event::RegisterFailed(RegisterError::Remote { + rendezvous_node: peer_id, + namespace, + error, + }), + )) + } OutEvent::UnregisterRequested { namespace } => { self.registrations.remove(namespace, peer_id); } @@ -315,7 +301,7 @@ impl NetworkBehaviour for Rendezvous { fn poll( &mut self, - _: &mut Context<'_>, + _cx: &mut Context<'_>, poll_params: &mut impl PollParameters, ) -> Poll< NetworkBehaviourAction< @@ -323,14 +309,48 @@ impl NetworkBehaviour for Rendezvous { Self::OutEvent, >, > { - // 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. - self.external_addresses = poll_params.external_addresses().map(|r| r.addr).collect(); - if let Some(event) = self.events.pop_front() { return Poll::Ready(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 external_addresses = poll_params + .external_addresses() + .map(|r| r.addr) + .collect::>(); + + if external_addresses.is_empty() { + self.pending_register_requests + .push((namespace, rendezvous_node, ttl)); + return Poll::Ready(NetworkBehaviourAction::GenerateEvent( + Event::RegisterFailed(RegisterError::NoExternalAddresses), + )); + } + + match PeerRecord::new(self.key_pair.clone(), external_addresses) { + Ok(peer_record) => { + return Poll::Ready(NetworkBehaviourAction::NotifyHandler { + peer_id: rendezvous_node, + event: InEvent::RegisterRequest { + request: NewRegistration { + namespace, + record: peer_record, + ttl, + }, + }, + handler: NotifyHandler::Any, + }) + } + Err(signing_error) => { + return Poll::Ready(NetworkBehaviourAction::GenerateEvent( + Event::RegisterFailed(RegisterError::FailedToMakeRecord(signing_error)), + )) + } + } + } + Poll::Pending } } From 26b2ac1bc52a74d89b9b59c5a51a276d27eb84c3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 11:22:19 +1000 Subject: [PATCH 172/242] Remove unnecessary naming of field in single field struct variant The name of the newtype is descriptive enough, we can be more concise by not naming the field. --- protocols/rendezvous/src/behaviour.rs | 12 +++++------- protocols/rendezvous/src/handler.rs | 6 ++---- protocols/rendezvous/tests/rendezvous.rs | 9 +++++---- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index eac1131703d..28410e56f60 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -333,13 +333,11 @@ impl NetworkBehaviour for Rendezvous { Ok(peer_record) => { return Poll::Ready(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: InEvent::RegisterRequest { - request: NewRegistration { - namespace, - record: peer_record, - ttl, - }, - }, + event: InEvent::RegisterRequest(NewRegistration { + namespace, + record: peer_record, + ttl, + }), handler: NotifyHandler::Any, }) } diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 18151eb01f1..14264ae59a8 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -85,9 +85,7 @@ pub enum OutEvent { #[derive(Debug)] pub enum InEvent { - RegisterRequest { - request: NewRegistration, - }, + RegisterRequest(NewRegistration), DeclineRegisterRequest(DeclineReason), UnregisterRequest { namespace: String, @@ -569,7 +567,7 @@ impl ProtocolsHandler for RendezvousHandler { mem::replace(&mut self.inbound, SubstreamState::Poisoned), mem::replace(&mut self.outbound, SubstreamState::Poisoned), ) { - (InEvent::RegisterRequest { request: reggo }, inbound, SubstreamState::None) => ( + (InEvent::RegisterRequest(reggo), inbound, SubstreamState::None) => ( inbound, SubstreamState::Active(Outbound::PendingOpen(Message::Register(reggo))), ), diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 07b248e3a40..5ed0eb6fdc5 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -197,10 +197,11 @@ impl RendezvousTest { expected_ttl: i64, expected_peer_id: PeerId, ) { - match await_events_or_timeout(&mut self.rendezvous_swarm, &mut self.discovery_swarm) - .await - { - (SwarmEvent::Behaviour(Event::DiscoverServed { .. }), SwarmEvent::Behaviour(Event::Discovered { registrations, .. })) => { + match await_events_or_timeout(&mut self.rendezvous_swarm, &mut self.discovery_swarm).await { + ( + SwarmEvent::Behaviour(Event::DiscoverServed { .. }), + SwarmEvent::Behaviour(Event::Discovered { registrations, .. }), + ) => { if let Some(reg) = registrations.get(&(expected_namespace.clone(), expected_peer_id)) { From c3b1de510fbdff67d1b7e170efb08e1a1d74b9da Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 11:39:39 +1000 Subject: [PATCH 173/242] RIP PoW --- .../rendezvous/examples/discovery_swarm.rs | 5 +- .../rendezvous/examples/registration_swarm.rs | 4 +- .../rendezvous/examples/rendezvous_swarm.rs | 4 +- protocols/rendezvous/src/behaviour.rs | 85 +----- protocols/rendezvous/src/codec.rs | 118 +------- protocols/rendezvous/src/handler.rs | 262 +++--------------- protocols/rendezvous/src/lib.rs | 1 - protocols/rendezvous/src/pow.rs | 192 ------------- protocols/rendezvous/src/rpc.proto | 11 - protocols/rendezvous/tests/rendezvous.rs | 31 +-- 10 files changed, 72 insertions(+), 641 deletions(-) delete mode 100644 protocols/rendezvous/src/pow.rs diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discovery_swarm.rs index 9a94bac214b..aa92d89addc 100644 --- a/protocols/rendezvous/examples/discovery_swarm.rs +++ b/protocols/rendezvous/examples/discovery_swarm.rs @@ -6,7 +6,7 @@ use libp2p_core::PeerId; use libp2p_core::{identity, Transport}; use libp2p_mplex::MplexConfig; use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::behaviour::{Difficulty, Event, Rendezvous}; +use libp2p_rendezvous::behaviour::{Event, Rendezvous}; use libp2p_swarm::Swarm; use libp2p_swarm::SwarmEvent; use libp2p_tcp::TcpConfig; @@ -36,8 +36,7 @@ fn main() { .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let difficulty = Difficulty::from_u32(2).unwrap(); - let behaviour = Rendezvous::new(identity, 10000, difficulty); + let behaviour = Rendezvous::new(identity, 10000); let mut swarm = Swarm::new(transport, behaviour, peer_id); diff --git a/protocols/rendezvous/examples/registration_swarm.rs b/protocols/rendezvous/examples/registration_swarm.rs index 256aed66021..6f9f9dcb305 100644 --- a/protocols/rendezvous/examples/registration_swarm.rs +++ b/protocols/rendezvous/examples/registration_swarm.rs @@ -8,7 +8,7 @@ use libp2p_core::{identity, Transport}; use libp2p_identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p_mplex::MplexConfig; use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::behaviour::{Difficulty, Event as RendezvousEvent, Rendezvous}; +use libp2p_rendezvous::behaviour::{Event as RendezvousEvent, Rendezvous}; use libp2p_swarm::Swarm; use libp2p_swarm::SwarmEvent; use libp2p_tcp::TcpConfig; @@ -67,7 +67,7 @@ fn main() { "rendezvous-example/1.0.0".to_string(), identity.public(), )); - let rendezvous = Rendezvous::new(identity, 10000, Difficulty::from_u32(2).unwrap()); + let rendezvous = Rendezvous::new(identity, 10000); let mut swarm = Swarm::new( transport, diff --git a/protocols/rendezvous/examples/rendezvous_swarm.rs b/protocols/rendezvous/examples/rendezvous_swarm.rs index 7262d70da1b..51044d95285 100644 --- a/protocols/rendezvous/examples/rendezvous_swarm.rs +++ b/protocols/rendezvous/examples/rendezvous_swarm.rs @@ -7,7 +7,7 @@ use libp2p_core::PeerId; use libp2p_core::{identity, Transport}; use libp2p_mplex::MplexConfig; use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::behaviour::{Difficulty, Event as RendezvousEvent, Rendezvous}; +use libp2p_rendezvous::behaviour::{Event as RendezvousEvent, Rendezvous}; use libp2p_swarm::Swarm; use libp2p_tcp::TcpConfig; use libp2p_yamux::YamuxConfig; @@ -68,7 +68,7 @@ async fn main() { "rendezvous-example/1.0.0".to_string(), identity.public(), )); - let rendezvous = Rendezvous::new(identity, 10000, Difficulty::from_u32(2).unwrap()); + let rendezvous = Rendezvous::new(identity, 10000); let mut swarm = Swarm::new( transport, diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 28410e56f60..893447978a3 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,8 +1,6 @@ -pub use crate::pow::Difficulty; - use crate::codec::{Cookie, ErrorCode, NewRegistration, Registration}; use crate::handler; -use crate::handler::{DeclineReason, InEvent, OutEvent, RendezvousHandler}; +use crate::handler::{InEvent, OutEvent, RendezvousHandler}; use bimap::BiMap; use futures::future::BoxFuture; use futures::ready; @@ -28,22 +26,15 @@ pub struct Rendezvous { registrations: Registrations, key_pair: Keypair, pending_register_requests: Vec<(String, PeerId, Option)>, - /// The maximum PoW difficulty we are willing to accept from a rendezvous point. - max_accepted_difficulty: Difficulty, } impl Rendezvous { - pub fn new( - key_pair: Keypair, - ttl_upper_board: i64, - max_accepted_difficulty: Difficulty, - ) -> Self { + pub fn new(key_pair: Keypair, ttl_upper_board: i64) -> Self { Self { events: Default::default(), registrations: Registrations::new(ttl_upper_board), key_pair, pending_register_requests: vec![], - max_accepted_difficulty, } } @@ -146,7 +137,7 @@ impl NetworkBehaviour for Rendezvous { type OutEvent = Event; fn new_handler(&mut self) -> Self::ProtocolsHandler { - RendezvousHandler::new(self.max_accepted_difficulty) + RendezvousHandler::default() } fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { @@ -170,24 +161,7 @@ impl NetworkBehaviour for Rendezvous { event: handler::OutEvent, ) { match event { - OutEvent::RegistrationRequested { - registration, - pow_difficulty: provided_difficulty, - } => { - let expected_difficulty = self.registrations.expected_pow(®istration); - - if expected_difficulty > provided_difficulty { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: InEvent::DeclineRegisterRequest(DeclineReason::PowRequired { - target: expected_difficulty, - }), - }); - return; - } - + OutEvent::RegistrationRequested(registration) => { let namespace = registration.namespace.clone(); let record = registration.record.clone(); @@ -217,9 +191,7 @@ impl NetworkBehaviour for Rendezvous { NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: InEvent::DeclineRegisterRequest( - DeclineReason::BadRegistration(error), - ), + event: InEvent::DeclineRegisterRequest(error), }, NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { peer: peer_id, @@ -509,18 +481,6 @@ impl Registrations { Ok((registrations, new_cookie)) } - pub fn expected_pow(&self, registration: &NewRegistration) -> Difficulty { - let peer = registration.record.peer_id(); - - let num_registrations = self - .registrations_for_peer - .left_values() - .filter(|(candidate, _)| candidate == &peer) - .count(); - - difficulty_from_num_registrations(num_registrations) - } - pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll { 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", @@ -543,17 +503,6 @@ impl Registrations { } } -fn difficulty_from_num_registrations(existing_registrations: usize) -> Difficulty { - if existing_registrations == 0 { - return Difficulty::ZERO; - } - - let new_registrations = existing_registrations + 1; - - Difficulty::from_u32(((new_registrations) as f32 / 2f32).round() as u32) - .unwrap_or(Difficulty::MAX) -} - #[derive(Debug, thiserror::Error, Eq, PartialEq)] #[error("The provided cookie is not valid for a DISCOVER request for the given namespace")] pub struct CookieNamespaceMismatch; @@ -726,30 +675,6 @@ mod tests { assert_eq!(registrations.cookies.len(), 0); } - #[test] - fn first_registration_is_free() { - let required = difficulty_from_num_registrations(0); - let expected = Difficulty::ZERO; - - assert_eq!(required, expected) - } - - #[test] - fn second_registration_is_not_free() { - let required = difficulty_from_num_registrations(1); - let expected = Difficulty::from_u32(1).unwrap(); - - assert_eq!(required, expected) - } - - #[test] - fn fourth_registration_requires_two() { - let required = difficulty_from_num_registrations(3); - let expected = Difficulty::from_u32(2).unwrap(); - - assert_eq!(required, expected) - } - fn new_dummy_registration(namespace: &str) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 45f467337d1..44576ca22ea 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -1,7 +1,5 @@ -use crate::pow::Difficulty; use asynchronous_codec::{Bytes, BytesMut, Decoder, Encoder}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; -use rand::RngCore; use std::convert::{TryFrom, TryInto}; use std::time::SystemTime; use unsigned_varint::codec::UviBytes; @@ -12,7 +10,7 @@ pub type Ttl = i64; #[derive(Debug, Clone)] pub enum Message { Register(NewRegistration), - RegisterResponse(Result), + RegisterResponse(Result), Unregister { namespace: String, }, @@ -21,39 +19,6 @@ pub enum Message { cookie: Option, }, DiscoverResponse(Result<(Vec, Cookie), ErrorCode>), - ProofOfWork { - hash: [u8; 32], - nonce: i64, - }, -} - -#[derive(Debug, Clone)] -pub enum RegisterErrorResponse { - Failed(ErrorCode), - /// We are declining the registration because PoW is required. - PowRequired { - challenge: Challenge, - target: Difficulty, - }, -} - -/// A challenge that needs to be incorporated into the PoW hash by the client. -/// -/// Sending a challenge to the client ensures the PoW is unique to the registration and the client cannot pre-compute or reuse an existing hash. -#[derive(Debug, Clone)] -pub struct Challenge(Vec); - -impl Challenge { - pub fn new(rng: &mut impl RngCore) -> Self { - let mut bytes = [0u8; 32]; - rng.fill_bytes(&mut bytes); - - Self(bytes.to_vec()) - } - - pub fn as_bytes(&self) -> &[u8] { - self.0.as_slice() - } } #[derive(Debug, Eq, PartialEq, Hash, Clone)] @@ -255,7 +220,6 @@ impl From for wire::Message { unregister: None, discover: None, discover_response: None, - proof_of_work: None, }, Message::RegisterResponse(Ok(ttl)) => wire::Message { r#type: Some(MessageType::RegisterResponse.into()), @@ -263,47 +227,23 @@ impl From for wire::Message { status: Some(ResponseStatus::Ok.into()), status_text: None, ttl: Some(ttl), - challenge: None, - target_difficulty: None, }), register: None, discover: None, unregister: None, discover_response: None, - proof_of_work: None, }, - Message::RegisterResponse(Err(RegisterErrorResponse::Failed(error))) => wire::Message { + Message::RegisterResponse(Err(error)) => wire::Message { r#type: Some(MessageType::RegisterResponse.into()), register_response: Some(RegisterResponse { status: Some(ResponseStatus::from(error).into()), status_text: None, ttl: None, - challenge: None, - target_difficulty: None, - }), - register: None, - discover: None, - unregister: None, - discover_response: None, - proof_of_work: None, - }, - Message::RegisterResponse(Err(RegisterErrorResponse::PowRequired { - challenge, - target, - })) => wire::Message { - r#type: Some(MessageType::RegisterResponse.into()), - register_response: Some(RegisterResponse { - status: Some(ResponseStatus::EPowRequired.into()), - status_text: None, - ttl: None, - challenge: Some(challenge.0), - target_difficulty: Some(target.to_u32()), }), register: None, discover: None, unregister: None, discover_response: None, - proof_of_work: None, }, Message::Unregister { namespace } => wire::Message { r#type: Some(MessageType::Unregister.into()), @@ -315,7 +255,6 @@ impl From for wire::Message { register_response: None, discover: None, discover_response: None, - proof_of_work: None, }, Message::Discover { namespace, cookie } => wire::Message { r#type: Some(MessageType::Discover.into()), @@ -328,7 +267,6 @@ impl From for wire::Message { register_response: None, unregister: None, discover_response: None, - proof_of_work: None, }, Message::DiscoverResponse(Ok((registrations, cookie))) => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), @@ -351,7 +289,6 @@ impl From for wire::Message { discover: None, unregister: None, register_response: None, - proof_of_work: None, }, Message::DiscoverResponse(Err(error)) => wire::Message { r#type: Some(MessageType::DiscoverResponse.into()), @@ -365,19 +302,6 @@ impl From for wire::Message { discover: None, unregister: None, register_response: None, - proof_of_work: None, - }, - Message::ProofOfWork { hash, nonce } => wire::Message { - r#type: Some(MessageType::ProofOfWork.into()), - proof_of_work: Some(ProofOfWork { - hash: Some(hash.to_vec()), - nonce: Some(nonce), - }), - register: None, - discover: None, - discover_response: None, - unregister: None, - register_response: None, }, } } @@ -461,30 +385,14 @@ impl TryFrom for Message { register_response: Some(RegisterResponse { status: Some(error_code), - challenge: challenge_bytes, - target_difficulty, .. }), .. } => { - let error = wire::message::ResponseStatus::from_i32(error_code) - .ok_or(ConversionError::BadStatusCode)?; - - let error_response = match (error, challenge_bytes, target_difficulty) { - ( - wire::message::ResponseStatus::EPowRequired, - Some(bytes), - Some(target_difficulty), - ) => RegisterErrorResponse::PowRequired { - challenge: Challenge(bytes), - target: Difficulty::from_u32(target_difficulty) - .ok_or(ConversionError::PoWDifficultyOutOfRange)?, - }, - (code, None, None) => RegisterErrorResponse::Failed(code.try_into()?), - _ => return Err(ConversionError::InconsistentWireMessage), - }; - - Message::RegisterResponse(Err(error_response)) + let error_code = wire::message::ResponseStatus::from_i32(error_code) + .ok_or(ConversionError::BadStatusCode)? + .try_into()?; + Message::RegisterResponse(Err(error_code)) } wire::Message { r#type: Some(2), @@ -507,18 +415,6 @@ impl TryFrom for Message { .try_into()?; Message::DiscoverResponse(Err(error)) } - wire::Message { - r#type: Some(5), - proof_of_work: - Some(ProofOfWork { - hash: Some(hash), - nonce: Some(nonce), - }), - .. - } => Message::ProofOfWork { - hash: hash.try_into().map_err(|_| ConversionError::BadPoWHash)?, - nonce, - }, _ => return Err(ConversionError::InconsistentWireMessage), }; @@ -574,7 +470,7 @@ impl TryFrom for ErrorCode { use wire::message::ResponseStatus::*; let code = match value { - Ok | EPowRequired | EDifficultyTooLow => return Err(UnmappableStatusCode(value)), + Ok => return Err(UnmappableStatusCode(value)), EInvalidNamespace => ErrorCode::InvalidNamespace, EInvalidSignedPeerRecord => ErrorCode::InvalidSignedPeerRecord, EInvalidTtl => ErrorCode::InvalidTtl, diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 14264ae59a8..30095d64179 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,10 +1,8 @@ use crate::codec::{ - self, Challenge, Cookie, ErrorCode, Message, NewRegistration, RegisterErrorResponse, - Registration, RendezvousCodec, + self, Cookie, ErrorCode, Message, NewRegistration, Registration, RendezvousCodec, }; -use crate::pow::Difficulty; +use crate::protocol; use crate::substream::{Advance, Next, SubstreamState}; -use crate::{pow, protocol}; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; use libp2p_core::{InboundUpgrade, OutboundUpgrade}; @@ -12,7 +10,6 @@ use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; -use std::sync::mpsc::TryRecvError; use std::task::{Context, Poll}; use std::{fmt, mem}; use void::Void; @@ -22,15 +19,13 @@ pub struct RendezvousHandler { outbound_history: MessageHistory, inbound: SubstreamState, inbound_history: MessageHistory, - max_difficulty: Difficulty, } -impl RendezvousHandler { - pub fn new(max_difficulty: Difficulty) -> Self { +impl Default for RendezvousHandler { + fn default() -> Self { Self { outbound: SubstreamState::None, outbound_history: Default::default(), - max_difficulty, inbound: SubstreamState::None, inbound_history: Default::default(), } @@ -52,12 +47,7 @@ impl MessageHistory { #[derive(Debug, Clone)] pub enum OutEvent { - /// A peer wants to store registration with us. - RegistrationRequested { - registration: NewRegistration, - /// The PoW that was supplied with the registration. - pow_difficulty: Difficulty, - }, + RegistrationRequested(NewRegistration), Registered { namespace: String, ttl: i64, @@ -86,7 +76,7 @@ pub enum OutEvent { #[derive(Debug)] pub enum InEvent { RegisterRequest(NewRegistration), - DeclineRegisterRequest(DeclineReason), + DeclineRegisterRequest(ErrorCode), UnregisterRequest { namespace: String, }, @@ -103,12 +93,6 @@ pub enum InEvent { }, } -#[derive(Debug)] -pub enum DeclineReason { - BadRegistration(ErrorCode), - PowRequired { target: Difficulty }, -} - /// The state of an inbound substream (i.e. the remote node opened it). enum Inbound { /// We are in the process of reading a message from the substream. @@ -117,8 +101,6 @@ enum Inbound { PendingBehaviour(Framed), /// We are in the process of sending a response. PendingSend(Framed, Message), - /// We started sending and are currently flushing the data out, afterwards we will go and read the next message. - PendingFlushThenRead(Framed), /// We've sent the message and are now closing down the substream. PendingClose(Framed), } @@ -129,7 +111,6 @@ impl fmt::Debug for Inbound { Inbound::PendingRead(_) => write!(f, "Inbound::PendingRead"), Inbound::PendingBehaviour(_) => write!(f, "Inbound::PendingBehaviour"), Inbound::PendingSend(_, _) => write!(f, "Inbound::PendingSend"), - Inbound::PendingFlushThenRead(_) => write!(f, "Inbound::PendingFlushThenRead"), Inbound::PendingClose(_) => write!(f, "Inbound::PendingClose"), } } @@ -148,11 +129,6 @@ enum Outbound { }, /// We sent the message, now we need to flush the data out. PendingFlush(Framed), - /// We are waiting for our PoW thread to finish. - PendingPoW { - substream: Framed, - channel: std::sync::mpsc::Receiver>, - }, /// We are waiting for the response from the remote. PendingRemote(Framed), /// We are closing down the substream. @@ -166,7 +142,6 @@ impl fmt::Debug for Outbound { Outbound::PendingNegotiate => write!(f, "Outbound::PendingNegotiate"), Outbound::PendingSend { .. } => write!(f, "Outbound::PendingSend"), Outbound::PendingFlush(_) => write!(f, "Outbound::PendingFlush"), - Outbound::PendingPoW { .. } => write!(f, "Outbound::PendingPoW"), Outbound::PendingRemote(_) => write!(f, "Outbound::PendingRemote"), Outbound::PendingClose(_) => write!(f, "Outbound::PendingClose"), } @@ -184,15 +159,6 @@ pub enum Error { ReadMessage(#[source] codec::Error), #[error("Substream ended unexpectedly mid-protocol")] UnexpectedEndOfStream, - #[error("Failed to compute proof of work")] - PowFailed(#[from] pow::ExhaustedNonceSpace), - #[error("Failed to verify Proof of Work")] - BadPoWSupplied(#[from] pow::VerifyError), - #[error("Rendezvous point requested difficulty {requested} but we are only willing to produce {limit}")] - MaxDifficultyExceeded { - requested: Difficulty, - limit: Difficulty, - }, } struct InboundPollParams<'handler> { @@ -211,67 +177,39 @@ impl<'handler> Advance<'handler> for Inbound { InboundPollParams { history }: &mut Self::Params, ) -> Result, Self::Error> { Ok(match self { - Inbound::PendingRead(mut substream) => match substream - .poll_next_unpin(cx) - .map_err(Error::ReadMessage)? - { - Poll::Ready(Some(msg)) => { - let event = match ( - history.received.as_slice(), - history.sent.as_slice(), - msg.clone(), - ) { - (.., Message::Register(registration)) => OutEvent::RegistrationRequested { - registration, - pow_difficulty: Difficulty::ZERO, // initial Register has no PoW - }, - // this next pattern matches if: - // 1. the first message we received from this peer was `Register` - // 2. the last message we sent to them was `PowRequired` - // 3. the message we just received is `ProofOfWork` - ( - [Message::Register(registration), ..], - [.., Message::RegisterResponse(Err(RegisterErrorResponse::PowRequired { - challenge, - target: target_difficulty, - }))], - Message::ProofOfWork { hash, nonce }, - ) => { - pow::verify( - challenge.as_bytes(), - registration.namespace.as_str(), - registration.record.to_signed_envelope(), - *target_difficulty, - hash, - nonce, - )?; - - OutEvent::RegistrationRequested { - registration: registration.clone(), - pow_difficulty: pow::difficulty_of(&hash), + Inbound::PendingRead(mut substream) => { + match substream.poll_next_unpin(cx).map_err(Error::ReadMessage)? { + Poll::Ready(Some(msg)) => { + let event = match ( + history.received.as_slice(), + history.sent.as_slice(), + msg.clone(), + ) { + (.., Message::Register(registration)) => { + OutEvent::RegistrationRequested(registration) } - } - (.., Message::Discover { cookie, namespace }) => { - OutEvent::DiscoverRequested { cookie, namespace } - } - (.., Message::Unregister { namespace }) => { - OutEvent::UnregisterRequested { namespace } - } - (.., other) => return Err(Error::BadMessage(other)), - }; + (.., Message::Discover { cookie, namespace }) => { + OutEvent::DiscoverRequested { cookie, namespace } + } + (.., Message::Unregister { namespace }) => { + OutEvent::UnregisterRequested { namespace } + } + (.., other) => return Err(Error::BadMessage(other)), + }; - history.received.push(msg); + history.received.push(msg); - Next::EmitEvent { - event, - next_state: Inbound::PendingBehaviour(substream), + Next::EmitEvent { + event, + next_state: Inbound::PendingBehaviour(substream), + } } + Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), + Poll::Pending => Next::Pending { + next_state: Inbound::PendingRead(substream), + }, } - Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), - Poll::Pending => Next::Pending { - next_state: Inbound::PendingRead(substream), - }, - }, + } Inbound::PendingBehaviour(substream) => Next::Pending { next_state: Inbound::PendingBehaviour(substream), }, @@ -284,40 +222,16 @@ impl<'handler> Advance<'handler> for Inbound { .start_send_unpin(message.clone()) .map_err(Error::WriteMessage)?; - let next = match message { - // In case we requested PoW from the client, we need to wait for the response and hence go to `PendingFlushThenRead` afterwards - Message::RegisterResponse(Err(RegisterErrorResponse::PowRequired { - .. - })) => Next::Continue { - next_state: Inbound::PendingFlushThenRead(substream), - }, - // In case of any other message, just close the stream (that implies flushing) - _ => Next::Continue { - next_state: Inbound::PendingClose(substream), - }, - }; - history.sent.push(message); - next + Next::Continue { + next_state: Inbound::PendingClose(substream), + } } Poll::Pending => Next::Pending { next_state: Inbound::PendingSend(substream, message), }, }, - Inbound::PendingFlushThenRead(mut substream) => { - match substream - .poll_flush_unpin(cx) - .map_err(Error::WriteMessage)? - { - Poll::Ready(()) => Next::Continue { - next_state: Inbound::PendingRead(substream), - }, - Poll::Pending => Next::Pending { - next_state: Inbound::PendingFlushThenRead(substream), - }, - } - } Inbound::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 @@ -331,7 +245,6 @@ impl<'handler> Advance<'handler> for Inbound { struct OutboundPollParams<'handler> { history: &'handler mut MessageHistory, - max_difficulty: Difficulty, } impl<'handler> Advance<'handler> for Outbound { @@ -343,10 +256,7 @@ impl<'handler> Advance<'handler> for Outbound { fn advance( self, cx: &mut Context<'_>, - OutboundPollParams { - history, - max_difficulty, - }: &mut OutboundPollParams, + OutboundPollParams { history }: &mut OutboundPollParams, ) -> Result, Self::Error> { Ok(match self { Outbound::PendingOpen(msg) => Next::OpenSubstream { @@ -407,13 +317,12 @@ impl<'handler> Advance<'handler> for Outbound { namespace: registration.namespace.to_owned(), ttl, }, - ( - [Register(registration), ..], - RegisterResponse(Err(RegisterErrorResponse::Failed(error))), - ) => RegisterFailed { - namespace: registration.namespace.to_owned(), - error, - }, + ([Register(registration), ..], RegisterResponse(Err(error))) => { + RegisterFailed { + namespace: registration.namespace.to_owned(), + error, + } + } ([Discover { .. }, ..], DiscoverResponse(Ok((registrations, cookie)))) => { Discovered { registrations, @@ -426,48 +335,6 @@ impl<'handler> Advance<'handler> for Outbound { error, } } - ( - [Register(registration), ..], - RegisterResponse(Err(RegisterErrorResponse::PowRequired { - challenge, - target: target_difficulty, - })), - ) => { - if target_difficulty > *max_difficulty { - return Err(Error::MaxDifficultyExceeded { - requested: target_difficulty, - limit: *max_difficulty, - }); - } - - let (sender, receiver) = std::sync::mpsc::channel(); - - // do the PoW on a separate thread to not block the networking tasks - std::thread::spawn({ - let waker = cx.waker().clone(); - let registration = registration.clone(); - - move || { - let result = pow::run( - challenge.as_bytes(), - ®istration.namespace, - registration.record.to_signed_envelope(), - target_difficulty, - ); - - waker.wake(); // let the runtime know that we are ready, this should get us polled - - let _ = sender.send(result); - } - }); - - return Ok(Next::Continue { - next_state: Outbound::PendingPoW { - substream, - channel: receiver, - }, - }); - } (.., other) => return Err(Error::BadMessage(other)), }; @@ -481,26 +348,6 @@ impl<'handler> Advance<'handler> for Outbound { next_state: Outbound::PendingRemote(substream), }, }, - Outbound::PendingPoW { substream, channel } => { - let (hash, nonce) = match channel.try_recv() { - Ok(result) => result?, - Err(TryRecvError::Empty) => { - return Ok(Next::Pending { - next_state: Outbound::PendingPoW { substream, channel }, - }) - } - Err(TryRecvError::Disconnected) => { - unreachable!("sender is never dropped") - } - }; - - return Ok(Next::Continue { - next_state: Outbound::PendingSend { - substream, - to_send: Message::ProofOfWork { hash, nonce }, - }, - }); - } Outbound::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 @@ -594,29 +441,13 @@ impl ProtocolsHandler for RendezvousHandler { outbound, ), ( - InEvent::DeclineRegisterRequest(DeclineReason::BadRegistration(error)), - SubstreamState::Active(Inbound::PendingBehaviour(substream)), - outbound, - ) => ( - SubstreamState::Active(Inbound::PendingSend( - substream, - Message::RegisterResponse(Err(RegisterErrorResponse::Failed(error))), - )), - outbound, - ), - ( - InEvent::DeclineRegisterRequest(DeclineReason::PowRequired { - target: target_difficulty, - }), + InEvent::DeclineRegisterRequest(error), SubstreamState::Active(Inbound::PendingBehaviour(substream)), outbound, ) => ( SubstreamState::Active(Inbound::PendingSend( substream, - Message::RegisterResponse(Err(RegisterErrorResponse::PowRequired { - challenge: Challenge::new(&mut rand::thread_rng()), - target: target_difficulty, - })), + Message::RegisterResponse(Err(error)), )), outbound, ), @@ -682,7 +513,6 @@ impl ProtocolsHandler for RendezvousHandler { cx, &mut OutboundPollParams { history: &mut self.outbound_history, - max_difficulty: self.max_difficulty, }, ) { return Poll::Ready(event); diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 83d89c8111e..8d5432fd806 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,6 +1,5 @@ pub mod behaviour; pub mod codec; mod handler; -mod pow; mod protocol; mod substream; diff --git a/protocols/rendezvous/src/pow.rs b/protocols/rendezvous/src/pow.rs deleted file mode 100644 index a0004fd4d26..00000000000 --- a/protocols/rendezvous/src/pow.rs +++ /dev/null @@ -1,192 +0,0 @@ -use libp2p_core::SignedEnvelope; -use sha2::{Digest, Sha256}; -use std::fmt; - -const DOMAIN_TAG: &str = "libp2p-rendezvous-pow"; - -/// Run the Proof of Work algorithm for a registration at a rendezvous node. -/// -/// Returns the calculated hash together with the computed nonce. -/// In the odd case that the nonce space is exhausted without meeting the difficulty requirement, the function fails with [`ExhaustedNonceSpace`]. -pub fn run( - challenge: &[u8], - namespace: &str, - envelope: SignedEnvelope, - target_difficulty: Difficulty, -) -> Result<([u8; 32], i64), ExhaustedNonceSpace> { - let envelope = envelope.into_protobuf_encoding(); - - for i in i64::MIN..i64::MAX { - let hash = make_hash(challenge, namespace, envelope.as_slice(), i); - - if difficulty_of(&hash) < target_difficulty { - continue; - } - - return Ok((hash, i)); - } - - Err(ExhaustedNonceSpace) -} - -/// Verifies a PoW hash against the given parameters and requested difficulty. -pub fn verify( - challenge: &[u8], - namespace: &str, - envelope: SignedEnvelope, - requested_difficulty: Difficulty, - hash: [u8; 32], - nonce: i64, -) -> Result<(), VerifyError> { - if difficulty_of(&hash) < requested_difficulty { - return Err(VerifyError::DifficultyTooLow); - } - - let actual_hash = make_hash( - challenge, - namespace, - envelope.into_protobuf_encoding().as_slice(), - nonce, - ); - - if actual_hash != hash { - return Err(VerifyError::NotTheSameHash); - } - - Ok(()) -} - -/// The difficulty of a PoW hash. -/// -/// Measured as the number of leading `0`s at the front of the hash in big-endian representation. -#[derive(Debug, PartialEq, Clone, Copy, PartialOrd)] -pub struct Difficulty(u32); - -impl fmt::Display for Difficulty { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Difficulty { - pub const ZERO: Difficulty = Difficulty(0); - pub const MAX: Difficulty = Difficulty(32); - - pub fn from_u32(value: u32) -> Option { - if value > 32 { - return None; - } - - Some(Difficulty(value)) - } - - pub fn to_u32(self) -> u32 { - self.0 - } -} - -#[derive(Debug)] -pub struct ExhaustedNonceSpace; - -impl fmt::Display for ExhaustedNonceSpace { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Exhausted nonce space while computing proof of work") - } -} - -impl std::error::Error for ExhaustedNonceSpace {} - -#[derive(Debug, PartialEq)] -pub enum VerifyError { - DifficultyTooLow, - NotTheSameHash, -} - -impl fmt::Display for VerifyError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - VerifyError::DifficultyTooLow => { - write!(f, "The difficulty of the provided hash is too low") - } - VerifyError::NotTheSameHash => write!(f, "The hash differs unexpectedly"), - } - } -} - -impl std::error::Error for VerifyError {} - -/// Returns the difficulty of the provided hash. -pub fn difficulty_of(hash: &[u8; 32]) -> Difficulty { - for i in 0..32 { - if hash[i as usize] != 0 { - return Difficulty(i); - } - } - - Difficulty(32) -} - -fn make_hash(challenge: &[u8], namespace: &str, envelope: &[u8], nonce: i64) -> [u8; 32] { - let mut digest = Sha256::new(); - - digest.update(DOMAIN_TAG.as_bytes()); - digest.update(challenge); - digest.update(namespace.as_bytes()); - digest.update(envelope); - digest.update(&nonce.to_le_bytes()); - - digest.finalize().into() -} - -#[cfg(test)] -mod tests { - use super::*; - use libp2p_core::{identity, PeerRecord}; - - #[test] - fn pow_verifies() { - let challenge = b"foobar"; - let namespace = "baz"; - let envelope = PeerRecord::new(identity::Keypair::generate_ed25519(), vec![]) - .unwrap() - .into_signed_envelope(); - - let (hash, nonce) = run(challenge, namespace, envelope.clone(), Difficulty(1)).unwrap(); - let result = verify(challenge, namespace, envelope, Difficulty(1), hash, nonce); - - assert_eq!(result, Ok(())) - } - - #[test] - fn meets_difficulty_test_cases() { - let test_cases = &[ - ( - [ - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, - ], - Difficulty(5), - ), - ( - [ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, - ], - Difficulty(0), - ), - ( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - ], - Difficulty(32), - ), - ]; - - for (hash, expected_result) in test_cases { - let actual = difficulty_of(&hash); - - assert_eq!(actual, *expected_result); - } - } -} diff --git a/protocols/rendezvous/src/rpc.proto b/protocols/rendezvous/src/rpc.proto index a80d8d4a015..f3cbd9505ff 100644 --- a/protocols/rendezvous/src/rpc.proto +++ b/protocols/rendezvous/src/rpc.proto @@ -9,7 +9,6 @@ message Message { UNREGISTER = 2; DISCOVER = 3; DISCOVER_RESPONSE = 4; - PROOF_OF_WORK = 5; } enum ResponseStatus { @@ -18,8 +17,6 @@ message Message { E_INVALID_SIGNED_PEER_RECORD = 101; E_INVALID_TTL = 102; E_INVALID_COOKIE = 103; - E_POW_REQUIRED = 104; - E_DIFFICULTY_TOO_LOW = 105; E_NOT_AUTHORIZED = 200; E_INTERNAL_ERROR = 300; E_UNAVAILABLE = 400; @@ -35,8 +32,6 @@ message Message { optional ResponseStatus status = 1; optional string statusText = 2; optional int64 ttl = 3; // in seconds - optional bytes challenge = 4; - optional uint32 target_difficulty = 5; } message Unregister { @@ -57,16 +52,10 @@ message Message { optional string statusText = 4; } - message ProofOfWork { - optional bytes hash = 1; - optional int64 nonce = 2; - } - optional MessageType type = 1; optional Register register = 2; optional RegisterResponse registerResponse = 3; optional Unregister unregister = 4; optional Discover discover = 5; optional DiscoverResponse discoverResponse = 6; - optional ProofOfWork proofOfWork = 7; } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 5ed0eb6fdc5..c387132887a 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -2,7 +2,7 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use libp2p_core::PeerId; -use libp2p_rendezvous::behaviour::{Difficulty, Event, Rendezvous}; +use libp2p_rendezvous::behaviour::{Event, RegisterError, Rendezvous}; use libp2p_rendezvous::codec::{ErrorCode, DEFAULT_TTL}; use libp2p_swarm::{Swarm, SwarmEvent}; @@ -106,7 +106,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { match await_events_or_timeout(&mut test.rendezvous_swarm, &mut test.registration_swarm).await { ( SwarmEvent::Behaviour(Event::PeerNotRegistered { .. }), - SwarmEvent::Behaviour(Event::RegisterFailed { error: err_code, .. }), + SwarmEvent::Behaviour(Event::RegisterFailed(RegisterError::Remote { error: err_code , ..})), ) => { assert_eq!(err_code, ErrorCode::InvalidTtl); } @@ -127,31 +127,16 @@ const DEFAULT_TTL_UPPER_BOUND: i64 = 56_000; impl RendezvousTest { pub async fn setup() -> Self { - let mut registration_swarm = new_swarm(|_, identity| { - Rendezvous::new( - identity, - DEFAULT_TTL_UPPER_BOUND, - Difficulty::from_u32(2).expect("2 < 32"), - ) - }); + let mut registration_swarm = + new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); registration_swarm.listen_on_random_memory_address().await; - let mut discovery_swarm = new_swarm(|_, identity| { - Rendezvous::new( - identity, - DEFAULT_TTL_UPPER_BOUND, - Difficulty::from_u32(2).expect("2 < 32"), - ) - }); + let mut discovery_swarm = + new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); discovery_swarm.listen_on_random_memory_address().await; - let mut rendezvous_swarm = new_swarm(|_, identity| { - Rendezvous::new( - identity, - DEFAULT_TTL_UPPER_BOUND, - Difficulty::from_u32(2).expect("2 < 32"), - ) - }); + let mut rendezvous_swarm = + new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); rendezvous_swarm.listen_on_random_memory_address().await; registration_swarm From eae577b008711391c359405e8b1348089fa7855a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 11:45:21 +1000 Subject: [PATCH 174/242] Free handler from abstractions that were only needed with PoW --- protocols/rendezvous/src/handler.rs | 38 ++++++----------------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 30095d64179..0334b6bb341 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -18,7 +18,6 @@ pub struct RendezvousHandler { outbound: SubstreamState, outbound_history: MessageHistory, inbound: SubstreamState, - inbound_history: MessageHistory, } impl Default for RendezvousHandler { @@ -27,7 +26,6 @@ impl Default for RendezvousHandler { outbound: SubstreamState::None, outbound_history: Default::default(), inbound: SubstreamState::None, - inbound_history: Default::default(), } } } @@ -35,13 +33,11 @@ impl Default for RendezvousHandler { #[derive(Default)] struct MessageHistory { sent: Vec, - received: Vec, } impl MessageHistory { fn clear(&mut self) { self.sent.clear(); - self.received.clear(); } } @@ -161,44 +157,34 @@ pub enum Error { UnexpectedEndOfStream, } -struct InboundPollParams<'handler> { - history: &'handler mut MessageHistory, -} - impl<'handler> Advance<'handler> for Inbound { type Event = OutEvent; - type Params = InboundPollParams<'handler>; + type Params = (); type Error = Error; type Protocol = SubstreamProtocol; fn advance( self, cx: &mut Context<'_>, - InboundPollParams { history }: &mut Self::Params, + _: &mut Self::Params, ) -> Result, Self::Error> { Ok(match self { Inbound::PendingRead(mut substream) => { match substream.poll_next_unpin(cx).map_err(Error::ReadMessage)? { Poll::Ready(Some(msg)) => { - let event = match ( - history.received.as_slice(), - history.sent.as_slice(), - msg.clone(), - ) { - (.., Message::Register(registration)) => { + let event = match msg.clone() { + Message::Register(registration) => { OutEvent::RegistrationRequested(registration) } - (.., Message::Discover { cookie, namespace }) => { + Message::Discover { cookie, namespace } => { OutEvent::DiscoverRequested { cookie, namespace } } - (.., Message::Unregister { namespace }) => { + Message::Unregister { namespace } => { OutEvent::UnregisterRequested { namespace } } - (.., other) => return Err(Error::BadMessage(other)), + other => return Err(Error::BadMessage(other)), }; - history.received.push(msg); - Next::EmitEvent { event, next_state: Inbound::PendingBehaviour(substream), @@ -222,8 +208,6 @@ impl<'handler> Advance<'handler> for Inbound { .start_send_unpin(message.clone()) .map_err(Error::WriteMessage)?; - history.sent.push(message); - Next::Continue { next_state: Inbound::PendingClose(substream), } @@ -380,7 +364,6 @@ impl ProtocolsHandler for RendezvousHandler { match self.inbound { SubstreamState::None => { self.inbound = SubstreamState::Active(Inbound::PendingRead(substream)); - self.inbound_history.clear(); } _ => { log::warn!("Ignoring new inbound substream because existing one is still active") @@ -500,12 +483,7 @@ impl ProtocolsHandler for RendezvousHandler { Self::Error, >, > { - if let Poll::Ready(event) = self.inbound.poll( - cx, - &mut InboundPollParams { - history: &mut self.inbound_history, - }, - ) { + if let Poll::Ready(event) = self.inbound.poll(cx, &mut ()) { return Poll::Ready(event); } From 24c0322b62aac97d99ef7919184f7e60810ef230 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 13:34:10 +1000 Subject: [PATCH 175/242] Respect `limit` parameter of `Discover` requests --- .../rendezvous/examples/discovery_swarm.rs | 1 + protocols/rendezvous/src/behaviour.rs | 82 +++++++++++++++---- protocols/rendezvous/src/codec.rs | 14 +++- protocols/rendezvous/src/handler.rs | 25 +++++- protocols/rendezvous/tests/rendezvous.rs | 3 + 5 files changed, 102 insertions(+), 23 deletions(-) diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discovery_swarm.rs index aa92d89addc..855172ad60b 100644 --- a/protocols/rendezvous/examples/discovery_swarm.rs +++ b/protocols/rendezvous/examples/discovery_swarm.rs @@ -52,6 +52,7 @@ fn main() { swarm.behaviour_mut().discover( Some("rendezvous".to_string()), None, + None, server_peer_id, ); }; diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 893447978a3..0dd2d49c87e 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -16,6 +16,7 @@ use libp2p_swarm::{ use log::debug; use std::collections::{HashMap, HashSet, VecDeque}; use std::iter::FromIterator; +use std::option::Option::None; use std::task::{Context, Poll}; use std::time::{Duration, SystemTime}; use tokio::time::sleep; @@ -62,6 +63,7 @@ impl Rendezvous { &mut self, ns: Option, cookie: Option, + limit: Option, rendezvous_node: PeerId, ) { self.events @@ -70,6 +72,7 @@ impl Rendezvous { event: InEvent::DiscoverRequest { namespace: ns, cookie, + limit, }, handler: NotifyHandler::Any, }); @@ -224,10 +227,14 @@ impl NetworkBehaviour for Rendezvous { OutEvent::UnregisterRequested { namespace } => { self.registrations.remove(namespace, peer_id); } - OutEvent::DiscoverRequested { namespace, cookie } => { + OutEvent::DiscoverRequested { + namespace, + cookie, + limit, + } => { let (registrations, cookie) = self .registrations - .get(namespace, cookie) + .get(namespace, cookie, limit) .expect("TODO: error handling: send back bad cookie"); let discovered = registrations.cloned().collect::>(); @@ -425,6 +432,7 @@ impl Registrations { &mut self, discover_namespace: Option, cookie: Option, + mut limit: Option, ) -> Result<(impl Iterator + '_, Cookie), CookieNamespaceMismatch> { let cookie_namespace = cookie.as_ref().and_then(|cookie| cookie.namespace()); @@ -462,6 +470,15 @@ impl Registrations { } } }) + .take_while(|_| { + let limit = match limit.as_mut() { + None => return true, + Some(limit) => limit, + }; + *limit -= 1; + + *limit >= 0 + }) .cloned() .collect::>(); @@ -511,6 +528,7 @@ pub struct CookieNamespaceMismatch; mod tests { use super::*; use libp2p_core::identity; + use std::option::Option::None; #[tokio::test] async fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { @@ -518,10 +536,10 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); - let (initial_discover, cookie) = registrations.get(None, None).unwrap(); + let (initial_discover, cookie) = registrations.get(None, None, None).unwrap(); assert_eq!(initial_discover.count(), 2); - let (subsequent_discover, _) = registrations.get(None, Some(cookie)).unwrap(); + let (subsequent_discover, _) = registrations.get(None, Some(cookie), None).unwrap(); assert_eq!(subsequent_discover.count(), 0); } @@ -531,7 +549,7 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); - let (discover, _) = registrations.get(None, None).unwrap(); + let (discover, _) = registrations.get(None, None, None).unwrap(); assert_eq!(discover.count(), 2); } @@ -543,7 +561,9 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); - let (discover, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); + let (discover, _) = registrations + .get(Some("foo".to_owned()), None, None) + .unwrap(); assert_eq!( discover.map(|r| r.namespace.as_str()).collect::>(), @@ -562,7 +582,9 @@ mod tests { .add(new_registration("foo", alice, None)) .unwrap(); - let (discover, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); + let (discover, _) = registrations + .get(Some("foo".to_owned()), None, None) + .unwrap(); assert_eq!( discover.map(|r| r.namespace.as_str()).collect::>(), @@ -576,13 +598,13 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); - let (initial_discover, cookie1) = registrations.get(None, None).unwrap(); + let (initial_discover, cookie1) = registrations.get(None, None, None).unwrap(); assert_eq!(initial_discover.count(), 2); - let (subsequent_discover, cookie2) = registrations.get(None, Some(cookie1)).unwrap(); + let (subsequent_discover, cookie2) = registrations.get(None, Some(cookie1), None).unwrap(); assert_eq!(subsequent_discover.count(), 0); - let (subsequent_discover, _) = registrations.get(None, Some(cookie2)).unwrap(); + let (subsequent_discover, _) = registrations.get(None, Some(cookie2), None).unwrap(); assert_eq!(subsequent_discover.count(), 0); } @@ -592,8 +614,10 @@ mod tests { registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); - let (_, foo_discover_cookie) = registrations.get(Some("foo".to_owned()), None).unwrap(); - let result = registrations.get(Some("bar".to_owned()), Some(foo_discover_cookie)); + let (_, foo_discover_cookie) = registrations + .get(Some("foo".to_owned()), None, None) + .unwrap(); + let result = registrations.get(Some("bar".to_owned()), Some(foo_discover_cookie), None); assert!(matches!(result, Err(CookieNamespaceMismatch))) } @@ -620,10 +644,14 @@ mod tests { assert_eq!(event.0.namespace, "foo"); { - let (mut discovered_foo, _) = registrations.get(Some("foo".to_owned()), None).unwrap(); + let (mut discovered_foo, _) = registrations + .get(Some("foo".to_owned()), None, None) + .unwrap(); assert!(discovered_foo.next().is_none()); } - let (mut discovered_bar, _) = registrations.get(Some("bar".to_owned()), None).unwrap(); + let (mut discovered_bar, _) = registrations + .get(Some("bar".to_owned()), None, None) + .unwrap(); assert!(discovered_bar.next().is_some()); } @@ -666,7 +694,7 @@ mod tests { registrations .add(new_dummy_registration_with_ttl("foo", 2)) .unwrap(); - let (_, _) = registrations.get(None, None).unwrap(); + let (_, _) = registrations.get(None, None, None).unwrap(); assert_eq!(registrations.cookies.len(), 1); @@ -675,6 +703,30 @@ mod tests { assert_eq!(registrations.cookies.len(), 0); } + #[tokio::test] + async fn given_limit_discover_only_returns_n_results() { + let mut registrations = Registrations::new(7200); + registrations.add(new_dummy_registration("foo")).unwrap(); + registrations.add(new_dummy_registration("foo")).unwrap(); + + let (registrations, _) = registrations.get(None, None, Some(1)).unwrap(); + + assert_eq!(registrations.count(), 1); + } + + #[tokio::test] + async fn given_limit_cookie_can_be_used_for_pagination() { + let mut registrations = Registrations::new(7200); + registrations.add(new_dummy_registration("foo")).unwrap(); + registrations.add(new_dummy_registration("foo")).unwrap(); + + let (discover1, cookie) = registrations.get(None, None, Some(1)).unwrap(); + assert_eq!(discover1.count(), 1); + + let (discover2, _) = registrations.get(None, Some(cookie), None).unwrap(); + assert_eq!(discover2.count(), 1); + } + fn new_dummy_registration(namespace: &str) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 44576ca22ea..91e2e4cb180 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -17,6 +17,7 @@ pub enum Message { Discover { namespace: Option, cookie: Option, + limit: Option, }, DiscoverResponse(Result<(Vec, Cookie), ErrorCode>), } @@ -256,12 +257,16 @@ impl From for wire::Message { discover: None, discover_response: None, }, - Message::Discover { namespace, cookie } => wire::Message { + Message::Discover { + namespace, + cookie, + limit, + } => wire::Message { r#type: Some(MessageType::Discover.into()), discover: Some(Discover { ns: namespace, cookie: cookie.map(|cookie| cookie.into_wire_encoding()), - limit: None, + limit, }), register: None, register_response: None, @@ -342,11 +347,12 @@ impl TryFrom for Message { } => Message::RegisterResponse(Ok(ttl.ok_or(ConversionError::MissingTtl)?)), wire::Message { r#type: Some(3), - discover: Some(Discover { ns, .. }), + discover: Some(Discover { ns, limit, cookie }), .. } => Message::Discover { namespace: ns, - cookie: None, + cookie: cookie.map(Cookie::from_wire_encoding).transpose()?, + limit, }, wire::Message { r#type: Some(4), diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 0334b6bb341..d1b0c2c8c7c 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -58,6 +58,7 @@ pub enum OutEvent { DiscoverRequested { namespace: Option, cookie: Option, + limit: Option, }, Discovered { registrations: Vec, @@ -79,6 +80,7 @@ pub enum InEvent { DiscoverRequest { namespace: Option, cookie: Option, + limit: Option, }, RegisterResponse { ttl: i64, @@ -176,9 +178,15 @@ impl<'handler> Advance<'handler> for Inbound { Message::Register(registration) => { OutEvent::RegistrationRequested(registration) } - Message::Discover { cookie, namespace } => { - OutEvent::DiscoverRequested { cookie, namespace } - } + Message::Discover { + cookie, + namespace, + limit, + } => OutEvent::DiscoverRequested { + cookie, + namespace, + limit, + }, Message::Unregister { namespace } => { OutEvent::UnregisterRequested { namespace } } @@ -405,11 +413,20 @@ impl ProtocolsHandler for RendezvousHandler { inbound, SubstreamState::Active(Outbound::PendingOpen(Message::Unregister { namespace })), ), - (InEvent::DiscoverRequest { namespace, cookie }, inbound, SubstreamState::None) => ( + ( + InEvent::DiscoverRequest { + namespace, + cookie, + limit, + }, + inbound, + SubstreamState::None, + ) => ( inbound, SubstreamState::Active(Outbound::PendingOpen(Message::Discover { namespace, cookie, + limit, })), ), ( diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index c387132887a..e9c59c52a25 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -25,6 +25,7 @@ async fn given_successful_registration_then_successful_discovery() { test.discovery_swarm.behaviour_mut().discover( Some(namespace.clone()), None, + None, *test.rendezvous_swarm.local_peer_id(), ); @@ -57,6 +58,7 @@ async fn given_successful_registration_then_refresh_ttl() { test.discovery_swarm.behaviour_mut().discover( Some(namespace.clone()), None, + None, *test.rendezvous_swarm.local_peer_id(), ); @@ -79,6 +81,7 @@ async fn given_successful_registration_then_refresh_ttl() { test.discovery_swarm.behaviour_mut().discover( Some(namespace.clone()), None, + None, *test.rendezvous_swarm.local_peer_id(), ); From ee53ba14eb8c1fbc04a52264ad802704f05c98d6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 13:40:55 +1000 Subject: [PATCH 176/242] Make all modules private and only expose particular structs This should help us detect dead code better. --- protocols/rendezvous/examples/discovery_swarm.rs | 8 +++++--- protocols/rendezvous/examples/registration_swarm.rs | 2 +- protocols/rendezvous/examples/rendezvous_swarm.rs | 2 +- protocols/rendezvous/src/lib.rs | 10 ++++++++-- protocols/rendezvous/tests/rendezvous.rs | 4 ++-- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discovery_swarm.rs index 855172ad60b..d791feb8e40 100644 --- a/protocols/rendezvous/examples/discovery_swarm.rs +++ b/protocols/rendezvous/examples/discovery_swarm.rs @@ -6,14 +6,13 @@ use libp2p_core::PeerId; use libp2p_core::{identity, Transport}; use libp2p_mplex::MplexConfig; use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::behaviour::{Event, Rendezvous}; +use libp2p_rendezvous::{Event as RendzevousEvent, Rendezvous}; use libp2p_swarm::Swarm; use libp2p_swarm::SwarmEvent; use libp2p_tcp::TcpConfig; use libp2p_yamux::YamuxConfig; use std::str::FromStr; use std::time::Duration; -use Event::Discovered; fn main() { let identity = identity::Keypair::generate_ed25519(); @@ -56,7 +55,10 @@ fn main() { server_peer_id, ); }; - if let Some(SwarmEvent::Behaviour(Discovered { registrations, .. })) = event { + if let Some(SwarmEvent::Behaviour(RendzevousEvent::Discovered { + registrations, .. + })) = event + { println!("discovered: {:?}", registrations.values()); }; } diff --git a/protocols/rendezvous/examples/registration_swarm.rs b/protocols/rendezvous/examples/registration_swarm.rs index 6f9f9dcb305..cc917e34d4f 100644 --- a/protocols/rendezvous/examples/registration_swarm.rs +++ b/protocols/rendezvous/examples/registration_swarm.rs @@ -8,7 +8,7 @@ use libp2p_core::{identity, Transport}; use libp2p_identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p_mplex::MplexConfig; use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::behaviour::{Event as RendezvousEvent, Rendezvous}; +use libp2p_rendezvous::{Event as RendezvousEvent, Rendezvous}; use libp2p_swarm::Swarm; use libp2p_swarm::SwarmEvent; use libp2p_tcp::TcpConfig; diff --git a/protocols/rendezvous/examples/rendezvous_swarm.rs b/protocols/rendezvous/examples/rendezvous_swarm.rs index 51044d95285..caef74772fe 100644 --- a/protocols/rendezvous/examples/rendezvous_swarm.rs +++ b/protocols/rendezvous/examples/rendezvous_swarm.rs @@ -7,7 +7,7 @@ use libp2p_core::PeerId; use libp2p_core::{identity, Transport}; use libp2p_mplex::MplexConfig; use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::behaviour::{Event as RendezvousEvent, Rendezvous}; +use libp2p_rendezvous::{Event as RendezvousEvent, Rendezvous}; use libp2p_swarm::Swarm; use libp2p_tcp::TcpConfig; use libp2p_yamux::YamuxConfig; diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 8d5432fd806..3121d0175ea 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,5 +1,11 @@ -pub mod behaviour; -pub mod codec; +mod behaviour; +mod codec; mod handler; mod protocol; mod substream; + +pub use behaviour::Event; +pub use behaviour::RegisterError; +pub use behaviour::Rendezvous; +pub use codec::ErrorCode; +pub use codec::DEFAULT_TTL; diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index e9c59c52a25..1e4bdd907e9 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -2,8 +2,8 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use libp2p_core::PeerId; -use libp2p_rendezvous::behaviour::{Event, RegisterError, Rendezvous}; -use libp2p_rendezvous::codec::{ErrorCode, DEFAULT_TTL}; +use libp2p_rendezvous::{ErrorCode, DEFAULT_TTL}; +use libp2p_rendezvous::{Event, RegisterError, Rendezvous}; use libp2p_swarm::{Swarm, SwarmEvent}; #[tokio::test] From cd68a75f955c7e826e59c1c1dc89e8ef8a088240 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 13:44:52 +1000 Subject: [PATCH 177/242] Actually use `Registrations::poll` to check for expired registrations --- protocols/rendezvous/src/behaviour.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 0dd2d49c87e..64bef36de38 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -133,6 +133,8 @@ pub enum Event { }, /// A peer successfully unregistered with us. PeerUnregistered { peer: PeerId, namespace: String }, + /// A registration from a peer expired. + RegistrationExpired(Registration), } impl NetworkBehaviour for Rendezvous { @@ -280,7 +282,7 @@ impl NetworkBehaviour for Rendezvous { fn poll( &mut self, - _cx: &mut Context<'_>, + cx: &mut Context<'_>, poll_params: &mut impl PollParameters, ) -> Poll< NetworkBehaviourAction< @@ -288,6 +290,12 @@ impl NetworkBehaviour for Rendezvous { Self::OutEvent, >, > { + if let Poll::Ready(ExpiredRegistration(registration)) = self.registrations.poll(cx) { + return Poll::Ready(NetworkBehaviourAction::GenerateEvent( + Event::RegistrationExpired(registration), + )); + } + if let Some(event) = self.events.pop_front() { return Poll::Ready(event); } @@ -342,7 +350,7 @@ impl RegistrationId { } #[derive(Debug, PartialEq)] -pub struct RegistrationExpired(Registration); +struct ExpiredRegistration(Registration); pub struct Registrations { registrations_for_peer: BiMap<(PeerId, String), RegistrationId>, @@ -498,7 +506,7 @@ impl Registrations { Ok((registrations, new_cookie)) } - pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll { + fn poll(&mut self, cx: &mut Context<'_>) -> Poll { 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", ); @@ -515,7 +523,7 @@ impl Registrations { .remove_by_right(&expired_registration); match self.registrations.remove(&expired_registration) { None => self.poll(cx), - Some(registration) => Poll::Ready(RegistrationExpired(registration)), + Some(registration) => Poll::Ready(ExpiredRegistration(registration)), } } } @@ -753,7 +761,7 @@ mod tests { /// Defines utility functions that make the tests more readable. impl Registrations { - async fn next_event(&mut self) -> RegistrationExpired { + async fn next_event(&mut self) -> ExpiredRegistration { futures::future::poll_fn(|cx| self.poll(cx)).await } @@ -765,7 +773,7 @@ mod tests { } /// Polls [`Registrations`] for at most `seconds` and panics if doesn't return an event within that time. - async fn next_event_in_at_most(&mut self, seconds: u64) -> RegistrationExpired { + async fn next_event_in_at_most(&mut self, seconds: u64) -> ExpiredRegistration { tokio::time::timeout(Duration::from_secs(seconds), self.next_event()) .await .unwrap() From 0dfba095e02635993c401bb7573e8323ffadbcc0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 13:48:01 +1000 Subject: [PATCH 178/242] Re-export rendezvous from libp2p meta crate --- src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e675b40e7f0..2901ae50071 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -125,6 +125,10 @@ pub use libp2p_relay as relay; #[cfg_attr(docsrs, doc(cfg(feature = "request-response")))] #[doc(inline)] pub use libp2p_request_response as request_response; +#[cfg(feature = "rendezvous")] +#[cfg_attr(docsrs, doc(cfg(feature = "rendezvous")))] +#[doc(inline)] +pub use libp2p_rendezvous as rendezvous; mod transport_ext; From f6cd3b9556b971993076084a727df0bff92e7d84 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 13:54:00 +1000 Subject: [PATCH 179/242] Streamline public interface of libp2p_rendezvous crate --- protocols/rendezvous/Cargo.toml | 8 +---- .../rendezvous/examples/discovery_swarm.rs | 34 +++++++++---------- .../rendezvous/examples/registration_swarm.rs | 33 +++++++++--------- .../rendezvous/examples/rendezvous_swarm.rs | 29 ++++++++-------- protocols/rendezvous/tests/harness/mod.rs | 18 +++++----- 5 files changed, 59 insertions(+), 63 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 47e78237720..fdc40b32e07 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -26,13 +26,7 @@ rand = "0.8" tokio = { version = "1", features = [ "time" ] } [dev-dependencies] -libp2p-tcp = { path = "../../transports/tcp" } -libp2p-noise = { path = "../../transports/noise" } -libp2p-yamux = { path = "../../muxers/yamux" } -libp2p-mplex = { path = "../../muxers/mplex" } -libp2p-identify = { path = "../../protocols/identify" } -libp2p-swarm-derive = {path = "../../swarm-derive"} -libp2p = {path = "../.."} +libp2p = { path = "../.." } rand = "0.8" async-std = "1.6.2" env_logger = "0.8" diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discovery_swarm.rs index d791feb8e40..b38e0bcafca 100644 --- a/protocols/rendezvous/examples/discovery_swarm.rs +++ b/protocols/rendezvous/examples/discovery_swarm.rs @@ -1,16 +1,17 @@ use async_std::task; use futures::StreamExt; -use libp2p_core::muxing::StreamMuxerBox; -use libp2p_core::upgrade::{SelectUpgrade, Version}; -use libp2p_core::PeerId; -use libp2p_core::{identity, Transport}; -use libp2p_mplex::MplexConfig; -use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::{Event as RendzevousEvent, Rendezvous}; -use libp2p_swarm::Swarm; -use libp2p_swarm::SwarmEvent; -use libp2p_tcp::TcpConfig; -use libp2p_yamux::YamuxConfig; +use libp2p::core::muxing::StreamMuxerBox; +use libp2p::core::upgrade::{SelectUpgrade, Version}; +use libp2p::core::PeerId; +use libp2p::core::{identity, Transport}; +use libp2p::mplex::MplexConfig; +use libp2p::noise::{Keypair, X25519Spec}; +use libp2p::rendezvous; +use libp2p::rendezvous::Rendezvous; +use libp2p::swarm::Swarm; +use libp2p::swarm::SwarmEvent; +use libp2p::tcp::TcpConfig; +use libp2p::yamux::YamuxConfig; use std::str::FromStr; use std::time::Duration; @@ -21,7 +22,7 @@ fn main() { let dh_keys = Keypair::::new() .into_authentic(&identity) .expect("failed to create dh_keys"); - let noise_config = libp2p_noise::NoiseConfig::xx(dh_keys).into_authenticated(); + let noise_config = libp2p::noise::NoiseConfig::xx(dh_keys).into_authenticated(); let tcp_config = TcpConfig::new(); let transport = tcp_config @@ -35,9 +36,7 @@ fn main() { .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let behaviour = Rendezvous::new(identity, 10000); - - let mut swarm = Swarm::new(transport, behaviour, peer_id); + let mut swarm = Swarm::new(transport, Rendezvous::new(identity, 10000), peer_id); let _ = swarm.dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()); @@ -55,8 +54,9 @@ fn main() { server_peer_id, ); }; - if let Some(SwarmEvent::Behaviour(RendzevousEvent::Discovered { - registrations, .. + if let Some(SwarmEvent::Behaviour(rendezvous::Event::Discovered { + registrations, + .. })) = event { println!("discovered: {:?}", registrations.values()); diff --git a/protocols/rendezvous/examples/registration_swarm.rs b/protocols/rendezvous/examples/registration_swarm.rs index cc917e34d4f..297c5e74c51 100644 --- a/protocols/rendezvous/examples/registration_swarm.rs +++ b/protocols/rendezvous/examples/registration_swarm.rs @@ -1,29 +1,30 @@ use async_std::task; use futures::StreamExt; +use libp2p::core::muxing::StreamMuxerBox; +use libp2p::core::upgrade::{SelectUpgrade, Version}; +use libp2p::core::PeerId; +use libp2p::core::{identity, Transport}; +use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; +use libp2p::mplex::MplexConfig; +use libp2p::noise::{Keypair, X25519Spec}; +use libp2p::rendezvous; +use libp2p::rendezvous::Rendezvous; +use libp2p::swarm::Swarm; +use libp2p::swarm::SwarmEvent; +use libp2p::tcp::TcpConfig; +use libp2p::yamux::YamuxConfig; use libp2p::NetworkBehaviour; -use libp2p_core::muxing::StreamMuxerBox; -use libp2p_core::upgrade::{SelectUpgrade, Version}; -use libp2p_core::PeerId; -use libp2p_core::{identity, Transport}; -use libp2p_identify::{Identify, IdentifyConfig, IdentifyEvent}; -use libp2p_mplex::MplexConfig; -use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::{Event as RendezvousEvent, Rendezvous}; -use libp2p_swarm::Swarm; -use libp2p_swarm::SwarmEvent; -use libp2p_tcp::TcpConfig; -use libp2p_yamux::YamuxConfig; use std::str::FromStr; use std::time::Duration; #[derive(Debug)] enum MyEvent { - Rendezvous(RendezvousEvent), + Rendezvous(rendezvous::Event), Identify(IdentifyEvent), } -impl From for MyEvent { - fn from(event: RendezvousEvent) -> Self { +impl From for MyEvent { + fn from(event: rendezvous::Event) -> Self { MyEvent::Rendezvous(event) } } @@ -49,7 +50,7 @@ fn main() { let dh_keys = Keypair::::new() .into_authentic(&identity) .expect("failed to create dh_keys"); - let noise_config = libp2p_noise::NoiseConfig::xx(dh_keys).into_authenticated(); + let noise_config = libp2p::noise::NoiseConfig::xx(dh_keys).into_authenticated(); let tcp_config = TcpConfig::new(); let transport = tcp_config diff --git a/protocols/rendezvous/examples/rendezvous_swarm.rs b/protocols/rendezvous/examples/rendezvous_swarm.rs index caef74772fe..a2a79de615a 100644 --- a/protocols/rendezvous/examples/rendezvous_swarm.rs +++ b/protocols/rendezvous/examples/rendezvous_swarm.rs @@ -1,26 +1,27 @@ use futures::StreamExt; +use libp2p::core::muxing::StreamMuxerBox; +use libp2p::core::upgrade::{SelectUpgrade, Version}; +use libp2p::core::PeerId; +use libp2p::core::{identity, Transport}; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; +use libp2p::mplex::MplexConfig; +use libp2p::noise::{Keypair, X25519Spec}; +use libp2p::rendezvous; +use libp2p::rendezvous::Rendezvous; +use libp2p::swarm::Swarm; +use libp2p::tcp::TcpConfig; +use libp2p::yamux::YamuxConfig; use libp2p::NetworkBehaviour; -use libp2p_core::muxing::StreamMuxerBox; -use libp2p_core::upgrade::{SelectUpgrade, Version}; -use libp2p_core::PeerId; -use libp2p_core::{identity, Transport}; -use libp2p_mplex::MplexConfig; -use libp2p_noise::{Keypair, X25519Spec}; -use libp2p_rendezvous::{Event as RendezvousEvent, Rendezvous}; -use libp2p_swarm::Swarm; -use libp2p_tcp::TcpConfig; -use libp2p_yamux::YamuxConfig; use std::time::Duration; #[derive(Debug)] enum MyEvent { - Rendezvous(RendezvousEvent), + Rendezvous(rendezvous::Event), Identify(IdentifyEvent), } -impl From for MyEvent { - fn from(event: RendezvousEvent) -> Self { +impl From for MyEvent { + fn from(event: rendezvous::Event) -> Self { MyEvent::Rendezvous(event) } } @@ -50,7 +51,7 @@ async fn main() { let dh_keys = Keypair::::new() .into_authentic(&identity) .expect("failed to create dh_keys"); - let noise_config = libp2p_noise::NoiseConfig::xx(dh_keys).into_authenticated(); + let noise_config = libp2p::noise::NoiseConfig::xx(dh_keys).into_authenticated(); let tcp_config = TcpConfig::new(); let transport = tcp_config diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index e628daefb5e..ea40ef74099 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -3,15 +3,15 @@ use futures::stream::FusedStream; use futures::Future; use futures::StreamExt; use futures::{future, Stream}; -use libp2p_core::muxing::StreamMuxerBox; -use libp2p_core::transport::upgrade::Version; -use libp2p_core::transport::MemoryTransport; -use libp2p_core::upgrade::SelectUpgrade; -use libp2p_core::{identity, Executor, Multiaddr, PeerId, Transport}; -use libp2p_mplex::MplexConfig; -use libp2p_noise::{self, Keypair, NoiseConfig, X25519Spec}; -use libp2p_swarm::{AddressScore, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; -use libp2p_yamux::YamuxConfig; +use libp2p::core::muxing::StreamMuxerBox; +use libp2p::core::transport::upgrade::Version; +use libp2p::core::transport::MemoryTransport; +use libp2p::core::upgrade::SelectUpgrade; +use libp2p::core::{identity, Executor, Multiaddr, PeerId, Transport}; +use libp2p::mplex::MplexConfig; +use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; +use libp2p::swarm::{AddressScore, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; +use libp2p::yamux::YamuxConfig; use std::fmt::Debug; use std::pin::Pin; use std::time::Duration; From c25f554da893013e5c4fad284efceecd193922ec Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 13:57:52 +1000 Subject: [PATCH 180/242] Inline some variables to make examples easier to read --- .../rendezvous/examples/discovery_swarm.rs | 23 ++++++------ .../rendezvous/examples/registration_swarm.rs | 36 +++++++++---------- .../rendezvous/examples/rendezvous_swarm.rs | 8 ++--- 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discovery_swarm.rs index b38e0bcafca..ef5cab45aa3 100644 --- a/protocols/rendezvous/examples/discovery_swarm.rs +++ b/protocols/rendezvous/examples/discovery_swarm.rs @@ -5,7 +5,7 @@ use libp2p::core::upgrade::{SelectUpgrade, Version}; use libp2p::core::PeerId; use libp2p::core::{identity, Transport}; use libp2p::mplex::MplexConfig; -use libp2p::noise::{Keypair, X25519Spec}; +use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; use libp2p::rendezvous; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; @@ -17,17 +17,17 @@ use std::time::Duration; fn main() { let identity = identity::Keypair::generate_ed25519(); - let peer_id = PeerId::from(identity.public()); - let dh_keys = Keypair::::new() - .into_authentic(&identity) - .expect("failed to create dh_keys"); - let noise_config = libp2p::noise::NoiseConfig::xx(dh_keys).into_authenticated(); - - let tcp_config = TcpConfig::new(); - let transport = tcp_config + let transport = TcpConfig::new() .upgrade(Version::V1) - .authenticate(noise_config) + .authenticate( + NoiseConfig::xx( + Keypair::::new() + .into_authentic(&identity) + .expect("failed to create dh_keys"), + ) + .into_authenticated(), + ) .multiplex(SelectUpgrade::new( YamuxConfig::default(), MplexConfig::new(), @@ -36,7 +36,8 @@ fn main() { .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let mut swarm = Swarm::new(transport, Rendezvous::new(identity, 10000), peer_id); + let local_peer_id = PeerId::from(identity.public()); + let mut swarm = Swarm::new(transport, Rendezvous::new(identity, 10000), local_peer_id); let _ = swarm.dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()); diff --git a/protocols/rendezvous/examples/registration_swarm.rs b/protocols/rendezvous/examples/registration_swarm.rs index 297c5e74c51..3c3ca451be4 100644 --- a/protocols/rendezvous/examples/registration_swarm.rs +++ b/protocols/rendezvous/examples/registration_swarm.rs @@ -6,7 +6,7 @@ use libp2p::core::PeerId; use libp2p::core::{identity, Transport}; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p::mplex::MplexConfig; -use libp2p::noise::{Keypair, X25519Spec}; +use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; use libp2p::rendezvous; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; @@ -45,17 +45,17 @@ struct MyBehaviour { fn main() { let identity = identity::Keypair::generate_ed25519(); - let peer_id = PeerId::from(identity.public()); - let dh_keys = Keypair::::new() - .into_authentic(&identity) - .expect("failed to create dh_keys"); - let noise_config = libp2p::noise::NoiseConfig::xx(dh_keys).into_authenticated(); - - let tcp_config = TcpConfig::new(); - let transport = tcp_config + let transport = TcpConfig::new() .upgrade(Version::V1) - .authenticate(noise_config) + .authenticate( + NoiseConfig::xx( + Keypair::::new() + .into_authentic(&identity) + .expect("failed to create dh_keys"), + ) + .into_authenticated(), + ) .multiplex(SelectUpgrade::new( YamuxConfig::default(), MplexConfig::new(), @@ -64,19 +64,17 @@ fn main() { .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let identify = Identify::new(IdentifyConfig::new( - "rendezvous-example/1.0.0".to_string(), - identity.public(), - )); - let rendezvous = Rendezvous::new(identity, 10000); - + let local_peer_id = PeerId::from(identity.public()); let mut swarm = Swarm::new( transport, MyBehaviour { - identify, - rendezvous, + identify: Identify::new(IdentifyConfig::new( + "rendezvous-example/1.0.0".to_string(), + identity.public(), + )), + rendezvous: Rendezvous::new(identity, 10000), }, - peer_id, + local_peer_id, ); let _ = swarm.listen_on("/ip4/127.0.0.1/tcp/62343".parse().unwrap()); diff --git a/protocols/rendezvous/examples/rendezvous_swarm.rs b/protocols/rendezvous/examples/rendezvous_swarm.rs index a2a79de615a..7cdc061b9d2 100644 --- a/protocols/rendezvous/examples/rendezvous_swarm.rs +++ b/protocols/rendezvous/examples/rendezvous_swarm.rs @@ -5,7 +5,7 @@ use libp2p::core::PeerId; use libp2p::core::{identity, Transport}; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p::mplex::MplexConfig; -use libp2p::noise::{Keypair, X25519Spec}; +use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; use libp2p::rendezvous; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; @@ -51,12 +51,10 @@ async fn main() { let dh_keys = Keypair::::new() .into_authentic(&identity) .expect("failed to create dh_keys"); - let noise_config = libp2p::noise::NoiseConfig::xx(dh_keys).into_authenticated(); - let tcp_config = TcpConfig::new(); - let transport = tcp_config + let transport = TcpConfig::new() .upgrade(Version::V1) - .authenticate(noise_config) + .authenticate(NoiseConfig::xx(dh_keys).into_authenticated()) .multiplex(SelectUpgrade::new( YamuxConfig::default(), MplexConfig::new(), From 5d29f74e7bd1ddc6350f81fe712a08d1c1e4da37 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:01:15 +1000 Subject: [PATCH 181/242] Prefer async fn main over task::block_on Reduces levels of indentation and is easier to grasp in general. --- protocols/rendezvous/Cargo.toml | 2 +- .../rendezvous/examples/discovery_swarm.rs | 41 +++++++++---------- .../rendezvous/examples/registration_swarm.rs | 40 ++++++++---------- 3 files changed, 38 insertions(+), 45 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index fdc40b32e07..dfc8c4bf736 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -28,7 +28,7 @@ tokio = { version = "1", features = [ "time" ] } [dev-dependencies] libp2p = { path = "../.." } rand = "0.8" -async-std = "1.6.2" +async-std = { version = "1", features = ["attributes"] } env_logger = "0.8" async-trait = "0.1" tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } # TODO: REMOVE FOR PRODUCTION USE diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discovery_swarm.rs index ef5cab45aa3..c2896b29769 100644 --- a/protocols/rendezvous/examples/discovery_swarm.rs +++ b/protocols/rendezvous/examples/discovery_swarm.rs @@ -1,4 +1,3 @@ -use async_std::task; use futures::StreamExt; use libp2p::core::muxing::StreamMuxerBox; use libp2p::core::upgrade::{SelectUpgrade, Version}; @@ -15,7 +14,8 @@ use libp2p::yamux::YamuxConfig; use std::str::FromStr; use std::time::Duration; -fn main() { +#[async_std::main] +async fn main() { let identity = identity::Keypair::generate_ed25519(); let transport = TcpConfig::new() @@ -44,24 +44,21 @@ fn main() { let server_peer_id = PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); - task::block_on(async move { - loop { - let event = swarm.next().await; - if let Some(SwarmEvent::ConnectionEstablished { .. }) = event { - swarm.behaviour_mut().discover( - Some("rendezvous".to_string()), - None, - None, - server_peer_id, - ); - }; - if let Some(SwarmEvent::Behaviour(rendezvous::Event::Discovered { - registrations, - .. - })) = event - { - println!("discovered: {:?}", registrations.values()); - }; - } - }) + loop { + let event = swarm.next().await; + if let Some(SwarmEvent::ConnectionEstablished { .. }) = event { + swarm.behaviour_mut().discover( + Some("rendezvous".to_string()), + None, + None, + server_peer_id, + ); + }; + if let Some(SwarmEvent::Behaviour(rendezvous::Event::Discovered { + registrations, .. + })) = event + { + println!("discovered: {:?}", registrations.values()); + }; + } } diff --git a/protocols/rendezvous/examples/registration_swarm.rs b/protocols/rendezvous/examples/registration_swarm.rs index 3c3ca451be4..c29c333c0f4 100644 --- a/protocols/rendezvous/examples/registration_swarm.rs +++ b/protocols/rendezvous/examples/registration_swarm.rs @@ -1,4 +1,3 @@ -use async_std::task; use futures::StreamExt; use libp2p::core::muxing::StreamMuxerBox; use libp2p::core::upgrade::{SelectUpgrade, Version}; @@ -43,7 +42,8 @@ struct MyBehaviour { rendezvous: Rendezvous, } -fn main() { +#[async_std::main] +async fn main() { let identity = identity::Keypair::generate_ed25519(); let transport = TcpConfig::new() @@ -86,24 +86,20 @@ fn main() { let server_peer_id = PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); - task::block_on(async move { - loop { - let event = swarm.next().await; - match event { - Some(SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { - .. - }))) => { - swarm - .behaviour_mut() - .rendezvous - .register("rendezvous".to_string(), server_peer_id, None) - .unwrap(); - } - Some(SwarmEvent::Behaviour(MyEvent::Rendezvous(event))) => { - println!("registered event: {:?}", event); - } - _ => {} - }; - } - }) + loop { + let event = swarm.next().await; + match event { + Some(SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { .. }))) => { + swarm + .behaviour_mut() + .rendezvous + .register("rendezvous".to_string(), server_peer_id, None) + .unwrap(); + } + Some(SwarmEvent::Behaviour(MyEvent::Rendezvous(event))) => { + println!("registered event: {:?}", event); + } + _ => {} + }; + } } From 77ee0ad55a27b8f4f7e98e0428f008efb65a9131 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:07:21 +1000 Subject: [PATCH 182/242] Polish register example a bit --- .../rendezvous/examples/registration_swarm.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/protocols/rendezvous/examples/registration_swarm.rs b/protocols/rendezvous/examples/registration_swarm.rs index c29c333c0f4..64d29b11822 100644 --- a/protocols/rendezvous/examples/registration_swarm.rs +++ b/protocols/rendezvous/examples/registration_swarm.rs @@ -86,20 +86,27 @@ async fn main() { let server_peer_id = PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); - loop { - let event = swarm.next().await; + while let Some(event) = swarm.next().await { match event { - Some(SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { .. }))) => { + // once `/identify` did its job, we know our external address and can register + SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { .. })) => { swarm .behaviour_mut() .rendezvous .register("rendezvous".to_string(), server_peer_id, None) .unwrap(); } - Some(SwarmEvent::Behaviour(MyEvent::Rendezvous(event))) => { - println!("registered event: {:?}", event); + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Registered { + namespace, + ttl, + rendezvous_node, + })) => { + println!( + "Registered for namespace '{}' at rendezvous point {} for the next {} seconds", + namespace, rendezvous_node, ttl + ); } _ => {} - }; + } } } From 189dfde0905aa91f975baa3a89a82d64b00c783b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:07:49 +1000 Subject: [PATCH 183/242] Rename examples to what they are showcasing --- protocols/rendezvous/examples/{discovery_swarm.rs => discover.rs} | 0 .../rendezvous/examples/{registration_swarm.rs => register.rs} | 0 .../examples/{rendezvous_swarm.rs => rendezvous_point.rs} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename protocols/rendezvous/examples/{discovery_swarm.rs => discover.rs} (100%) rename protocols/rendezvous/examples/{registration_swarm.rs => register.rs} (100%) rename protocols/rendezvous/examples/{rendezvous_swarm.rs => rendezvous_point.rs} (100%) diff --git a/protocols/rendezvous/examples/discovery_swarm.rs b/protocols/rendezvous/examples/discover.rs similarity index 100% rename from protocols/rendezvous/examples/discovery_swarm.rs rename to protocols/rendezvous/examples/discover.rs diff --git a/protocols/rendezvous/examples/registration_swarm.rs b/protocols/rendezvous/examples/register.rs similarity index 100% rename from protocols/rendezvous/examples/registration_swarm.rs rename to protocols/rendezvous/examples/register.rs diff --git a/protocols/rendezvous/examples/rendezvous_swarm.rs b/protocols/rendezvous/examples/rendezvous_point.rs similarity index 100% rename from protocols/rendezvous/examples/rendezvous_swarm.rs rename to protocols/rendezvous/examples/rendezvous_point.rs From b873f238a97c1d7ff791ff2228f3fc5bbaa8ba86 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:23:30 +1000 Subject: [PATCH 184/242] Discover example dials discovered nodes --- protocols/rendezvous/examples/discover.rs | 68 +++++++++++++++++------ 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index c2896b29769..0b69bd0cff3 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -4,19 +4,23 @@ use libp2p::core::upgrade::{SelectUpgrade, Version}; use libp2p::core::PeerId; use libp2p::core::{identity, Transport}; use libp2p::mplex::MplexConfig; +use libp2p::multiaddr::Protocol; use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; -use libp2p::rendezvous; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; use libp2p::tcp::TcpConfig; use libp2p::yamux::YamuxConfig; +use libp2p::{rendezvous, Multiaddr}; use std::str::FromStr; use std::time::Duration; +const NAMESPACE: &'static str = "rendezvous"; + #[async_std::main] async fn main() { let identity = identity::Keypair::generate_ed25519(); + let rendezvous_point = "/ip4/127.0.0.1/tcp/62649".parse::().unwrap(); let transport = TcpConfig::new() .upgrade(Version::V1) @@ -39,26 +43,54 @@ async fn main() { let local_peer_id = PeerId::from(identity.public()); let mut swarm = Swarm::new(transport, Rendezvous::new(identity, 10000), local_peer_id); - let _ = swarm.dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()); + let _ = swarm.dial_addr(rendezvous_point.clone()); let server_peer_id = PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); - loop { - let event = swarm.next().await; - if let Some(SwarmEvent::ConnectionEstablished { .. }) = event { - swarm.behaviour_mut().discover( - Some("rendezvous".to_string()), - None, - None, - server_peer_id, - ); - }; - if let Some(SwarmEvent::Behaviour(rendezvous::Event::Discovered { - registrations, .. - })) = event - { - println!("discovered: {:?}", registrations.values()); - }; + while let Some(event) = swarm.next().await { + match event { + SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == server_peer_id => { + println!( + "Connected to rendezvous point, discovering nodes in `{}` namespace ...", + NAMESPACE + ); + + swarm.behaviour_mut().discover( + Some(NAMESPACE.to_string()), + None, + None, + server_peer_id, + ); + } + SwarmEvent::UnreachableAddr { error, address, .. } + | SwarmEvent::UnknownPeerUnreachableAddr { error, address, .. } + if address == rendezvous_point => + { + println!( + "Failed to connect to rendezvous point at {}: {}", + address, error + ); + return; + } + SwarmEvent::Behaviour(rendezvous::Event::Discovered { registrations, .. }) => { + for ((_, peer), registration) in registrations { + for address in registration.record.addresses() { + println!("Discovered peer {} at {}", peer, address); + + let p2p_suffix = Protocol::P2p(peer.as_ref().clone()); + let address_with_p2p = + if !address.ends_with(&Multiaddr::empty().with(p2p_suffix.clone())) { + address.clone().with(p2p_suffix) + } else { + address.clone() + }; + + swarm.dial_addr(address_with_p2p).unwrap() + } + } + } + _ => {} + } } } From 7a452aa43ee3c3f24915a932167935a333e7b843 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:30:57 +1000 Subject: [PATCH 185/242] Make discover and register share a common protocol The whole point of the rendezvous protocol is for two nodes with common protocols to find each other. We showcase this using the ping protocol that allows peers to measure the latency of their network connection. --- protocols/rendezvous/examples/discover.rs | 49 +++++++++++++++- protocols/rendezvous/examples/register.rs | 68 ++++++++++++++--------- 2 files changed, 88 insertions(+), 29 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 0b69bd0cff3..542870b8be2 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -6,6 +6,7 @@ use libp2p::core::{identity, Transport}; use libp2p::mplex::MplexConfig; use libp2p::multiaddr::Protocol; use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; +use libp2p::ping::{Ping, PingEvent, PingSuccess}; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; @@ -41,7 +42,14 @@ async fn main() { .boxed(); let local_peer_id = PeerId::from(identity.public()); - let mut swarm = Swarm::new(transport, Rendezvous::new(identity, 10000), local_peer_id); + let mut swarm = Swarm::new( + transport, + MyBehaviour { + rendezvous: Rendezvous::new(identity, 10000), + ping: Ping::default(), + }, + local_peer_id, + ); let _ = swarm.dial_addr(rendezvous_point.clone()); @@ -56,7 +64,7 @@ async fn main() { NAMESPACE ); - swarm.behaviour_mut().discover( + swarm.behaviour_mut().rendezvous.discover( Some(NAMESPACE.to_string()), None, None, @@ -73,7 +81,10 @@ async fn main() { ); return; } - SwarmEvent::Behaviour(rendezvous::Event::Discovered { registrations, .. }) => { + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Discovered { + registrations, + .. + })) => { for ((_, peer), registration) in registrations { for address in registration.record.addresses() { println!("Discovered peer {} at {}", peer, address); @@ -90,7 +101,39 @@ async fn main() { } } } + SwarmEvent::Behaviour(MyEvent::Ping(PingEvent { + peer, + result: Ok(PingSuccess::Ping { rtt }), + })) => { + println!("Ping to {} is {}ms", peer, rtt.as_millis()) + } _ => {} } } } + +#[derive(Debug)] +enum MyEvent { + Rendezvous(rendezvous::Event), + Ping(PingEvent), +} + +impl From for MyEvent { + fn from(event: rendezvous::Event) -> Self { + MyEvent::Rendezvous(event) + } +} + +impl From for MyEvent { + fn from(event: PingEvent) -> Self { + MyEvent::Ping(event) + } +} + +#[derive(libp2p::NetworkBehaviour)] +#[behaviour(event_process = false)] +#[behaviour(out_event = "MyEvent")] +struct MyBehaviour { + rendezvous: Rendezvous, + ping: Ping, +} diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 64d29b11822..59eaba8a6e2 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -6,6 +6,7 @@ use libp2p::core::{identity, Transport}; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p::mplex::MplexConfig; use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; +use libp2p::ping::{Ping, PingEvent, PingSuccess}; use libp2p::rendezvous; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; @@ -16,32 +17,6 @@ use libp2p::NetworkBehaviour; use std::str::FromStr; use std::time::Duration; -#[derive(Debug)] -enum MyEvent { - Rendezvous(rendezvous::Event), - Identify(IdentifyEvent), -} - -impl From for MyEvent { - fn from(event: rendezvous::Event) -> Self { - MyEvent::Rendezvous(event) - } -} - -impl From for MyEvent { - fn from(event: IdentifyEvent) -> Self { - MyEvent::Identify(event) - } -} - -#[derive(NetworkBehaviour)] -#[behaviour(event_process = false)] -#[behaviour(out_event = "MyEvent")] -struct MyBehaviour { - identify: Identify, - rendezvous: Rendezvous, -} - #[async_std::main] async fn main() { let identity = identity::Keypair::generate_ed25519(); @@ -73,6 +48,7 @@ async fn main() { identity.public(), )), rendezvous: Rendezvous::new(identity, 10000), + ping: Ping::default(), }, local_peer_id, ); @@ -106,7 +82,47 @@ async fn main() { namespace, rendezvous_node, ttl ); } + SwarmEvent::Behaviour(MyEvent::Ping(PingEvent { + peer, + result: Ok(PingSuccess::Ping { rtt }), + })) => { + println!("Ping to {} is {}ms", peer, rtt.as_millis()) + } _ => {} } } } + +#[derive(Debug)] +enum MyEvent { + Rendezvous(rendezvous::Event), + Identify(IdentifyEvent), + Ping(PingEvent), +} + +impl From for MyEvent { + fn from(event: rendezvous::Event) -> Self { + MyEvent::Rendezvous(event) + } +} + +impl From for MyEvent { + fn from(event: IdentifyEvent) -> Self { + MyEvent::Identify(event) + } +} + +impl From for MyEvent { + fn from(event: PingEvent) -> Self { + MyEvent::Ping(event) + } +} + +#[derive(NetworkBehaviour)] +#[behaviour(event_process = false)] +#[behaviour(out_event = "MyEvent")] +struct MyBehaviour { + identify: Identify, + rendezvous: Rendezvous, + ping: Ping, +} From 5d8f42f046accf3e52c9c7f1525c60198f52e036 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:32:37 +1000 Subject: [PATCH 186/242] Make register example listen on a random port This showcases better that we automatically discover our external address. --- protocols/rendezvous/examples/register.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 59eaba8a6e2..2de0bf281fd 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -53,7 +53,7 @@ async fn main() { local_peer_id, ); - let _ = swarm.listen_on("/ip4/127.0.0.1/tcp/62343".parse().unwrap()); + let _ = swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()); swarm .dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()) @@ -64,6 +64,9 @@ async fn main() { while let Some(event) = swarm.next().await { match event { + SwarmEvent::NewListenAddr(addr) => { + println!("Listening on {}", addr); + } // once `/identify` did its job, we know our external address and can register SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { .. })) => { swarm From ec79ba649004205b1d15b433ead009ce7a19dc38 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:34:18 +1000 Subject: [PATCH 187/242] Rename server to rendezvous point --- protocols/rendezvous/examples/register.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 2de0bf281fd..3f6caa4a841 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -19,6 +19,9 @@ use std::time::Duration; #[async_std::main] async fn main() { + let rendezvous_point = + PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); + let identity = identity::Keypair::generate_ed25519(); let transport = TcpConfig::new() @@ -59,9 +62,6 @@ async fn main() { .dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()) .unwrap(); - let server_peer_id = - PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); - while let Some(event) = swarm.next().await { match event { SwarmEvent::NewListenAddr(addr) => { @@ -72,7 +72,7 @@ async fn main() { swarm .behaviour_mut() .rendezvous - .register("rendezvous".to_string(), server_peer_id, None) + .register("rendezvous".to_string(), rendezvous_point, None) .unwrap(); } SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Registered { From a3270caca4362469c22c3d91252cae04ddf36d2f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:36:27 +1000 Subject: [PATCH 188/242] Polish rendezvous point example --- .../rendezvous/examples/rendezvous_point.rs | 87 +++++++++---------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/protocols/rendezvous/examples/rendezvous_point.rs b/protocols/rendezvous/examples/rendezvous_point.rs index 7cdc061b9d2..c11d3b12c6c 100644 --- a/protocols/rendezvous/examples/rendezvous_point.rs +++ b/protocols/rendezvous/examples/rendezvous_point.rs @@ -14,47 +14,22 @@ use libp2p::yamux::YamuxConfig; use libp2p::NetworkBehaviour; use std::time::Duration; -#[derive(Debug)] -enum MyEvent { - Rendezvous(rendezvous::Event), - Identify(IdentifyEvent), -} - -impl From for MyEvent { - fn from(event: rendezvous::Event) -> Self { - MyEvent::Rendezvous(event) - } -} - -impl From for MyEvent { - fn from(event: IdentifyEvent) -> Self { - MyEvent::Identify(event) - } -} - -#[derive(NetworkBehaviour)] -#[behaviour(event_process = false)] -#[behaviour(out_event = "MyEvent")] -struct MyBehaviour { - identify: Identify, - rendezvous: Rendezvous, -} - #[tokio::main] async fn main() { let bytes = [0u8; 32]; let key = identity::ed25519::SecretKey::from_bytes(bytes).expect("we always pass 32 bytes"); let identity = identity::Keypair::Ed25519(key.into()); - let peer_id = PeerId::from(identity.public()); - - let dh_keys = Keypair::::new() - .into_authentic(&identity) - .expect("failed to create dh_keys"); - let transport = TcpConfig::new() .upgrade(Version::V1) - .authenticate(NoiseConfig::xx(dh_keys).into_authenticated()) + .authenticate( + NoiseConfig::xx( + Keypair::::new() + .into_authentic(&identity) + .expect("failed to create dh_keys"), + ) + .into_authenticated(), + ) .multiplex(SelectUpgrade::new( YamuxConfig::default(), MplexConfig::new(), @@ -63,19 +38,18 @@ async fn main() { .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); - let identify = Identify::new(IdentifyConfig::new( - "rendezvous-example/1.0.0".to_string(), - identity.public(), - )); - let rendezvous = Rendezvous::new(identity, 10000); + let local_peer_id = PeerId::from(identity.public()); let mut swarm = Swarm::new( transport, MyBehaviour { - identify, - rendezvous, + identify: Identify::new(IdentifyConfig::new( + "rendezvous-example/1.0.0".to_string(), + identity.public(), + )), + rendezvous: Rendezvous::new(identity, 10000), }, - peer_id, + local_peer_id, ); println!("peer id: {}", swarm.local_peer_id()); @@ -84,8 +58,33 @@ async fn main() { .listen_on("/ip4/0.0.0.0/tcp/62649".parse().unwrap()) .unwrap(); - loop { - let event = swarm.next().await; - println!("swarm event: {:?}", event); + while let Some(event) = swarm.next().await { + println!("{:?}", event); + } +} + +#[derive(Debug)] +enum MyEvent { + Rendezvous(rendezvous::Event), + Identify(IdentifyEvent), +} + +impl From for MyEvent { + fn from(event: rendezvous::Event) -> Self { + MyEvent::Rendezvous(event) } } + +impl From for MyEvent { + fn from(event: IdentifyEvent) -> Self { + MyEvent::Identify(event) + } +} + +#[derive(NetworkBehaviour)] +#[behaviour(event_process = false)] +#[behaviour(out_event = "MyEvent")] +struct MyBehaviour { + identify: Identify, + rendezvous: Rendezvous, +} From 0fdb340d4438c4ca662eb06617f296a80c8ebbcd Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:41:46 +1000 Subject: [PATCH 189/242] Use libp2p::development_transport for examples --- protocols/rendezvous/examples/discover.rs | 36 +++--------------- protocols/rendezvous/examples/register.rs | 36 +++--------------- .../rendezvous/examples/rendezvous_point.rs | 37 +++---------------- 3 files changed, 15 insertions(+), 94 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 542870b8be2..31fb23159e8 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -1,20 +1,13 @@ use futures::StreamExt; -use libp2p::core::muxing::StreamMuxerBox; -use libp2p::core::upgrade::{SelectUpgrade, Version}; +use libp2p::core::identity; use libp2p::core::PeerId; -use libp2p::core::{identity, Transport}; -use libp2p::mplex::MplexConfig; use libp2p::multiaddr::Protocol; -use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; use libp2p::ping::{Ping, PingEvent, PingSuccess}; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; -use libp2p::tcp::TcpConfig; -use libp2p::yamux::YamuxConfig; -use libp2p::{rendezvous, Multiaddr}; +use libp2p::{development_transport, rendezvous, Multiaddr}; use std::str::FromStr; -use std::time::Duration; const NAMESPACE: &'static str = "rendezvous"; @@ -23,32 +16,13 @@ async fn main() { let identity = identity::Keypair::generate_ed25519(); let rendezvous_point = "/ip4/127.0.0.1/tcp/62649".parse::().unwrap(); - let transport = TcpConfig::new() - .upgrade(Version::V1) - .authenticate( - NoiseConfig::xx( - Keypair::::new() - .into_authentic(&identity) - .expect("failed to create dh_keys"), - ) - .into_authenticated(), - ) - .multiplex(SelectUpgrade::new( - YamuxConfig::default(), - MplexConfig::new(), - )) - .timeout(Duration::from_secs(20)) - .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) - .boxed(); - - let local_peer_id = PeerId::from(identity.public()); let mut swarm = Swarm::new( - transport, + development_transport(identity.clone()).await.unwrap(), MyBehaviour { - rendezvous: Rendezvous::new(identity, 10000), + rendezvous: Rendezvous::new(identity.clone(), 10000), ping: Ping::default(), }, - local_peer_id, + PeerId::from(identity.public()), ); let _ = swarm.dial_addr(rendezvous_point.clone()); diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 3f6caa4a841..a8c03ef2df0 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -1,21 +1,14 @@ use futures::StreamExt; -use libp2p::core::muxing::StreamMuxerBox; -use libp2p::core::upgrade::{SelectUpgrade, Version}; +use libp2p::core::identity; use libp2p::core::PeerId; -use libp2p::core::{identity, Transport}; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; -use libp2p::mplex::MplexConfig; -use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; use libp2p::ping::{Ping, PingEvent, PingSuccess}; -use libp2p::rendezvous; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; -use libp2p::tcp::TcpConfig; -use libp2p::yamux::YamuxConfig; use libp2p::NetworkBehaviour; +use libp2p::{development_transport, rendezvous}; use std::str::FromStr; -use std::time::Duration; #[async_std::main] async fn main() { @@ -24,36 +17,17 @@ async fn main() { let identity = identity::Keypair::generate_ed25519(); - let transport = TcpConfig::new() - .upgrade(Version::V1) - .authenticate( - NoiseConfig::xx( - Keypair::::new() - .into_authentic(&identity) - .expect("failed to create dh_keys"), - ) - .into_authenticated(), - ) - .multiplex(SelectUpgrade::new( - YamuxConfig::default(), - MplexConfig::new(), - )) - .timeout(Duration::from_secs(20)) - .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) - .boxed(); - - let local_peer_id = PeerId::from(identity.public()); let mut swarm = Swarm::new( - transport, + development_transport(identity.clone()).await.unwrap(), MyBehaviour { identify: Identify::new(IdentifyConfig::new( "rendezvous-example/1.0.0".to_string(), identity.public(), )), - rendezvous: Rendezvous::new(identity, 10000), + rendezvous: Rendezvous::new(identity.clone(), 10000), ping: Ping::default(), }, - local_peer_id, + PeerId::from(identity.public()), ); let _ = swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()); diff --git a/protocols/rendezvous/examples/rendezvous_point.rs b/protocols/rendezvous/examples/rendezvous_point.rs index c11d3b12c6c..56afc306da9 100644 --- a/protocols/rendezvous/examples/rendezvous_point.rs +++ b/protocols/rendezvous/examples/rendezvous_point.rs @@ -1,18 +1,11 @@ use futures::StreamExt; -use libp2p::core::muxing::StreamMuxerBox; -use libp2p::core::upgrade::{SelectUpgrade, Version}; +use libp2p::core::identity; use libp2p::core::PeerId; -use libp2p::core::{identity, Transport}; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; -use libp2p::mplex::MplexConfig; -use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; -use libp2p::rendezvous; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; -use libp2p::tcp::TcpConfig; -use libp2p::yamux::YamuxConfig; use libp2p::NetworkBehaviour; -use std::time::Duration; +use libp2p::{development_transport, rendezvous}; #[tokio::main] async fn main() { @@ -20,36 +13,16 @@ async fn main() { let key = identity::ed25519::SecretKey::from_bytes(bytes).expect("we always pass 32 bytes"); let identity = identity::Keypair::Ed25519(key.into()); - let transport = TcpConfig::new() - .upgrade(Version::V1) - .authenticate( - NoiseConfig::xx( - Keypair::::new() - .into_authentic(&identity) - .expect("failed to create dh_keys"), - ) - .into_authenticated(), - ) - .multiplex(SelectUpgrade::new( - YamuxConfig::default(), - MplexConfig::new(), - )) - .timeout(Duration::from_secs(20)) - .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) - .boxed(); - - let local_peer_id = PeerId::from(identity.public()); - let mut swarm = Swarm::new( - transport, + development_transport(identity.clone()).await.unwrap(), MyBehaviour { identify: Identify::new(IdentifyConfig::new( "rendezvous-example/1.0.0".to_string(), identity.public(), )), - rendezvous: Rendezvous::new(identity, 10000), + rendezvous: Rendezvous::new(identity.clone(), 10000), }, - local_peer_id, + PeerId::from(identity.public()), ); println!("peer id: {}", swarm.local_peer_id()); From dae0f6e133ad30bc0cdb678d3e34b61e53483c16 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 14:54:52 +1000 Subject: [PATCH 190/242] Add Ping behaviour to rendezvous point Otherwise the connection is being closed automatically. We don't want the logs to be spammed by pings to the rendezvous point though because the examples are here to showcase two nodes connecting through the rendezvous node. --- protocols/rendezvous/examples/discover.rs | 21 ++++----- protocols/rendezvous/examples/register.rs | 44 +++++++++++++------ .../rendezvous/examples/rendezvous_point.rs | 16 ++++++- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 31fb23159e8..83500fb2965 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -7,14 +7,18 @@ use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; use libp2p::{development_transport, rendezvous, Multiaddr}; -use std::str::FromStr; const NAMESPACE: &'static str = "rendezvous"; #[async_std::main] async fn main() { + env_logger::init(); + let identity = identity::Keypair::generate_ed25519(); - let rendezvous_point = "/ip4/127.0.0.1/tcp/62649".parse::().unwrap(); + let rendezvous_point_address = "/ip4/127.0.0.1/tcp/62649".parse::().unwrap(); + let rendezvous_point = "12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN" + .parse() + .unwrap(); let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), @@ -25,14 +29,11 @@ async fn main() { PeerId::from(identity.public()), ); - let _ = swarm.dial_addr(rendezvous_point.clone()); - - let server_peer_id = - PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); + let _ = swarm.dial_addr(rendezvous_point_address.clone()); while let Some(event) = swarm.next().await { match event { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == server_peer_id => { + SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == rendezvous_point => { println!( "Connected to rendezvous point, discovering nodes in `{}` namespace ...", NAMESPACE @@ -42,12 +43,12 @@ async fn main() { Some(NAMESPACE.to_string()), None, None, - server_peer_id, + rendezvous_point, ); } SwarmEvent::UnreachableAddr { error, address, .. } | SwarmEvent::UnknownPeerUnreachableAddr { error, address, .. } - if address == rendezvous_point => + if address == rendezvous_point_address => { println!( "Failed to connect to rendezvous point at {}: {}", @@ -78,7 +79,7 @@ async fn main() { SwarmEvent::Behaviour(MyEvent::Ping(PingEvent { peer, result: Ok(PingSuccess::Ping { rtt }), - })) => { + })) if peer != rendezvous_point => { println!("Ping to {} is {}ms", peer, rtt.as_millis()) } _ => {} diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index a8c03ef2df0..38712632cad 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -6,14 +6,17 @@ use libp2p::ping::{Ping, PingEvent, PingSuccess}; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; -use libp2p::NetworkBehaviour; use libp2p::{development_transport, rendezvous}; -use std::str::FromStr; +use libp2p::{Multiaddr, NetworkBehaviour}; #[async_std::main] async fn main() { - let rendezvous_point = - PeerId::from_str("12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN").unwrap(); + env_logger::init(); + + let rendezvous_point_address = "/ip4/127.0.0.1/tcp/62649".parse::().unwrap(); + let rendezvous_point = "12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN" + .parse() + .unwrap(); let identity = identity::Keypair::generate_ed25519(); @@ -32,14 +35,19 @@ async fn main() { let _ = swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()); - swarm - .dial_addr("/ip4/127.0.0.1/tcp/62649".parse().unwrap()) - .unwrap(); + swarm.dial_addr(rendezvous_point_address).unwrap(); while let Some(event) = swarm.next().await { match event { SwarmEvent::NewListenAddr(addr) => { - println!("Listening on {}", addr); + log::info!("Listening on {}", addr); + } + SwarmEvent::ConnectionClosed { + peer_id, + cause: Some(error), + .. + } if peer_id == rendezvous_point => { + log::error!("Lost connection to rendezvous point {}", error); } // once `/identify` did its job, we know our external address and can register SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { .. })) => { @@ -54,18 +62,28 @@ async fn main() { ttl, rendezvous_node, })) => { - println!( + log::info!( "Registered for namespace '{}' at rendezvous point {} for the next {} seconds", - namespace, rendezvous_node, ttl + namespace, + rendezvous_node, + ttl ); } + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::RegisterFailed( + error, + ))) => { + log::error!("Failed to register {}", error); + return; + } SwarmEvent::Behaviour(MyEvent::Ping(PingEvent { peer, result: Ok(PingSuccess::Ping { rtt }), - })) => { - println!("Ping to {} is {}ms", peer, rtt.as_millis()) + })) if peer != rendezvous_point => { + log::info!("Ping to {} is {}ms", peer, rtt.as_millis()) + } + other => { + log::warn!("Unhandled event {:?}", other) } - _ => {} } } } diff --git a/protocols/rendezvous/examples/rendezvous_point.rs b/protocols/rendezvous/examples/rendezvous_point.rs index 56afc306da9..0ff6afc6ef2 100644 --- a/protocols/rendezvous/examples/rendezvous_point.rs +++ b/protocols/rendezvous/examples/rendezvous_point.rs @@ -2,6 +2,7 @@ use futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; +use libp2p::ping::{Ping, PingEvent}; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::NetworkBehaviour; @@ -9,6 +10,8 @@ use libp2p::{development_transport, rendezvous}; #[tokio::main] async fn main() { + env_logger::init(); + let bytes = [0u8; 32]; let key = identity::ed25519::SecretKey::from_bytes(bytes).expect("we always pass 32 bytes"); let identity = identity::Keypair::Ed25519(key.into()); @@ -21,18 +24,19 @@ async fn main() { identity.public(), )), rendezvous: Rendezvous::new(identity.clone(), 10000), + ping: Ping::default(), }, PeerId::from(identity.public()), ); - println!("peer id: {}", swarm.local_peer_id()); + log::info!("Local peer id: {}", swarm.local_peer_id()); swarm .listen_on("/ip4/0.0.0.0/tcp/62649".parse().unwrap()) .unwrap(); while let Some(event) = swarm.next().await { - println!("{:?}", event); + log::info!("{:?}", event); } } @@ -40,6 +44,7 @@ async fn main() { enum MyEvent { Rendezvous(rendezvous::Event), Identify(IdentifyEvent), + Ping(PingEvent), } impl From for MyEvent { @@ -54,10 +59,17 @@ impl From for MyEvent { } } +impl From for MyEvent { + fn from(event: PingEvent) -> Self { + MyEvent::Ping(event) + } +} + #[derive(NetworkBehaviour)] #[behaviour(event_process = false)] #[behaviour(out_event = "MyEvent")] struct MyBehaviour { identify: Identify, rendezvous: Rendezvous, + ping: Ping, } From 7776b37cbb25574451f6bccbec0edb9ff5f904c3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 15:13:50 +1000 Subject: [PATCH 191/242] Make logging prettier --- protocols/rendezvous/examples/discover.rs | 17 ++++++---- protocols/rendezvous/examples/register.rs | 2 +- .../rendezvous/examples/rendezvous_point.rs | 34 +++++++++++++++++-- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 83500fb2965..105eb6dd814 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -34,8 +34,8 @@ async fn main() { while let Some(event) = swarm.next().await { match event { SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == rendezvous_point => { - println!( - "Connected to rendezvous point, discovering nodes in `{}` namespace ...", + log::info!( + "Connected to rendezvous point, discovering nodes in '{}' namespace ...", NAMESPACE ); @@ -50,9 +50,10 @@ async fn main() { | SwarmEvent::UnknownPeerUnreachableAddr { error, address, .. } if address == rendezvous_point_address => { - println!( + log::error!( "Failed to connect to rendezvous point at {}: {}", - address, error + address, + error ); return; } @@ -62,7 +63,7 @@ async fn main() { })) => { for ((_, peer), registration) in registrations { for address in registration.record.addresses() { - println!("Discovered peer {} at {}", peer, address); + log::info!("Discovered peer {} at {}", peer, address); let p2p_suffix = Protocol::P2p(peer.as_ref().clone()); let address_with_p2p = @@ -80,9 +81,11 @@ async fn main() { peer, result: Ok(PingSuccess::Ping { rtt }), })) if peer != rendezvous_point => { - println!("Ping to {} is {}ms", peer, rtt.as_millis()) + log::info!("Ping to {} is {}ms", peer, rtt.as_millis()) + } + other => { + log::debug!("Unhandled {:?}", other); } - _ => {} } } } diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 38712632cad..de0c62f9dfd 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -82,7 +82,7 @@ async fn main() { log::info!("Ping to {} is {}ms", peer, rtt.as_millis()) } other => { - log::warn!("Unhandled event {:?}", other) + log::debug!("Unhandled {:?}", other); } } } diff --git a/protocols/rendezvous/examples/rendezvous_point.rs b/protocols/rendezvous/examples/rendezvous_point.rs index 0ff6afc6ef2..d0f371647db 100644 --- a/protocols/rendezvous/examples/rendezvous_point.rs +++ b/protocols/rendezvous/examples/rendezvous_point.rs @@ -4,7 +4,7 @@ use libp2p::core::PeerId; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p::ping::{Ping, PingEvent}; use libp2p::rendezvous::Rendezvous; -use libp2p::swarm::Swarm; +use libp2p::swarm::{Swarm, SwarmEvent}; use libp2p::NetworkBehaviour; use libp2p::{development_transport, rendezvous}; @@ -36,7 +36,37 @@ async fn main() { .unwrap(); while let Some(event) = swarm.next().await { - log::info!("{:?}", event); + match event { + SwarmEvent::ConnectionEstablished { peer_id, .. } => { + log::info!("Connected to {}", peer_id); + } + SwarmEvent::ConnectionClosed { peer_id, .. } => { + log::info!("Disconnected from {}", peer_id); + } + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::PeerRegistered { + peer, + registration, + })) => { + log::info!( + "Peer {} registered for namespace '{}'", + peer, + registration.namespace + ); + } + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::DiscoverServed { + enquirer, + registrations, + })) => { + log::info!( + "Served peer {} with {} registrations", + enquirer, + registrations.len() + ); + } + other => { + log::debug!("Unhandled {:?}", other); + } + } } } From f1b763a5a46dffb4189d06c631488b60646cfa02 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 15:21:06 +1000 Subject: [PATCH 192/242] Have pings be every second for example's sake --- protocols/rendezvous/examples/discover.rs | 7 +++++-- protocols/rendezvous/examples/register.rs | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 105eb6dd814..1423f2567be 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -2,11 +2,12 @@ use futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; use libp2p::multiaddr::Protocol; -use libp2p::ping::{Ping, PingEvent, PingSuccess}; +use libp2p::ping::{Ping, PingConfig, PingEvent, PingSuccess}; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; use libp2p::{development_transport, rendezvous, Multiaddr}; +use std::time::Duration; const NAMESPACE: &'static str = "rendezvous"; @@ -24,11 +25,13 @@ async fn main() { development_transport(identity.clone()).await.unwrap(), MyBehaviour { rendezvous: Rendezvous::new(identity.clone(), 10000), - ping: Ping::default(), + ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), ); + log::info!("Local peer id: {}", swarm.local_peer_id()); + let _ = swarm.dial_addr(rendezvous_point_address.clone()); while let Some(event) = swarm.next().await { diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index de0c62f9dfd..302f60963e9 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -2,12 +2,13 @@ use futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; -use libp2p::ping::{Ping, PingEvent, PingSuccess}; +use libp2p::ping::{Ping, PingConfig, PingEvent, PingSuccess}; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; use libp2p::{development_transport, rendezvous}; use libp2p::{Multiaddr, NetworkBehaviour}; +use std::time::Duration; #[async_std::main] async fn main() { @@ -28,11 +29,13 @@ async fn main() { identity.public(), )), rendezvous: Rendezvous::new(identity.clone(), 10000), - ping: Ping::default(), + ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), ); + log::info!("Local peer id: {}", swarm.local_peer_id()); + let _ = swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()); swarm.dial_addr(rendezvous_point_address).unwrap(); From 6d14c5b8ea49e0790af504217a9e8ebec552b5b1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 15:25:53 +1000 Subject: [PATCH 193/242] Remove timestamp that is not used for anything --- protocols/rendezvous/src/behaviour.rs | 16 +++++----------- protocols/rendezvous/src/codec.rs | 3 --- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 64bef36de38..7b0de722afb 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -18,7 +18,7 @@ use std::collections::{HashMap, HashSet, VecDeque}; use std::iter::FromIterator; use std::option::Option::None; use std::task::{Context, Poll}; -use std::time::{Duration, SystemTime}; +use std::time::Duration; use tokio::time::sleep; use uuid::Uuid; @@ -171,7 +171,7 @@ impl NetworkBehaviour for Rendezvous { let record = registration.record.clone(); let events = match self.registrations.add(registration) { - Ok((effective_ttl, timestamp)) => { + Ok(effective_ttl) => { vec![ NetworkBehaviourAction::NotifyHandler { peer_id, @@ -184,7 +184,6 @@ impl NetworkBehaviour for Rendezvous { namespace, record, ttl: effective_ttl, - timestamp, }, }), ] @@ -378,10 +377,7 @@ impl Registrations { } } - pub fn add( - &mut self, - new_registration: NewRegistration, - ) -> Result<(i64, SystemTime), TtlTooLong> { + pub fn add(&mut self, new_registration: NewRegistration) -> Result { let ttl = new_registration.effective_ttl(); if ttl > self.ttl_upper_bound { return Err(TtlTooLong { @@ -405,15 +401,12 @@ impl Registrations { registration_id, ); - let timestamp = SystemTime::now(); - self.registrations.insert( registration_id, Registration { namespace, record: new_registration.record, ttl, - timestamp, }, ); @@ -423,7 +416,7 @@ impl Registrations { self.next_expiry.push(next_expiry); - Ok((ttl, timestamp)) + Ok(ttl) } pub fn remove(&mut self, namespace: String, peer_id: PeerId) { @@ -537,6 +530,7 @@ mod tests { use super::*; use libp2p_core::identity; use std::option::Option::None; + use std::time::SystemTime; #[tokio::test] async fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 91e2e4cb180..55705ec5d3f 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -1,7 +1,6 @@ use asynchronous_codec::{Bytes, BytesMut, Decoder, Encoder}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; use std::convert::{TryFrom, TryInto}; -use std::time::SystemTime; use unsigned_varint::codec::UviBytes; use uuid::Uuid; @@ -119,7 +118,6 @@ pub struct Registration { pub namespace: String, pub record: PeerRecord, pub ttl: i64, - pub timestamp: SystemTime, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -378,7 +376,6 @@ impl TryFrom for Message { )?, )?, ttl: reggo.ttl.ok_or(ConversionError::MissingTtl)?, - timestamp: SystemTime::now(), }) }) .collect::, ConversionError>>()?; From b2a0c6fa00253ea36394ee69b721a3982aa0f25b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 15:29:57 +1000 Subject: [PATCH 194/242] Return full registration from `Registrations` --- protocols/rendezvous/src/behaviour.rs | 32 ++++++++++++--------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 7b0de722afb..a6976bd1d71 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -168,23 +168,20 @@ impl NetworkBehaviour for Rendezvous { match event { OutEvent::RegistrationRequested(registration) => { let namespace = registration.namespace.clone(); - let record = registration.record.clone(); let events = match self.registrations.add(registration) { - Ok(effective_ttl) => { + Ok(registration) => { vec![ NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: InEvent::RegisterResponse { ttl: effective_ttl }, + event: InEvent::RegisterResponse { + ttl: registration.ttl, + }, }, NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { peer: peer_id, - registration: Registration { - namespace, - record, - ttl: effective_ttl, - }, + registration, }), ] } @@ -377,7 +374,7 @@ impl Registrations { } } - pub fn add(&mut self, new_registration: NewRegistration) -> Result { + pub fn add(&mut self, new_registration: NewRegistration) -> Result { let ttl = new_registration.effective_ttl(); if ttl > self.ttl_upper_bound { return Err(TtlTooLong { @@ -401,14 +398,13 @@ impl Registrations { registration_id, ); - self.registrations.insert( - registration_id, - Registration { - namespace, - record: new_registration.record, - ttl, - }, - ); + let registration = Registration { + namespace, + record: new_registration.record, + ttl, + }; + self.registrations + .insert(registration_id, registration.clone()); let next_expiry = sleep(Duration::from_secs(ttl as u64)) .map(move |()| registration_id) @@ -416,7 +412,7 @@ impl Registrations { self.next_expiry.push(next_expiry); - Ok(ttl) + Ok(registration) } pub fn remove(&mut self, namespace: String, peer_id: PeerId) { From b34b099986f8c7aef88ac889cb08441301fa4df7 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 15:31:43 +1000 Subject: [PATCH 195/242] Be quiet by default --- protocols/rendezvous/src/behaviour.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index a6976bd1d71..c718c7aafd8 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -13,7 +13,6 @@ use libp2p_core::{Multiaddr, PeerId, PeerRecord}; use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; -use log::debug; use std::collections::{HashMap, HashSet, VecDeque}; use std::iter::FromIterator; use std::option::Option::None; @@ -149,15 +148,9 @@ impl NetworkBehaviour for Rendezvous { Vec::new() } - fn inject_connected(&mut self, peer_id: &PeerId) { - debug!("New peer connected: {}", peer_id); - // Dont need to do anything here? - } + fn inject_connected(&mut self, _: &PeerId) {} - fn inject_disconnected(&mut self, peer_id: &PeerId) { - debug!("Peer disconnected: {}", peer_id); - // Don't need to do anything? - } + fn inject_disconnected(&mut self, _: &PeerId) {} fn inject_event( &mut self, From 798d5ab38f56df2e24393dacbf288f01aec930a7 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 15:39:18 +1000 Subject: [PATCH 196/242] Rename swarms in test-setup to Alice, Bob and Robert --- protocols/rendezvous/tests/rendezvous.rs | 113 +++++++++-------------- 1 file changed, 46 insertions(+), 67 deletions(-) diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 1e4bdd907e9..e6068208b1d 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -13,28 +13,23 @@ async fn given_successful_registration_then_successful_discovery() { let namespace = "some-namespace".to_string(); - let _ = test.registration_swarm.behaviour_mut().register( - namespace.clone(), - *test.rendezvous_swarm.local_peer_id(), - None, - ); + let _ = + test.alice + .behaviour_mut() + .register(namespace.clone(), *test.robert.local_peer_id(), None); test.assert_successful_registration(namespace.clone(), DEFAULT_TTL) .await; - test.discovery_swarm.behaviour_mut().discover( + test.bob.behaviour_mut().discover( Some(namespace.clone()), None, None, - *test.rendezvous_swarm.local_peer_id(), + *test.robert.local_peer_id(), ); - test.assert_successful_discovery( - namespace.clone(), - DEFAULT_TTL, - *test.registration_swarm.local_peer_id(), - ) - .await; + test.assert_successful_discovery(namespace.clone(), DEFAULT_TTL, *test.alice.local_peer_id()) + .await; } #[tokio::test] @@ -46,51 +41,42 @@ async fn given_successful_registration_then_refresh_ttl() { let refesh_ttl = 10_000; - let _ = test.registration_swarm.behaviour_mut().register( - namespace.clone(), - *test.rendezvous_swarm.local_peer_id(), - None, - ); + let _ = + test.alice + .behaviour_mut() + .register(namespace.clone(), *test.robert.local_peer_id(), None); test.assert_successful_registration(namespace.clone(), DEFAULT_TTL) .await; - test.discovery_swarm.behaviour_mut().discover( + test.bob.behaviour_mut().discover( Some(namespace.clone()), None, None, - *test.rendezvous_swarm.local_peer_id(), + *test.robert.local_peer_id(), ); - test.assert_successful_discovery( - namespace.clone(), - DEFAULT_TTL, - *test.registration_swarm.local_peer_id(), - ) - .await; + test.assert_successful_discovery(namespace.clone(), DEFAULT_TTL, *test.alice.local_peer_id()) + .await; - let _ = test.registration_swarm.behaviour_mut().register( + let _ = test.alice.behaviour_mut().register( namespace.clone(), - *test.rendezvous_swarm.local_peer_id(), + *test.robert.local_peer_id(), Some(refesh_ttl), ); test.assert_successful_registration(namespace.clone(), refesh_ttl) .await; - test.discovery_swarm.behaviour_mut().discover( + test.bob.behaviour_mut().discover( Some(namespace.clone()), None, None, - *test.rendezvous_swarm.local_peer_id(), + *test.robert.local_peer_id(), ); - test.assert_successful_discovery( - namespace.clone(), - refesh_ttl, - *test.registration_swarm.local_peer_id(), - ) - .await; + test.assert_successful_discovery(namespace.clone(), refesh_ttl, *test.alice.local_peer_id()) + .await; } #[tokio::test] @@ -100,13 +86,13 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { let namespace = "some-namespace".to_string(); - let _ = test.registration_swarm.behaviour_mut().register( + let _ = test.alice.behaviour_mut().register( namespace.clone(), - *test.rendezvous_swarm.local_peer_id(), + *test.robert.local_peer_id(), Some(100_000), ); - match await_events_or_timeout(&mut test.rendezvous_swarm, &mut test.registration_swarm).await { + match await_events_or_timeout(&mut test.robert, &mut test.alice).await { ( SwarmEvent::Behaviour(Event::PeerNotRegistered { .. }), SwarmEvent::Behaviour(Event::RegisterFailed(RegisterError::Remote { error: err_code , ..})), @@ -120,40 +106,33 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { } } +/// Holds a network of nodes that is used to test certain rendezvous functionality. +/// +/// In all cases, Alice would like to connect to Bob with Robert acting as a rendezvous point. struct RendezvousTest { - pub registration_swarm: Swarm, - pub discovery_swarm: Swarm, - pub rendezvous_swarm: Swarm, + pub alice: Swarm, + pub bob: Swarm, + pub robert: Swarm, } const DEFAULT_TTL_UPPER_BOUND: i64 = 56_000; impl RendezvousTest { pub async fn setup() -> Self { - let mut registration_swarm = - new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); - registration_swarm.listen_on_random_memory_address().await; + let mut alice = new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); + alice.listen_on_random_memory_address().await; - let mut discovery_swarm = - new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); - discovery_swarm.listen_on_random_memory_address().await; + let mut bob = new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); + bob.listen_on_random_memory_address().await; - let mut rendezvous_swarm = + let mut robert = new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); - rendezvous_swarm.listen_on_random_memory_address().await; - - registration_swarm - .block_on_connection(&mut rendezvous_swarm) - .await; - discovery_swarm - .block_on_connection(&mut rendezvous_swarm) - .await; - - Self { - registration_swarm, - discovery_swarm, - rendezvous_swarm, - } + robert.listen_on_random_memory_address().await; + + alice.block_on_connection(&mut robert).await; + bob.block_on_connection(&mut robert).await; + + Self { alice, bob, robert } } pub async fn assert_successful_registration( @@ -161,13 +140,13 @@ impl RendezvousTest { expected_namespace: String, expected_ttl: i64, ) { - match await_events_or_timeout(&mut self.rendezvous_swarm, &mut self.registration_swarm).await { + match await_events_or_timeout(&mut self.robert, &mut self.alice).await { ( SwarmEvent::Behaviour(Event::PeerRegistered { peer, registration }), SwarmEvent::Behaviour(Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), ) => { - assert_eq!(&peer, self.registration_swarm.local_peer_id()); - assert_eq!(&rendezvous_node, self.rendezvous_swarm.local_peer_id()); + assert_eq!(&peer, self.alice.local_peer_id()); + assert_eq!(&rendezvous_node, self.robert.local_peer_id()); assert_eq!(registration.namespace, expected_namespace); assert_eq!(register_node_namespace, expected_namespace); assert_eq!(ttl, expected_ttl); @@ -185,7 +164,7 @@ impl RendezvousTest { expected_ttl: i64, expected_peer_id: PeerId, ) { - match await_events_or_timeout(&mut self.rendezvous_swarm, &mut self.discovery_swarm).await { + match await_events_or_timeout(&mut self.robert, &mut self.bob).await { ( SwarmEvent::Behaviour(Event::DiscoverServed { .. }), SwarmEvent::Behaviour(Event::Discovered { registrations, .. }), From c5a968cdfd9cc500cf6b327e3b076718852940fb Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 16:17:14 +1000 Subject: [PATCH 197/242] Verify peers can only submit registrations for themselves --- protocols/rendezvous/src/behaviour.rs | 55 ++++++++++++++--------- protocols/rendezvous/tests/harness/mod.rs | 6 +-- protocols/rendezvous/tests/rendezvous.rs | 50 ++++++++++++++++++++- 3 files changed, 87 insertions(+), 24 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index c718c7aafd8..1b6d0c4f5ea 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -159,6 +159,25 @@ impl NetworkBehaviour for Rendezvous { event: handler::OutEvent, ) { match event { + // bad registration + OutEvent::RegistrationRequested(registration) + if registration.record.peer_id() != peer_id => + { + let error = ErrorCode::NotAuthorized; + + self.events.extend(vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: InEvent::DeclineRegisterRequest(error), + }, + NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { + peer: peer_id, + namespace: registration.namespace, + error, + }), + ]) + } OutEvent::RegistrationRequested(registration) => { let namespace = registration.namespace.clone(); @@ -298,31 +317,27 @@ impl NetworkBehaviour for Rendezvous { .collect::>(); if external_addresses.is_empty() { - self.pending_register_requests - .push((namespace, rendezvous_node, ttl)); return Poll::Ready(NetworkBehaviourAction::GenerateEvent( Event::RegisterFailed(RegisterError::NoExternalAddresses), )); } - match PeerRecord::new(self.key_pair.clone(), external_addresses) { - Ok(peer_record) => { - return Poll::Ready(NetworkBehaviourAction::NotifyHandler { - peer_id: rendezvous_node, - event: InEvent::RegisterRequest(NewRegistration { - namespace, - record: peer_record, - ttl, - }), - handler: NotifyHandler::Any, - }) - } - Err(signing_error) => { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent( - Event::RegisterFailed(RegisterError::FailedToMakeRecord(signing_error)), - )) - } - } + let action = match PeerRecord::new(self.key_pair.clone(), external_addresses) { + Ok(peer_record) => NetworkBehaviourAction::NotifyHandler { + peer_id: rendezvous_node, + event: InEvent::RegisterRequest(NewRegistration { + namespace, + record: peer_record, + ttl, + }), + handler: NotifyHandler::Any, + }, + Err(signing_error) => NetworkBehaviourAction::GenerateEvent(Event::RegisterFailed( + RegisterError::FailedToMakeRecord(signing_error), + )), + }; + + return Poll::Ready(action); } Poll::Pending diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index ea40ef74099..ce7d8546fdc 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -26,12 +26,12 @@ impl Executor for GlobalSpawnTokioExecutor { } } -pub fn new_swarm B>( - behaviour_fn: F, -) -> Swarm +pub fn new_swarm(behaviour_fn: F) -> Swarm where B: NetworkBehaviour, ::OutEvent: Debug, + B: NetworkBehaviour, + F: FnOnce(PeerId, identity::Keypair) -> B, { let identity = identity::Keypair::generate_ed25519(); let peer_id = PeerId::from(identity.public()); diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index e6068208b1d..75cff713410 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,6 +1,7 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; +use libp2p_core::identity; use libp2p_core::PeerId; use libp2p_rendezvous::{ErrorCode, DEFAULT_TTL}; use libp2p_rendezvous::{Event, RegisterError, Rendezvous}; @@ -106,12 +107,41 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { } } +#[tokio::test] +async fn eve_cannot_register() { + let _ = env_logger::try_init(); + let mut test = RendezvousTest::setup().await; + + let namespace = "some-namespace".to_string(); + + let _ = test.eve.behaviour_mut().register( + namespace.clone(), + *test.robert.local_peer_id(), + Some(100_000), + ); + + match await_events_or_timeout(&mut test.robert, &mut test.eve).await { + ( + SwarmEvent::Behaviour(Event::PeerNotRegistered { .. }), + SwarmEvent::Behaviour(Event::RegisterFailed(RegisterError::Remote { error: err_code , ..})), + ) => { + assert_eq!(err_code, ErrorCode::NotAuthorized); + } + (rendezvous_swarm_event, registration_swarm_event) => panic!( + "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", + rendezvous_swarm_event, registration_swarm_event + ), + } +} + /// Holds a network of nodes that is used to test certain rendezvous functionality. /// /// In all cases, Alice would like to connect to Bob with Robert acting as a rendezvous point. +/// Eve is an evil actor that tries to act maliciously. struct RendezvousTest { pub alice: Swarm, pub bob: Swarm, + pub eve: Swarm, pub robert: Swarm, } @@ -129,10 +159,28 @@ impl RendezvousTest { new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); robert.listen_on_random_memory_address().await; + let mut eve = { + // In reality, if Eve were to try and fake someones identity, she would obviously only know the public key. + // Due to the type-safe API of the `Rendezvous` behaviour and `PeerRecord`, we actually cannot construct a bad `PeerRecord` (i.e. one that is claims to be someone else). + // As such, the best we can do is hand eve a completely different keypair from what she is using to authenticate her connection. + let someone_else = identity::Keypair::generate_ed25519(); + let mut eve = + new_swarm(move |_, _| Rendezvous::new(someone_else, DEFAULT_TTL_UPPER_BOUND)); + eve.listen_on_random_memory_address().await; + + eve + }; + alice.block_on_connection(&mut robert).await; bob.block_on_connection(&mut robert).await; + eve.block_on_connection(&mut robert).await; - Self { alice, bob, robert } + Self { + alice, + bob, + eve, + robert, + } } pub async fn assert_successful_registration( From 7bdc2775ee6d4642cc95aefeabc962ce62347568 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 16:26:44 +1000 Subject: [PATCH 198/242] Don't panic on invalid cookie --- protocols/rendezvous/src/behaviour.rs | 57 +++++++++++++++++---------- protocols/rendezvous/src/handler.rs | 12 ++++++ 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 1b6d0c4f5ea..40a0aa9282e 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -119,6 +119,8 @@ pub enum Event { enquirer: PeerId, registrations: Vec, }, + /// We failed to serve a discover request for a peer. + DiscoverNotServed { enquirer: PeerId, error: ErrorCode }, /// A peer successfully registered with us. PeerRegistered { peer: PeerId, @@ -242,28 +244,43 @@ impl NetworkBehaviour for Rendezvous { cookie, limit, } => { - let (registrations, cookie) = self - .registrations - .get(namespace, cookie, limit) - .expect("TODO: error handling: send back bad cookie"); + let events = match self.registrations.get(namespace, cookie, limit) { + Ok((registrations, cookie)) => { + let discovered = registrations.cloned().collect::>(); - let discovered = registrations.cloned().collect::>(); + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: InEvent::DiscoverResponse { + discovered: discovered.clone(), + cookie, + }, + }, + NetworkBehaviourAction::GenerateEvent(Event::DiscoverServed { + enquirer: peer_id, + registrations: discovered, + }), + ] + } + Err(_) => { + let error = ErrorCode::InvalidCookie; - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: InEvent::DiscoverResponse { - discovered: discovered.clone(), - cookie, - }, - }); - self.events.push_back(NetworkBehaviourAction::GenerateEvent( - Event::DiscoverServed { - enquirer: peer_id, - registrations: discovered, - }, - )); + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: InEvent::DeclineDiscoverRequest(error), + }, + NetworkBehaviourAction::GenerateEvent(Event::DiscoverNotServed { + enquirer: peer_id, + error, + }), + ] + } + }; + + self.events.extend(events); } OutEvent::Discovered { registrations, diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index d1b0c2c8c7c..ae6759bb988 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -89,6 +89,7 @@ pub enum InEvent { discovered: Vec, cookie: Cookie, }, + DeclineDiscoverRequest(ErrorCode), } /// The state of an inbound substream (i.e. the remote node opened it). @@ -462,6 +463,17 @@ impl ProtocolsHandler for RendezvousHandler { )), outbound, ), + ( + InEvent::DeclineDiscoverRequest(error), + SubstreamState::Active(Inbound::PendingBehaviour(substream)), + outbound, + ) => ( + SubstreamState::Active(Inbound::PendingSend( + substream, + Message::DiscoverResponse(Err(error)), + )), + outbound, + ), (event, inbound, outbound) => { log::warn!( "Neither {:?} nor {:?} can handle {:?}", From ea202734921376a00513985d175c5e75a4dfbf9a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 16:31:03 +1000 Subject: [PATCH 199/242] Model `inject_event` implementation of mapping to new events This avoids the repetition of `self.events.{push, extend}`. --- protocols/rendezvous/src/behaviour.rs | 133 +++++++++++++------------- 1 file changed, 67 insertions(+), 66 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 40a0aa9282e..bf9b1122f5b 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -160,14 +160,14 @@ impl NetworkBehaviour for Rendezvous { connection: ConnectionId, event: handler::OutEvent, ) { - match event { + let new_events = match event { // bad registration OutEvent::RegistrationRequested(registration) if registration.record.peer_id() != peer_id => { let error = ErrorCode::NotAuthorized; - self.events.extend(vec![ + vec![ NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), @@ -178,12 +178,12 @@ impl NetworkBehaviour for Rendezvous { namespace: registration.namespace, error, }), - ]) + ] } OutEvent::RegistrationRequested(registration) => { let namespace = registration.namespace.clone(); - let events = match self.registrations.add(registration) { + match self.registrations.add(registration) { Ok(registration) => { vec![ NetworkBehaviourAction::NotifyHandler { @@ -215,94 +215,95 @@ impl NetworkBehaviour for Rendezvous { }), ] } - }; - - self.events.extend(events); + } } OutEvent::Registered { namespace, ttl } => { - self.events - .push_back(NetworkBehaviourAction::GenerateEvent(Event::Registered { - rendezvous_node: peer_id, - ttl, - namespace, - })) + vec![NetworkBehaviourAction::GenerateEvent(Event::Registered { + rendezvous_node: peer_id, + ttl, + namespace, + })] } OutEvent::RegisterFailed { namespace, error } => { - self.events.push_back(NetworkBehaviourAction::GenerateEvent( + vec![NetworkBehaviourAction::GenerateEvent( Event::RegisterFailed(RegisterError::Remote { rendezvous_node: peer_id, namespace, error, }), - )) + )] } OutEvent::UnregisterRequested { namespace } => { - self.registrations.remove(namespace, peer_id); + self.registrations.remove(namespace.clone(), peer_id); + + vec![NetworkBehaviourAction::GenerateEvent( + Event::PeerUnregistered { peer, namespace }, + )] } OutEvent::DiscoverRequested { namespace, cookie, limit, - } => { - let events = match self.registrations.get(namespace, cookie, limit) { - Ok((registrations, cookie)) => { - let discovered = registrations.cloned().collect::>(); - - vec![ - NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: InEvent::DiscoverResponse { - discovered: discovered.clone(), - cookie, - }, - }, - NetworkBehaviourAction::GenerateEvent(Event::DiscoverServed { - enquirer: peer_id, - registrations: discovered, - }), - ] - } - Err(_) => { - let error = ErrorCode::InvalidCookie; - - vec![ - NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: InEvent::DeclineDiscoverRequest(error), + } => match self.registrations.get(namespace, cookie, limit) { + Ok((registrations, cookie)) => { + let discovered = registrations.cloned().collect::>(); + + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: InEvent::DiscoverResponse { + discovered: discovered.clone(), + cookie, }, - NetworkBehaviourAction::GenerateEvent(Event::DiscoverNotServed { - enquirer: peer_id, - error, - }), - ] - } - }; - - self.events.extend(events); - } + }, + NetworkBehaviourAction::GenerateEvent(Event::DiscoverServed { + enquirer: peer_id, + registrations: discovered, + }), + ] + } + Err(_) => { + let error = ErrorCode::InvalidCookie; + + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: InEvent::DeclineDiscoverRequest(error), + }, + NetworkBehaviourAction::GenerateEvent(Event::DiscoverNotServed { + enquirer: peer_id, + error, + }), + ] + } + }, OutEvent::Discovered { registrations, cookie, - } => self - .events - .push_back(NetworkBehaviourAction::GenerateEvent(Event::Discovered { + } => { + vec![NetworkBehaviourAction::GenerateEvent(Event::Discovered { rendezvous_node: peer_id, registrations: registrations .iter() .map(|r| ((r.namespace.clone(), r.record.peer_id()), r.clone())) .collect(), cookie, - })), - OutEvent::DiscoverFailed { namespace, error } => self.events.push_back( - NetworkBehaviourAction::GenerateEvent(Event::DiscoverFailed { - rendezvous_node: peer_id, - namespace, - error, - }), - ), - } + })] + } + OutEvent::DiscoverFailed { namespace, error } => { + vec![NetworkBehaviourAction::GenerateEvent( + Event::DiscoverFailed { + rendezvous_node: peer_id, + namespace, + error, + }, + )] + } + }; + + self.events.extend(new_events); } fn poll( From 531570efe7926ac338f05735d89469e90241150d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 16:37:08 +1000 Subject: [PATCH 200/242] Return a simple list in `Discovered` event --- protocols/rendezvous/examples/discover.rs | 3 ++- protocols/rendezvous/src/behaviour.rs | 12 +++++------ protocols/rendezvous/src/lib.rs | 1 + protocols/rendezvous/tests/rendezvous.rs | 26 +++++++++++------------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 1423f2567be..8c848f7d41a 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -64,8 +64,9 @@ async fn main() { registrations, .. })) => { - for ((_, peer), registration) in registrations { + for registration in registrations { for address in registration.record.addresses() { + let peer = registration.record.peer_id(); log::info!("Discovered peer {} at {}", peer, address); let p2p_suffix = Protocol::P2p(peer.as_ref().clone()); diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index bf9b1122f5b..382417d31cb 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -97,7 +97,7 @@ pub enum Event { /// We successfully discovered other nodes with using the contained rendezvous node. Discovered { rendezvous_node: PeerId, - registrations: HashMap<(String, PeerId), Registration>, + registrations: Vec, cookie: Cookie, }, /// We failed to discover other nodes on the contained rendezvous node. @@ -237,7 +237,10 @@ impl NetworkBehaviour for Rendezvous { self.registrations.remove(namespace.clone(), peer_id); vec![NetworkBehaviourAction::GenerateEvent( - Event::PeerUnregistered { peer, namespace }, + Event::PeerUnregistered { + peer: peer_id, + namespace, + }, )] } OutEvent::DiscoverRequested { @@ -285,10 +288,7 @@ impl NetworkBehaviour for Rendezvous { } => { vec![NetworkBehaviourAction::GenerateEvent(Event::Discovered { rendezvous_node: peer_id, - registrations: registrations - .iter() - .map(|r| ((r.namespace.clone(), r.record.peer_id()), r.clone())) - .collect(), + registrations, cookie, })] } diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 3121d0175ea..02904dc771f 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -8,4 +8,5 @@ pub use behaviour::Event; pub use behaviour::RegisterError; pub use behaviour::Rendezvous; pub use codec::ErrorCode; +pub use codec::Registration; pub use codec::DEFAULT_TTL; diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 75cff713410..35352a06443 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -4,7 +4,7 @@ use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use libp2p_core::identity; use libp2p_core::PeerId; use libp2p_rendezvous::{ErrorCode, DEFAULT_TTL}; -use libp2p_rendezvous::{Event, RegisterError, Rendezvous}; +use libp2p_rendezvous::{Event, RegisterError, Registration, Rendezvous}; use libp2p_swarm::{Swarm, SwarmEvent}; #[tokio::test] @@ -216,20 +216,18 @@ impl RendezvousTest { ( SwarmEvent::Behaviour(Event::DiscoverServed { .. }), SwarmEvent::Behaviour(Event::Discovered { registrations, .. }), - ) => { - if let Some(reg) = - registrations.get(&(expected_namespace.clone(), expected_peer_id)) - { - assert_eq!(reg.ttl, expected_ttl) - } else { - { - panic!( - "Registration with namespace {} and peer id {} not found", - expected_namespace, expected_peer_id - ) - } + ) => match registrations.as_slice() { + [Registration { + namespace, + record, + ttl, + }] => { + assert_eq!(*ttl, expected_ttl); + assert_eq!(record.peer_id(), expected_peer_id); + assert_eq!(*namespace, expected_namespace); } - } + _ => panic!("Expected exactly one registration to be returned from discover"), + }, (e1, e2) => panic!("Unexpected events {:?} {:?}", e1, e2), } } From fad394187573143947a1402357f4d223c15c81f8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 16:51:35 +1000 Subject: [PATCH 201/242] Honor min and max TTL from spec --- protocols/rendezvous/examples/discover.rs | 2 +- protocols/rendezvous/examples/register.rs | 2 +- .../rendezvous/examples/rendezvous_point.rs | 2 +- protocols/rendezvous/src/behaviour.rs | 98 ++++++++++++++----- protocols/rendezvous/src/codec.rs | 6 +- protocols/rendezvous/src/lib.rs | 17 +++- protocols/rendezvous/tests/rendezvous.rs | 16 ++- 7 files changed, 97 insertions(+), 46 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 8c848f7d41a..2de39e19469 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -24,7 +24,7 @@ async fn main() { let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), MyBehaviour { - rendezvous: Rendezvous::new(identity.clone(), 10000), + rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 302f60963e9..30f493ad69a 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -28,7 +28,7 @@ async fn main() { "rendezvous-example/1.0.0".to_string(), identity.public(), )), - rendezvous: Rendezvous::new(identity.clone(), 10000), + rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), diff --git a/protocols/rendezvous/examples/rendezvous_point.rs b/protocols/rendezvous/examples/rendezvous_point.rs index d0f371647db..9066a58c833 100644 --- a/protocols/rendezvous/examples/rendezvous_point.rs +++ b/protocols/rendezvous/examples/rendezvous_point.rs @@ -23,7 +23,7 @@ async fn main() { "rendezvous-example/1.0.0".to_string(), identity.public(), )), - rendezvous: Rendezvous::new(identity.clone(), 10000), + rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), ping: Ping::default(), }, PeerId::from(identity.public()), diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 382417d31cb..1cc63d575e8 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,6 +1,6 @@ use crate::codec::{Cookie, ErrorCode, NewRegistration, Registration}; -use crate::handler; use crate::handler::{InEvent, OutEvent, RendezvousHandler}; +use crate::{handler, MAX_TTL, MIN_TTL}; use bimap::BiMap; use futures::future::BoxFuture; use futures::ready; @@ -28,11 +28,25 @@ pub struct Rendezvous { pending_register_requests: Vec<(String, PeerId, Option)>, } +pub struct Config { + min_ttl: i64, + max_ttl: i64, +} + +impl Default for Config { + fn default() -> Self { + Self { + min_ttl: MIN_TTL, + max_ttl: MAX_TTL, + } + } +} + impl Rendezvous { - pub fn new(key_pair: Keypair, ttl_upper_board: i64) -> Self { + pub fn new(key_pair: Keypair, config: Config) -> Self { Self { events: Default::default(), - registrations: Registrations::new(ttl_upper_board), + registrations: Registrations::with_config(config), key_pair, pending_register_requests: vec![], } @@ -199,7 +213,7 @@ impl NetworkBehaviour for Rendezvous { }), ] } - Err(TtlTooLong { .. }) => { + Err(TtlOutOfRange::TooLong { .. } | TtlOutOfRange::TooShort { .. }) => { let error = ErrorCode::InvalidTtl; vec![ @@ -378,33 +392,51 @@ pub struct Registrations { registrations_for_peer: BiMap<(PeerId, String), RegistrationId>, registrations: HashMap, cookies: HashMap>, - ttl_upper_bound: i64, + min_ttl: i64, + max_ttl: i64, next_expiry: FuturesUnordered>, } #[derive(Debug, thiserror::Error)] -#[error("Requested TTL {requested}s is longer than what we allow ({upper_bound}s)")] -pub struct TtlTooLong { - upper_bound: i64, - requested: i64, +pub enum TtlOutOfRange { + #[error("Requested TTL ({requested}s) is too long; max {bound}s")] + TooLong { bound: i64, requested: i64 }, + #[error("Requested TTL ({requested}s) is too short; min {bound}s")] + TooShort { bound: i64, requested: i64 }, +} + +impl Default for Registrations { + fn default() -> Self { + Registrations::with_config(Config::default()) + } } impl Registrations { - pub fn new(ttl_upper_bound: i64) -> Self { + pub fn with_config(config: Config) -> Self { Self { registrations_for_peer: Default::default(), registrations: Default::default(), - ttl_upper_bound, + min_ttl: config.min_ttl, + max_ttl: config.max_ttl, cookies: Default::default(), next_expiry: FuturesUnordered::from_iter(vec![futures::future::pending().boxed()]), } } - pub fn add(&mut self, new_registration: NewRegistration) -> Result { + pub fn add( + &mut self, + new_registration: NewRegistration, + ) -> Result { let ttl = new_registration.effective_ttl(); - if ttl > self.ttl_upper_bound { - return Err(TtlTooLong { - upper_bound: self.ttl_upper_bound, + if ttl > self.max_ttl { + return Err(TtlOutOfRange::TooLong { + bound: self.max_ttl, + requested: ttl, + }); + } + if ttl < self.min_ttl { + return Err(TtlOutOfRange::TooShort { + bound: self.min_ttl, requested: ttl, }); } @@ -556,7 +588,7 @@ mod tests { #[tokio::test] async fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -569,7 +601,7 @@ mod tests { #[tokio::test] async fn given_registrations_when_discover_all_then_all_are_returned() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -581,7 +613,7 @@ mod tests { #[tokio::test] async fn given_registrations_when_discover_only_for_specific_namespace_then_only_those_are_returned( ) { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); @@ -598,7 +630,7 @@ mod tests { #[tokio::test] async fn given_reregistration_old_registration_is_discarded() { let alice = identity::Keypair::generate_ed25519(); - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::default(); registrations .add(new_registration("foo", alice.clone(), None)) .unwrap(); @@ -618,7 +650,7 @@ mod tests { #[tokio::test] async fn given_cookie_from_2nd_discover_does_not_return_nodes_from_first_discover() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -634,7 +666,7 @@ mod tests { #[tokio::test] async fn cookie_from_different_discover_request_is_not_valid() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); @@ -648,7 +680,10 @@ mod tests { #[tokio::test] async fn given_two_registration_ttls_one_expires_one_lives() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::with_config(Config { + min_ttl: 0, + max_ttl: 4, + }); let start_time = SystemTime::now(); @@ -681,7 +716,10 @@ mod tests { #[tokio::test] async fn given_peer_unregisters_before_expiry_do_not_emit_registration_expired() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::with_config(Config { + min_ttl: 1, + max_ttl: 10, + }); let dummy_registration = new_dummy_registration_with_ttl("foo", 2); let namespace = dummy_registration.namespace.clone(); let peer_id = dummy_registration.record.peer_id(); @@ -699,7 +737,10 @@ mod tests { /// FuturesUnordered does not stop polling for ready futures. #[tokio::test] async fn given_all_registrations_expired_then_succesfully_handle_new_registration_and_expiry() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::with_config(Config { + min_ttl: 0, + max_ttl: 10, + }); let dummy_registration = new_dummy_registration_with_ttl("foo", 1); registrations.add(dummy_registration.clone()).unwrap(); @@ -713,7 +754,10 @@ mod tests { #[tokio::test] async fn cookies_are_cleaned_up_if_registrations_expire() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::with_config(Config { + min_ttl: 1, + max_ttl: 10, + }); registrations .add(new_dummy_registration_with_ttl("foo", 2)) @@ -729,7 +773,7 @@ mod tests { #[tokio::test] async fn given_limit_discover_only_returns_n_results() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -740,7 +784,7 @@ mod tests { #[tokio::test] async fn given_limit_cookie_can_be_used_for_pagination() { - let mut registrations = Registrations::new(7200); + let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 55705ec5d3f..230194ffb70 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -1,3 +1,4 @@ +use crate::DEFAULT_TTL; use asynchronous_codec::{Bytes, BytesMut, Decoder, Encoder}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; use std::convert::{TryFrom, TryInto}; @@ -94,11 +95,6 @@ pub struct NewRegistration { pub ttl: Option, } -/// If unspecified, rendezvous nodes should assume a TTL of 2h. -/// -/// See https://github.com/libp2p/specs/blob/d21418638d5f09f2a4e5a1ceca17058df134a300/rendezvous/README.md#L116-L117. -pub const DEFAULT_TTL: i64 = 60 * 60 * 2; - impl NewRegistration { pub fn new(namespace: String, record: PeerRecord, ttl: Option) -> Self { Self { diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 02904dc771f..9294b9071d3 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -4,9 +4,24 @@ mod handler; mod protocol; mod substream; +pub use behaviour::Config; pub use behaviour::Event; pub use behaviour::RegisterError; pub use behaviour::Rendezvous; pub use codec::ErrorCode; pub use codec::Registration; -pub use codec::DEFAULT_TTL; + +/// If unspecified, rendezvous nodes should assume a TTL of 2h. +/// +/// See https://github.com/libp2p/specs/blob/d21418638d5f09f2a4e5a1ceca17058df134a300/rendezvous/README.md#L116-L117. +pub const DEFAULT_TTL: i64 = 60 * 60 * 2; + +/// By default, nodes should require a minimum TTL of 2h +/// +/// https://github.com/libp2p/specs/tree/master/rendezvous#recommendations-for-rendezvous-points-configurations. +pub const MIN_TTL: i64 = 60 * 60 * 2; + +/// By default, nodes should allow a maximum TTL of 72h +/// +/// https://github.com/libp2p/specs/tree/master/rendezvous#recommendations-for-rendezvous-points-configurations. +pub const MAX_TTL: i64 = 60 * 60 * 72; diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 35352a06443..d8a95736d37 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -3,7 +3,7 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use libp2p_core::identity; use libp2p_core::PeerId; -use libp2p_rendezvous::{ErrorCode, DEFAULT_TTL}; +use libp2p_rendezvous::{Config, ErrorCode, DEFAULT_TTL}; use libp2p_rendezvous::{Event, RegisterError, Registration, Rendezvous}; use libp2p_swarm::{Swarm, SwarmEvent}; @@ -90,7 +90,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { let _ = test.alice.behaviour_mut().register( namespace.clone(), *test.robert.local_peer_id(), - Some(100_000), + Some(100_000_000), ); match await_events_or_timeout(&mut test.robert, &mut test.alice).await { @@ -145,18 +145,15 @@ struct RendezvousTest { pub robert: Swarm, } -const DEFAULT_TTL_UPPER_BOUND: i64 = 56_000; - impl RendezvousTest { pub async fn setup() -> Self { - let mut alice = new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); + let mut alice = new_swarm(|_, identity| Rendezvous::new(identity, Config::default())); alice.listen_on_random_memory_address().await; - let mut bob = new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); + let mut bob = new_swarm(|_, identity| Rendezvous::new(identity, Config::default())); bob.listen_on_random_memory_address().await; - let mut robert = - new_swarm(|_, identity| Rendezvous::new(identity, DEFAULT_TTL_UPPER_BOUND)); + let mut robert = new_swarm(|_, identity| Rendezvous::new(identity, Config::default())); robert.listen_on_random_memory_address().await; let mut eve = { @@ -164,8 +161,7 @@ impl RendezvousTest { // Due to the type-safe API of the `Rendezvous` behaviour and `PeerRecord`, we actually cannot construct a bad `PeerRecord` (i.e. one that is claims to be someone else). // As such, the best we can do is hand eve a completely different keypair from what she is using to authenticate her connection. let someone_else = identity::Keypair::generate_ed25519(); - let mut eve = - new_swarm(move |_, _| Rendezvous::new(someone_else, DEFAULT_TTL_UPPER_BOUND)); + let mut eve = new_swarm(move |_, _| Rendezvous::new(someone_else, Config::default())); eve.listen_on_random_memory_address().await; eve From 96002105b0a7019330413826a332b3a12a7e8a57 Mon Sep 17 00:00:00 2001 From: rishflab Date: Wed, 23 Jun 2021 17:14:44 +1000 Subject: [PATCH 202/242] Format exports + Cleanup + Re-naming --- protocols/rendezvous/src/lib.rs | 11 ++++------- protocols/rendezvous/tests/rendezvous.rs | 9 +++++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 9294b9071d3..13c5c14b791 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,16 +1,13 @@ +pub use self::behaviour::{Config, Event, RegisterError, Rendezvous}; +pub use self::codec::ErrorCode; +pub use codec::Registration; + mod behaviour; mod codec; mod handler; mod protocol; mod substream; -pub use behaviour::Config; -pub use behaviour::Event; -pub use behaviour::RegisterError; -pub use behaviour::Rendezvous; -pub use codec::ErrorCode; -pub use codec::Registration; - /// If unspecified, rendezvous nodes should assume a TTL of 2h. /// /// See https://github.com/libp2p/specs/blob/d21418638d5f09f2a4e5a1ceca17058df134a300/rendezvous/README.md#L116-L117. diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index d8a95736d37..81151d51c18 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -3,8 +3,9 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use libp2p_core::identity; use libp2p_core::PeerId; -use libp2p_rendezvous::{Config, ErrorCode, DEFAULT_TTL}; -use libp2p_rendezvous::{Event, RegisterError, Registration, Rendezvous}; +use libp2p_rendezvous::{ + Config, ErrorCode, Event, RegisterError, Registration, Rendezvous, DEFAULT_TTL, +}; use libp2p_swarm::{Swarm, SwarmEvent}; #[tokio::test] @@ -96,9 +97,9 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { match await_events_or_timeout(&mut test.robert, &mut test.alice).await { ( SwarmEvent::Behaviour(Event::PeerNotRegistered { .. }), - SwarmEvent::Behaviour(Event::RegisterFailed(RegisterError::Remote { error: err_code , ..})), + SwarmEvent::Behaviour(Event::RegisterFailed(RegisterError::Remote {error , ..})), ) => { - assert_eq!(err_code, ErrorCode::InvalidTtl); + assert_eq!(error, ErrorCode::InvalidTtl); } (rendezvous_swarm_event, registration_swarm_event) => panic!( "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", From 6aa4c36a0d88b41b32e7f1106ba0307dc6d35b81 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 17:23:33 +1000 Subject: [PATCH 203/242] Introduce `Namespace` newtype to capture max-length invariant Additionally, this allows us to convert some of our enum struct variants to tuple variants because the name of the type conveys the same amount of information as a fieldname. --- protocols/rendezvous/examples/discover.rs | 4 +- protocols/rendezvous/examples/register.rs | 3 +- protocols/rendezvous/src/behaviour.rs | 62 ++++++------ protocols/rendezvous/src/codec.rs | 118 +++++++++++++++++----- protocols/rendezvous/src/handler.rs | 36 +++---- protocols/rendezvous/src/lib.rs | 2 +- protocols/rendezvous/tests/rendezvous.rs | 13 +-- 7 files changed, 150 insertions(+), 88 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 2de39e19469..317b8e1920c 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -3,7 +3,7 @@ use libp2p::core::identity; use libp2p::core::PeerId; use libp2p::multiaddr::Protocol; use libp2p::ping::{Ping, PingConfig, PingEvent, PingSuccess}; -use libp2p::rendezvous::Rendezvous; +use libp2p::rendezvous::{Namespace, Rendezvous}; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; use libp2p::{development_transport, rendezvous, Multiaddr}; @@ -43,7 +43,7 @@ async fn main() { ); swarm.behaviour_mut().rendezvous.discover( - Some(NAMESPACE.to_string()), + Some(Namespace::new(NAMESPACE.to_string()).unwrap()), None, None, rendezvous_point, diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 30f493ad69a..a63b1755320 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -8,6 +8,7 @@ use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; use libp2p::{development_transport, rendezvous}; use libp2p::{Multiaddr, NetworkBehaviour}; +use libp2p_rendezvous::Namespace; use std::time::Duration; #[async_std::main] @@ -57,7 +58,7 @@ async fn main() { swarm .behaviour_mut() .rendezvous - .register("rendezvous".to_string(), rendezvous_point, None) + .register(Namespace::from_static("rendezvous"), rendezvous_point, None) .unwrap(); } SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Registered { diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 1cc63d575e8..c3fcbb29b9a 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,4 +1,4 @@ -use crate::codec::{Cookie, ErrorCode, NewRegistration, Registration}; +use crate::codec::{Cookie, ErrorCode, Namespace, NewRegistration, Registration}; use crate::handler::{InEvent, OutEvent, RendezvousHandler}; use crate::{handler, MAX_TTL, MIN_TTL}; use bimap::BiMap; @@ -25,7 +25,7 @@ pub struct Rendezvous { events: VecDeque>, registrations: Registrations, key_pair: Keypair, - pending_register_requests: Vec<(String, PeerId, Option)>, + pending_register_requests: Vec<(Namespace, PeerId, Option)>, } pub struct Config { @@ -54,7 +54,7 @@ impl Rendezvous { pub fn register( &mut self, - namespace: String, + namespace: Namespace, rendezvous_node: PeerId, ttl: Option, ) -> Result<(), RegisterError> { @@ -63,18 +63,18 @@ impl Rendezvous { Ok(()) } - pub fn unregister(&mut self, namespace: String, rendezvous_node: PeerId) { + pub fn unregister(&mut self, namespace: Namespace, rendezvous_node: PeerId) { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: InEvent::UnregisterRequest { namespace }, + event: InEvent::UnregisterRequest(namespace), handler: NotifyHandler::Any, }); } pub fn discover( &mut self, - ns: Option, + ns: Option, cookie: Option, limit: Option, rendezvous_node: PeerId, @@ -101,7 +101,7 @@ pub enum RegisterError { #[error("Failed to register with Rendezvous node")] Remote { rendezvous_node: PeerId, - namespace: String, + namespace: Namespace, error: ErrorCode, }, } @@ -117,14 +117,14 @@ pub enum Event { /// We failed to discover other nodes on the contained rendezvous node. DiscoverFailed { rendezvous_node: PeerId, - namespace: Option, + namespace: Option, error: ErrorCode, }, /// We successfully registered with the contained rendezvous node. Registered { rendezvous_node: PeerId, ttl: i64, - namespace: String, + namespace: Namespace, }, /// We failed to register with the contained rendezvous node. RegisterFailed(RegisterError), @@ -143,11 +143,11 @@ pub enum Event { /// We declined a registration from a peer. PeerNotRegistered { peer: PeerId, - namespace: String, + namespace: Namespace, error: ErrorCode, }, /// A peer successfully unregistered with us. - PeerUnregistered { peer: PeerId, namespace: String }, + PeerUnregistered { peer: PeerId, namespace: Namespace }, /// A registration from a peer expired. RegistrationExpired(Registration), } @@ -238,7 +238,7 @@ impl NetworkBehaviour for Rendezvous { namespace, })] } - OutEvent::RegisterFailed { namespace, error } => { + OutEvent::RegisterFailed(namespace, error) => { vec![NetworkBehaviourAction::GenerateEvent( Event::RegisterFailed(RegisterError::Remote { rendezvous_node: peer_id, @@ -247,7 +247,7 @@ impl NetworkBehaviour for Rendezvous { }), )] } - OutEvent::UnregisterRequested { namespace } => { + OutEvent::UnregisterRequested(namespace) => { self.registrations.remove(namespace.clone(), peer_id); vec![NetworkBehaviourAction::GenerateEvent( @@ -389,7 +389,7 @@ impl RegistrationId { struct ExpiredRegistration(Registration); pub struct Registrations { - registrations_for_peer: BiMap<(PeerId, String), RegistrationId>, + registrations_for_peer: BiMap<(PeerId, Namespace), RegistrationId>, registrations: HashMap, cookies: HashMap>, min_ttl: i64, @@ -473,7 +473,7 @@ impl Registrations { Ok(registration) } - pub fn remove(&mut self, namespace: String, peer_id: PeerId) { + pub fn remove(&mut self, namespace: Namespace, peer_id: PeerId) { let reggo_to_remove = self .registrations_for_peer .remove_by_left(&(peer_id, namespace)); @@ -485,7 +485,7 @@ impl Registrations { pub fn get( &mut self, - discover_namespace: Option, + discover_namespace: Option, cookie: Option, mut limit: Option, ) -> Result<(impl Iterator + '_, Cookie), CookieNamespaceMismatch> { @@ -618,11 +618,11 @@ mod tests { registrations.add(new_dummy_registration("bar")).unwrap(); let (discover, _) = registrations - .get(Some("foo".to_owned()), None, None) + .get(Some(Namespace::from_static("foo")), None, None) .unwrap(); assert_eq!( - discover.map(|r| r.namespace.as_str()).collect::>(), + discover.map(|r| &r.namespace).collect::>(), vec!["foo"] ); } @@ -639,11 +639,11 @@ mod tests { .unwrap(); let (discover, _) = registrations - .get(Some("foo".to_owned()), None, None) + .get(Some(Namespace::from_static("foo")), None, None) .unwrap(); assert_eq!( - discover.map(|r| r.namespace.as_str()).collect::>(), + discover.map(|r| &r.namespace).collect::>(), vec!["foo"] ); } @@ -671,9 +671,13 @@ mod tests { registrations.add(new_dummy_registration("bar")).unwrap(); let (_, foo_discover_cookie) = registrations - .get(Some("foo".to_owned()), None, None) + .get(Some(Namespace::from_static("foo")), None, None) .unwrap(); - let result = registrations.get(Some("bar".to_owned()), Some(foo_discover_cookie), None); + let result = registrations.get( + Some(Namespace::from_static("bar")), + Some(foo_discover_cookie), + None, + ); assert!(matches!(result, Err(CookieNamespaceMismatch))) } @@ -700,16 +704,16 @@ mod tests { assert!(elapsed.as_secs() >= 1); assert!(elapsed.as_secs() < 2); - assert_eq!(event.0.namespace, "foo"); + assert_eq!(event.0.namespace, Namespace::from_static("foo")); { let (mut discovered_foo, _) = registrations - .get(Some("foo".to_owned()), None, None) + .get(Some(Namespace::from_static("foo")), None, None) .unwrap(); assert!(discovered_foo.next().is_none()); } let (mut discovered_bar, _) = registrations - .get(Some("bar".to_owned()), None, None) + .get(Some(Namespace::from_static("bar")), None, None) .unwrap(); assert!(discovered_bar.next().is_some()); } @@ -795,25 +799,25 @@ mod tests { assert_eq!(discover2.count(), 1); } - fn new_dummy_registration(namespace: &str) -> NewRegistration { + fn new_dummy_registration(namespace: &'static str) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); new_registration(namespace, identity, None) } - fn new_dummy_registration_with_ttl(namespace: &str, ttl: i64) -> NewRegistration { + fn new_dummy_registration_with_ttl(namespace: &'static str, ttl: i64) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); new_registration(namespace, identity, Some(ttl)) } fn new_registration( - namespace: &str, + namespace: &'static str, identity: identity::Keypair, ttl: Option, ) -> NewRegistration { NewRegistration::new( - namespace.to_owned(), + Namespace::from_static(namespace), PeerRecord::new(identity, vec!["/ip4/127.0.0.1/tcp/1234".parse().unwrap()]).unwrap(), ttl, ) diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 230194ffb70..f3359011b38 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -2,6 +2,7 @@ use crate::DEFAULT_TTL; use asynchronous_codec::{Bytes, BytesMut, Decoder, Encoder}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; use std::convert::{TryFrom, TryInto}; +use std::fmt; use unsigned_varint::codec::UviBytes; use uuid::Uuid; @@ -11,28 +12,78 @@ pub type Ttl = i64; pub enum Message { Register(NewRegistration), RegisterResponse(Result), - Unregister { - namespace: String, - }, + Unregister(Namespace), Discover { - namespace: Option, + namespace: Option, cookie: Option, limit: Option, }, DiscoverResponse(Result<(Vec, Cookie), ErrorCode>), } +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct Namespace(String); + +impl Namespace { + /// Creates a new [`Namespace`] from a static string. + /// + /// 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 { + panic!("Namespace '{}' is too long!", value) + } + + Namespace(value.to_owned()) + } + + pub fn new(value: String) -> Result { + if value.len() > 255 { + return Err(NamespaceTooLong); + } + + Ok(Namespace(value)) + } +} + +impl From for String { + fn from(namespace: Namespace) -> Self { + namespace.0 + } +} + +impl fmt::Display for Namespace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl PartialEq for Namespace { + fn eq(&self, other: &str) -> bool { + self.0.eq(other) + } +} + +impl PartialEq for str { + fn eq(&self, other: &Namespace) -> bool { + other.0.eq(self) + } +} + +#[derive(Debug, thiserror::Error)] +#[error("Namespace is too long")] +pub struct NamespaceTooLong; + #[derive(Debug, Eq, PartialEq, Hash, Clone)] pub struct Cookie { id: Uuid, - namespace: Option, + namespace: Option, } impl Cookie { /// Construct a new [`Cookie`] for a given namespace. /// /// This cookie will only be valid for subsequent DISCOVER requests targeting the same namespace. - pub fn for_namespace(namespace: String) -> Self { + pub fn for_namespace(namespace: Namespace) -> Self { Self { id: Uuid::new_v4(), namespace: Some(namespace), @@ -48,7 +99,7 @@ impl Cookie { } pub fn into_wire_encoding(self) -> Vec { - let namespace = self.namespace.unwrap_or_default(); + let namespace = self.namespace.map(|ns| ns.0).unwrap_or_default(); let mut buffer = Vec::with_capacity(16 + namespace.len()); buffer.extend_from_slice(self.id.as_bytes()); @@ -67,7 +118,10 @@ impl Cookie { let namespace = if namespace.is_empty() { None } else { - Some(String::from_utf8(namespace).map_err(|_| InvalidCookie)?) + Some( + Namespace::new(String::from_utf8(namespace).map_err(|_| InvalidCookie)?) + .map_err(|_| InvalidCookie)?, + ) }; let bytes = <[u8; 16]>::try_from(bytes).map_err(|_| InvalidCookie)?; @@ -79,8 +133,8 @@ impl Cookie { }) } - pub fn namespace(&self) -> Option<&str> { - self.namespace.as_deref() + pub fn namespace(&self) -> Option<&Namespace> { + self.namespace.as_ref() } } @@ -90,13 +144,13 @@ pub struct InvalidCookie; #[derive(Debug, Clone)] pub struct NewRegistration { - pub namespace: String, + pub namespace: Namespace, pub record: PeerRecord, pub ttl: Option, } impl NewRegistration { - pub fn new(namespace: String, record: PeerRecord, ttl: Option) -> Self { + pub fn new(namespace: Namespace, record: PeerRecord, ttl: Option) -> Self { Self { namespace, record, @@ -111,7 +165,7 @@ impl NewRegistration { #[derive(Debug, Clone, PartialEq)] pub struct Registration { - pub namespace: String, + pub namespace: Namespace, pub record: PeerRecord, pub ttl: i64, } @@ -205,7 +259,7 @@ impl From for wire::Message { }) => wire::Message { r#type: Some(MessageType::Register.into()), register: Some(Register { - ns: Some(namespace), + ns: Some(namespace.into()), ttl, signed_peer_record: Some( record.into_signed_envelope().into_protobuf_encoding(), @@ -240,10 +294,10 @@ impl From for wire::Message { unregister: None, discover_response: None, }, - Message::Unregister { namespace } => wire::Message { + Message::Unregister(namespace) => wire::Message { r#type: Some(MessageType::Unregister.into()), unregister: Some(Unregister { - ns: Some(namespace), + ns: Some(namespace.into()), id: None, }), register: None, @@ -258,7 +312,7 @@ impl From for wire::Message { } => wire::Message { r#type: Some(MessageType::Discover.into()), discover: Some(Discover { - ns: namespace, + ns: namespace.map(|ns| ns.into()), cookie: cookie.map(|cookie| cookie.into_wire_encoding()), limit, }), @@ -273,7 +327,7 @@ impl From for wire::Message { registrations: registrations .into_iter() .map(|reggo| Register { - ns: Some(reggo.namespace), + ns: Some(reggo.namespace.into()), ttl: Some(reggo.ttl), signed_peer_record: Some( reggo.record.into_signed_envelope().into_protobuf_encoding(), @@ -323,7 +377,10 @@ impl TryFrom for Message { }), .. } => Message::Register(NewRegistration { - namespace: ns.ok_or(ConversionError::MissingNamespace)?, + namespace: ns + .map(Namespace::new) + .transpose()? + .ok_or(ConversionError::MissingNamespace)?, ttl, record: PeerRecord::from_signed_envelope(SignedEnvelope::from_protobuf_encoding( &signed_peer_record, @@ -344,7 +401,7 @@ impl TryFrom for Message { discover: Some(Discover { ns, limit, cookie }), .. } => Message::Discover { - namespace: ns, + namespace: ns.map(Namespace::new).transpose()?, cookie: cookie.map(Cookie::from_wire_encoding).transpose()?, limit, }, @@ -363,7 +420,11 @@ impl TryFrom for Message { .into_iter() .map(|reggo| { Ok(Registration { - namespace: reggo.ns.ok_or(ConversionError::MissingNamespace)?, + namespace: reggo + .ns + .map(Namespace::new) + .transpose()? + .ok_or(ConversionError::MissingNamespace)?, record: PeerRecord::from_signed_envelope( SignedEnvelope::from_protobuf_encoding( ®go @@ -397,9 +458,11 @@ impl TryFrom for Message { r#type: Some(2), unregister: Some(Unregister { ns, .. }), .. - } => Message::Unregister { - namespace: ns.ok_or(ConversionError::MissingNamespace)?, - }, + } => Message::Unregister( + ns.map(Namespace::new) + .transpose()? + .ok_or(ConversionError::MissingNamespace)?, + ), wire::Message { r#type: Some(4), discover_response: @@ -427,6 +490,8 @@ pub enum ConversionError { InconsistentWireMessage, #[error("Missing namespace field")] MissingNamespace, + #[error("Invalid namespace")] + InvalidNamespace(#[from] NamespaceTooLong), #[error("Missing signed peer record field")] MissingSignedPeerRecord, #[error("Missing TTL field")] @@ -458,6 +523,7 @@ impl ConversionError { ConversionError::BadStatusCode => ErrorCode::InternalError, ConversionError::PoWDifficultyOutOfRange => ErrorCode::InternalError, ConversionError::BadPoWHash => ErrorCode::InternalError, + ConversionError::InvalidNamespace(_) => ErrorCode::InvalidNamespace, } } } @@ -519,7 +585,7 @@ mod tests { #[test] fn cookie_wire_encoding_roundtrip() { - let cookie = Cookie::for_namespace("foo".to_owned()); + let cookie = Cookie::for_namespace(Namespace::from_static("foo")); let bytes = cookie.clone().into_wire_encoding(); let parsed = Cookie::from_wire_encoding(bytes).unwrap(); @@ -529,7 +595,7 @@ mod tests { #[test] fn cookie_wire_encoding_length() { - let cookie = Cookie::for_namespace("foo".to_owned()); + let cookie = Cookie::for_namespace(Namespace::from_static("foo")); let bytes = cookie.into_wire_encoding(); diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index ae6759bb988..296cc655574 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,5 +1,5 @@ use crate::codec::{ - self, Cookie, ErrorCode, Message, NewRegistration, Registration, RendezvousCodec, + self, Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, RendezvousCodec, }; use crate::protocol; use crate::substream::{Advance, Next, SubstreamState}; @@ -45,18 +45,13 @@ impl MessageHistory { pub enum OutEvent { RegistrationRequested(NewRegistration), Registered { - namespace: String, + namespace: Namespace, ttl: i64, }, - RegisterFailed { - namespace: String, - error: ErrorCode, - }, - UnregisterRequested { - namespace: String, - }, + RegisterFailed(Namespace, ErrorCode), + UnregisterRequested(Namespace), DiscoverRequested { - namespace: Option, + namespace: Option, cookie: Option, limit: Option, }, @@ -65,7 +60,7 @@ pub enum OutEvent { cookie: Cookie, }, DiscoverFailed { - namespace: Option, + namespace: Option, error: ErrorCode, }, } @@ -74,11 +69,9 @@ pub enum OutEvent { pub enum InEvent { RegisterRequest(NewRegistration), DeclineRegisterRequest(ErrorCode), - UnregisterRequest { - namespace: String, - }, + UnregisterRequest(Namespace), DiscoverRequest { - namespace: Option, + namespace: Option, cookie: Option, limit: Option, }, @@ -188,8 +181,8 @@ impl<'handler> Advance<'handler> for Inbound { namespace, limit, }, - Message::Unregister { namespace } => { - OutEvent::UnregisterRequested { namespace } + Message::Unregister(namespace) => { + OutEvent::UnregisterRequested(namespace) } other => return Err(Error::BadMessage(other)), }; @@ -311,10 +304,7 @@ impl<'handler> Advance<'handler> for Outbound { ttl, }, ([Register(registration), ..], RegisterResponse(Err(error))) => { - RegisterFailed { - namespace: registration.namespace.to_owned(), - error, - } + RegisterFailed(registration.namespace.to_owned(), error) } ([Discover { .. }, ..], DiscoverResponse(Ok((registrations, cookie)))) => { Discovered { @@ -410,9 +400,9 @@ impl ProtocolsHandler for RendezvousHandler { inbound, SubstreamState::Active(Outbound::PendingOpen(Message::Register(reggo))), ), - (InEvent::UnregisterRequest { namespace }, inbound, SubstreamState::None) => ( + (InEvent::UnregisterRequest(namespace), inbound, SubstreamState::None) => ( inbound, - SubstreamState::Active(Outbound::PendingOpen(Message::Unregister { namespace })), + SubstreamState::Active(Outbound::PendingOpen(Message::Unregister(namespace))), ), ( InEvent::DiscoverRequest { diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 13c5c14b791..4d551c17b69 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,5 +1,5 @@ pub use self::behaviour::{Config, Event, RegisterError, Rendezvous}; -pub use self::codec::ErrorCode; +pub use self::codec::{ErrorCode, Namespace, NamespaceTooLong}; pub use codec::Registration; mod behaviour; diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 81151d51c18..71338802bf9 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,6 +1,7 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; +use libp2p::rendezvous::Namespace; use libp2p_core::identity; use libp2p_core::PeerId; use libp2p_rendezvous::{ @@ -13,7 +14,7 @@ async fn given_successful_registration_then_successful_discovery() { let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; - let namespace = "some-namespace".to_string(); + let namespace = Namespace::from_static("some-namespace"); let _ = test.alice @@ -39,7 +40,7 @@ async fn given_successful_registration_then_refresh_ttl() { let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; - let namespace = "some-namespace".to_string(); + let namespace = Namespace::from_static("some-namespace"); let refesh_ttl = 10_000; @@ -86,7 +87,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; - let namespace = "some-namespace".to_string(); + let namespace = Namespace::from_static("some-namespace"); let _ = test.alice.behaviour_mut().register( namespace.clone(), @@ -113,7 +114,7 @@ async fn eve_cannot_register() { let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; - let namespace = "some-namespace".to_string(); + let namespace = Namespace::from_static("some-namespace"); let _ = test.eve.behaviour_mut().register( namespace.clone(), @@ -182,7 +183,7 @@ impl RendezvousTest { pub async fn assert_successful_registration( &mut self, - expected_namespace: String, + expected_namespace: Namespace, expected_ttl: i64, ) { match await_events_or_timeout(&mut self.robert, &mut self.alice).await { @@ -205,7 +206,7 @@ impl RendezvousTest { pub async fn assert_successful_discovery( &mut self, - expected_namespace: String, + expected_namespace: Namespace, expected_ttl: i64, expected_peer_id: PeerId, ) { From e0eaa41369b5b54d00fdd25fd6096b92fc7831fc Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 23 Jun 2021 17:44:28 +1000 Subject: [PATCH 204/242] Don't use latest stable features yet --- protocols/rendezvous/src/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index c3fcbb29b9a..d02c5e03a65 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -213,7 +213,7 @@ impl NetworkBehaviour for Rendezvous { }), ] } - Err(TtlOutOfRange::TooLong { .. } | TtlOutOfRange::TooShort { .. }) => { + Err(TtlOutOfRange::TooLong { .. }) | Err(TtlOutOfRange::TooShort { .. }) => { let error = ErrorCode::InvalidTtl; vec![ From 470587760a457dadfbf4ee01747caf9d95f29387 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 25 Jun 2021 09:59:09 +1000 Subject: [PATCH 205/242] Update to latest revision of spec See https://github.com/libp2p/specs/pull/338. --- protocols/rendezvous/src/behaviour.rs | 38 ++++++++++-------------- protocols/rendezvous/src/codec.rs | 12 ++++---- protocols/rendezvous/src/handler.rs | 9 +++--- protocols/rendezvous/src/lib.rs | 9 +++--- protocols/rendezvous/src/rpc.proto | 6 ++-- protocols/rendezvous/tests/rendezvous.rs | 6 ++-- 6 files changed, 36 insertions(+), 44 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index d02c5e03a65..4790f01c291 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,4 +1,4 @@ -use crate::codec::{Cookie, ErrorCode, Namespace, NewRegistration, Registration}; +use crate::codec::{Cookie, ErrorCode, Namespace, NewRegistration, Registration, Ttl}; use crate::handler::{InEvent, OutEvent, RendezvousHandler}; use crate::{handler, MAX_TTL, MIN_TTL}; use bimap::BiMap; @@ -25,12 +25,12 @@ pub struct Rendezvous { events: VecDeque>, registrations: Registrations, key_pair: Keypair, - pending_register_requests: Vec<(Namespace, PeerId, Option)>, + pending_register_requests: Vec<(Namespace, PeerId, Option)>, } pub struct Config { - min_ttl: i64, - max_ttl: i64, + min_ttl: Ttl, + max_ttl: Ttl, } impl Default for Config { @@ -56,7 +56,7 @@ impl Rendezvous { &mut self, namespace: Namespace, rendezvous_node: PeerId, - ttl: Option, + ttl: Option, ) -> Result<(), RegisterError> { self.pending_register_requests .push((namespace, rendezvous_node, ttl)); @@ -76,7 +76,7 @@ impl Rendezvous { &mut self, ns: Option, cookie: Option, - limit: Option, + limit: Option, rendezvous_node: PeerId, ) { self.events @@ -123,7 +123,7 @@ pub enum Event { /// We successfully registered with the contained rendezvous node. Registered { rendezvous_node: PeerId, - ttl: i64, + ttl: Ttl, namespace: Namespace, }, /// We failed to register with the contained rendezvous node. @@ -392,17 +392,17 @@ pub struct Registrations { registrations_for_peer: BiMap<(PeerId, Namespace), RegistrationId>, registrations: HashMap, cookies: HashMap>, - min_ttl: i64, - max_ttl: i64, + min_ttl: Ttl, + max_ttl: Ttl, next_expiry: FuturesUnordered>, } #[derive(Debug, thiserror::Error)] pub enum TtlOutOfRange { #[error("Requested TTL ({requested}s) is too long; max {bound}s")] - TooLong { bound: i64, requested: i64 }, + TooLong { bound: Ttl, requested: Ttl }, #[error("Requested TTL ({requested}s) is too short; min {bound}s")] - TooShort { bound: i64, requested: i64 }, + TooShort { bound: Ttl, requested: Ttl }, } impl Default for Registrations { @@ -487,7 +487,7 @@ impl Registrations { &mut self, discover_namespace: Option, cookie: Option, - mut limit: Option, + limit: Option, ) -> Result<(impl Iterator + '_, Cookie), CookieNamespaceMismatch> { let cookie_namespace = cookie.as_ref().and_then(|cookie| cookie.namespace()); @@ -525,15 +525,7 @@ impl Registrations { } } }) - .take_while(|_| { - let limit = match limit.as_mut() { - None => return true, - Some(limit) => limit, - }; - *limit -= 1; - - *limit >= 0 - }) + .take(limit.unwrap_or(u64::MAX) as usize) .cloned() .collect::>(); @@ -805,7 +797,7 @@ mod tests { new_registration(namespace, identity, None) } - fn new_dummy_registration_with_ttl(namespace: &'static str, ttl: i64) -> NewRegistration { + fn new_dummy_registration_with_ttl(namespace: &'static str, ttl: Ttl) -> NewRegistration { let identity = identity::Keypair::generate_ed25519(); new_registration(namespace, identity, Some(ttl)) @@ -814,7 +806,7 @@ mod tests { fn new_registration( namespace: &'static str, identity: identity::Keypair, - ttl: Option, + ttl: Option, ) -> NewRegistration { NewRegistration::new( Namespace::from_static(namespace), diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index f3359011b38..ad8078eadcb 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -6,7 +6,7 @@ use std::fmt; use unsigned_varint::codec::UviBytes; use uuid::Uuid; -pub type Ttl = i64; +pub type Ttl = u64; #[derive(Debug, Clone)] pub enum Message { @@ -16,7 +16,7 @@ pub enum Message { Discover { namespace: Option, cookie: Option, - limit: Option, + limit: Option, }, DiscoverResponse(Result<(Vec, Cookie), ErrorCode>), } @@ -146,11 +146,11 @@ pub struct InvalidCookie; pub struct NewRegistration { pub namespace: Namespace, pub record: PeerRecord, - pub ttl: Option, + pub ttl: Option, } impl NewRegistration { - pub fn new(namespace: Namespace, record: PeerRecord, ttl: Option) -> Self { + pub fn new(namespace: Namespace, record: PeerRecord, ttl: Option) -> Self { Self { namespace, record, @@ -158,7 +158,7 @@ impl NewRegistration { } } - pub fn effective_ttl(&self) -> i64 { + pub fn effective_ttl(&self) -> Ttl { self.ttl.unwrap_or(DEFAULT_TTL) } } @@ -167,7 +167,7 @@ impl NewRegistration { pub struct Registration { pub namespace: Namespace, pub record: PeerRecord, - pub ttl: i64, + pub ttl: Ttl, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 296cc655574..0db1cb48878 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,5 +1,6 @@ use crate::codec::{ self, Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, RendezvousCodec, + Ttl, }; use crate::protocol; use crate::substream::{Advance, Next, SubstreamState}; @@ -46,14 +47,14 @@ pub enum OutEvent { RegistrationRequested(NewRegistration), Registered { namespace: Namespace, - ttl: i64, + ttl: Ttl, }, RegisterFailed(Namespace, ErrorCode), UnregisterRequested(Namespace), DiscoverRequested { namespace: Option, cookie: Option, - limit: Option, + limit: Option, }, Discovered { registrations: Vec, @@ -73,10 +74,10 @@ pub enum InEvent { DiscoverRequest { namespace: Option, cookie: Option, - limit: Option, + limit: Option, }, RegisterResponse { - ttl: i64, + ttl: Ttl, }, DiscoverResponse { discovered: Vec, diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index 4d551c17b69..c5da2b0cf06 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,6 +1,5 @@ pub use self::behaviour::{Config, Event, RegisterError, Rendezvous}; -pub use self::codec::{ErrorCode, Namespace, NamespaceTooLong}; -pub use codec::Registration; +pub use self::codec::{ErrorCode, Namespace, NamespaceTooLong, Registration, Ttl}; mod behaviour; mod codec; @@ -11,14 +10,14 @@ mod substream; /// If unspecified, rendezvous nodes should assume a TTL of 2h. /// /// See https://github.com/libp2p/specs/blob/d21418638d5f09f2a4e5a1ceca17058df134a300/rendezvous/README.md#L116-L117. -pub const DEFAULT_TTL: i64 = 60 * 60 * 2; +pub const DEFAULT_TTL: Ttl = 60 * 60 * 2; /// By default, nodes should require a minimum TTL of 2h /// /// https://github.com/libp2p/specs/tree/master/rendezvous#recommendations-for-rendezvous-points-configurations. -pub const MIN_TTL: i64 = 60 * 60 * 2; +pub const MIN_TTL: Ttl = 60 * 60 * 2; /// By default, nodes should allow a maximum TTL of 72h /// /// https://github.com/libp2p/specs/tree/master/rendezvous#recommendations-for-rendezvous-points-configurations. -pub const MAX_TTL: i64 = 60 * 60 * 72; +pub const MAX_TTL: Ttl = 60 * 60 * 72; diff --git a/protocols/rendezvous/src/rpc.proto b/protocols/rendezvous/src/rpc.proto index f3cbd9505ff..d4e388ca96e 100644 --- a/protocols/rendezvous/src/rpc.proto +++ b/protocols/rendezvous/src/rpc.proto @@ -25,13 +25,13 @@ message Message { message Register { optional string ns = 1; optional bytes signedPeerRecord = 2; - optional int64 ttl = 3; // in seconds + optional uint64 ttl = 3; // in seconds } message RegisterResponse { optional ResponseStatus status = 1; optional string statusText = 2; - optional int64 ttl = 3; // in seconds + optional uint64 ttl = 3; // in seconds } message Unregister { @@ -41,7 +41,7 @@ message Message { message Discover { optional string ns = 1; - optional int64 limit = 2; + optional uint64 limit = 2; optional bytes cookie = 3; } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 71338802bf9..a7c8edb5249 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,7 +1,7 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; -use libp2p::rendezvous::Namespace; +use libp2p::rendezvous::{Namespace, Ttl}; use libp2p_core::identity; use libp2p_core::PeerId; use libp2p_rendezvous::{ @@ -184,7 +184,7 @@ impl RendezvousTest { pub async fn assert_successful_registration( &mut self, expected_namespace: Namespace, - expected_ttl: i64, + expected_ttl: Ttl, ) { match await_events_or_timeout(&mut self.robert, &mut self.alice).await { ( @@ -207,7 +207,7 @@ impl RendezvousTest { pub async fn assert_successful_discovery( &mut self, expected_namespace: Namespace, - expected_ttl: i64, + expected_ttl: Ttl, expected_peer_id: PeerId, ) { match await_events_or_timeout(&mut self.robert, &mut self.bob).await { From c3cd411cedb12eab72ba26b08c43c2467a8fd8e9 Mon Sep 17 00:00:00 2001 From: rishflab Date: Fri, 25 Jun 2021 11:54:56 +1000 Subject: [PATCH 206/242] Remove snake case to be consistent with type --- protocols/rendezvous/src/behaviour.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 4790f01c291..61fb76912f4 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -24,7 +24,7 @@ use uuid::Uuid; pub struct Rendezvous { events: VecDeque>, registrations: Registrations, - key_pair: Keypair, + keypair: Keypair, pending_register_requests: Vec<(Namespace, PeerId, Option)>, } @@ -43,11 +43,11 @@ impl Default for Config { } impl Rendezvous { - pub fn new(key_pair: Keypair, config: Config) -> Self { + pub fn new(keypair: Keypair, config: Config) -> Self { Self { events: Default::default(), registrations: Registrations::with_config(config), - key_pair, + keypair, pending_register_requests: vec![], } } @@ -354,7 +354,7 @@ impl NetworkBehaviour for Rendezvous { )); } - let action = match PeerRecord::new(self.key_pair.clone(), external_addresses) { + let action = match PeerRecord::new(self.keypair.clone(), external_addresses) { Ok(peer_record) => NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, event: InEvent::RegisterRequest(NewRegistration { From 2e143a01aebbbf318231e7841884f9f34638c4e2 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Thu, 1 Jul 2021 11:01:22 +1000 Subject: [PATCH 207/242] Expose Config params --- protocols/rendezvous/src/behaviour.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 61fb76912f4..e6c9e43b897 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -33,6 +33,18 @@ pub struct Config { max_ttl: Ttl, } +impl Config { + pub fn with_min_ttl(mut self, min_ttl: Ttl) -> Self { + self.min_ttl = min_ttl; + self + } + + pub fn with_max_ttl(mut self, max_ttl: Ttl) -> Self { + self.max_ttl = max_ttl; + self + } +} + impl Default for Config { fn default() -> Self { Self { From 8c3b01092599a127bfe22661d1fd7a9c91fe5939 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 1 Jul 2021 11:32:22 +1000 Subject: [PATCH 208/242] Fix typo in test name --- protocols/rendezvous/src/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index e6c9e43b897..36f8fe3d219 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -744,7 +744,7 @@ mod tests { /// is initialised with a future that always returns pending. This test ensures that /// FuturesUnordered does not stop polling for ready futures. #[tokio::test] - async fn given_all_registrations_expired_then_succesfully_handle_new_registration_and_expiry() { + async fn given_all_registrations_expired_then_successfully_handle_new_registration_and_expiry() { let mut registrations = Registrations::with_config(Config { min_ttl: 0, max_ttl: 10, From 622484de34e123897445df1f249a434fe9d59d1a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 1 Jul 2021 11:44:11 +1000 Subject: [PATCH 209/242] Use `wasm-timer` instead of `tokio` This should allow us to compile on wasm and also removes the need to mark some tests as `async` even though they don't use `await`. --- protocols/rendezvous/Cargo.toml | 2 +- protocols/rendezvous/src/behaviour.rs | 48 +++++++++++++++------------ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index dfc8c4bf736..18bea813b30 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -23,7 +23,7 @@ uuid = { version = "0.8", features = ["v4"] } bimap = "0.6.1" sha2 = "0.9" rand = "0.8" -tokio = { version = "1", features = [ "time" ] } +wasm-timer = "0.2" [dev-dependencies] libp2p = { path = "../.." } diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 36f8fe3d219..bd659f3ad67 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -18,7 +18,6 @@ use std::iter::FromIterator; use std::option::Option::None; use std::task::{Context, Poll}; use std::time::Duration; -use tokio::time::sleep; use uuid::Uuid; pub struct Rendezvous { @@ -476,8 +475,14 @@ impl Registrations { self.registrations .insert(registration_id, registration.clone()); - let next_expiry = sleep(Duration::from_secs(ttl as u64)) - .map(move |()| registration_id) + let next_expiry = wasm_timer::Delay::new(Duration::from_secs(ttl as u64)) + .map(move |result| { + if result.is_err() { + log::warn!("Timer for registration {} has unexpectedly errored, treating it as expired", registration_id.0); + } + + registration_id + }) .boxed(); self.next_expiry.push(next_expiry); @@ -590,8 +595,8 @@ mod tests { use std::option::Option::None; use std::time::SystemTime; - #[tokio::test] - async fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { + #[test] + fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -603,8 +608,8 @@ mod tests { assert_eq!(subsequent_discover.count(), 0); } - #[tokio::test] - async fn given_registrations_when_discover_all_then_all_are_returned() { + #[test] + fn given_registrations_when_discover_all_then_all_are_returned() { let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -614,9 +619,9 @@ mod tests { assert_eq!(discover.count(), 2); } - #[tokio::test] - async fn given_registrations_when_discover_only_for_specific_namespace_then_only_those_are_returned( - ) { + #[test] + fn given_registrations_when_discover_only_for_specific_namespace_then_only_those_are_returned() + { let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); @@ -631,8 +636,8 @@ mod tests { ); } - #[tokio::test] - async fn given_reregistration_old_registration_is_discarded() { + #[test] + fn given_reregistration_old_registration_is_discarded() { let alice = identity::Keypair::generate_ed25519(); let mut registrations = Registrations::default(); registrations @@ -652,8 +657,8 @@ mod tests { ); } - #[tokio::test] - async fn given_cookie_from_2nd_discover_does_not_return_nodes_from_first_discover() { + #[test] + fn given_cookie_from_2nd_discover_does_not_return_nodes_from_first_discover() { let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -668,8 +673,8 @@ mod tests { assert_eq!(subsequent_discover.count(), 0); } - #[tokio::test] - async fn cookie_from_different_discover_request_is_not_valid() { + #[test] + fn cookie_from_different_discover_request_is_not_valid() { let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("bar")).unwrap(); @@ -744,7 +749,8 @@ mod tests { /// is initialised with a future that always returns pending. This test ensures that /// FuturesUnordered does not stop polling for ready futures. #[tokio::test] - async fn given_all_registrations_expired_then_successfully_handle_new_registration_and_expiry() { + async fn given_all_registrations_expired_then_successfully_handle_new_registration_and_expiry() + { let mut registrations = Registrations::with_config(Config { min_ttl: 0, max_ttl: 10, @@ -779,8 +785,8 @@ mod tests { assert_eq!(registrations.cookies.len(), 0); } - #[tokio::test] - async fn given_limit_discover_only_returns_n_results() { + #[test] + fn given_limit_discover_only_returns_n_results() { let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); @@ -790,8 +796,8 @@ mod tests { assert_eq!(registrations.count(), 1); } - #[tokio::test] - async fn given_limit_cookie_can_be_used_for_pagination() { + #[test] + fn given_limit_cookie_can_be_used_for_pagination() { let mut registrations = Registrations::default(); registrations.add(new_dummy_registration("foo")).unwrap(); registrations.add(new_dummy_registration("foo")).unwrap(); From b4a66bddfabd3f5ff7c2c72d05f2d1ca72f85629 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 1 Jul 2021 11:46:10 +1000 Subject: [PATCH 210/242] Add copyright header to all files --- protocols/rendezvous/examples/discover.rs | 20 +++++++++++++++++++ protocols/rendezvous/examples/register.rs | 20 +++++++++++++++++++ .../rendezvous/examples/rendezvous_point.rs | 20 +++++++++++++++++++ protocols/rendezvous/src/behaviour.rs | 20 +++++++++++++++++++ protocols/rendezvous/src/codec.rs | 20 +++++++++++++++++++ protocols/rendezvous/src/handler.rs | 20 +++++++++++++++++++ protocols/rendezvous/src/lib.rs | 20 +++++++++++++++++++ protocols/rendezvous/src/protocol.rs | 20 +++++++++++++++++++ protocols/rendezvous/src/substream.rs | 20 +++++++++++++++++++ protocols/rendezvous/tests/harness/mod.rs | 20 +++++++++++++++++++ protocols/rendezvous/tests/rendezvous.rs | 20 +++++++++++++++++++ 11 files changed, 220 insertions(+) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 317b8e1920c..7cf60dac998 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -1,3 +1,23 @@ +// 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 futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index a63b1755320..643f46eb9b6 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -1,3 +1,23 @@ +// 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 futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; diff --git a/protocols/rendezvous/examples/rendezvous_point.rs b/protocols/rendezvous/examples/rendezvous_point.rs index 9066a58c833..93bddef4ed0 100644 --- a/protocols/rendezvous/examples/rendezvous_point.rs +++ b/protocols/rendezvous/examples/rendezvous_point.rs @@ -1,3 +1,23 @@ +// 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 futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index bd659f3ad67..c765af71a70 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -1,3 +1,23 @@ +// 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, Namespace, NewRegistration, Registration, Ttl}; use crate::handler::{InEvent, OutEvent, RendezvousHandler}; use crate::{handler, MAX_TTL, MIN_TTL}; diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index ad8078eadcb..55cc358f6d7 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -1,3 +1,23 @@ +// 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::DEFAULT_TTL; use asynchronous_codec::{Bytes, BytesMut, Decoder, Encoder}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 0db1cb48878..177e1af574b 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -1,3 +1,23 @@ +// 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::{ self, Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, RendezvousCodec, Ttl, diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index c5da2b0cf06..f431305f53a 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -1,3 +1,23 @@ +// 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. + pub use self::behaviour::{Config, Event, RegisterError, Rendezvous}; pub use self::codec::{ErrorCode, Namespace, NamespaceTooLong, Registration, Ttl}; diff --git a/protocols/rendezvous/src/protocol.rs b/protocols/rendezvous/src/protocol.rs index c3b4c6b60bc..32bd12d54b9 100644 --- a/protocols/rendezvous/src/protocol.rs +++ b/protocols/rendezvous/src/protocol.rs @@ -1,3 +1,23 @@ +// 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::RendezvousCodec; use asynchronous_codec::Framed; use libp2p_core::upgrade::FromFnUpgrade; diff --git a/protocols/rendezvous/src/substream.rs b/protocols/rendezvous/src/substream.rs index 249809cf7f7..3dea3bbd15e 100644 --- a/protocols/rendezvous/src/substream.rs +++ b/protocols/rendezvous/src/substream.rs @@ -1,3 +1,23 @@ +// 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 libp2p_swarm::{ProtocolsHandlerEvent, SubstreamProtocol}; use std::mem; use std::task::{Context, Poll}; diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index ce7d8546fdc..e0a4bb3661b 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -1,3 +1,23 @@ +// 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 async_trait::async_trait; use futures::stream::FusedStream; use futures::Future; diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index a7c8edb5249..d19bf94a016 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -1,3 +1,23 @@ +// 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. + pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; From 0a1613627ae7e1ca71d0f26f6e3f70c9dd75a0c5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 1 Jul 2021 11:47:05 +1000 Subject: [PATCH 211/242] Replace log with debug_assert --- protocols/rendezvous/src/handler.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 177e1af574b..456bf4dc9fe 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -486,11 +486,10 @@ impl ProtocolsHandler for RendezvousHandler { outbound, ), (event, inbound, outbound) => { - log::warn!( + debug_assert!( + false, "Neither {:?} nor {:?} can handle {:?}", - inbound, - outbound, - event + inbound, outbound, event ); (inbound, outbound) From 4cacaf215b334d447faca8f4e361fe19c30d2162 Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 1 Jul 2021 16:53:01 +1000 Subject: [PATCH 212/242] Remove redundant result return type from register --- protocols/rendezvous/src/behaviour.rs | 8 +------- protocols/rendezvous/tests/rendezvous.rs | 6 +++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index c765af71a70..78d5b0a6049 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -83,15 +83,9 @@ impl Rendezvous { } } - pub fn register( - &mut self, - namespace: Namespace, - rendezvous_node: PeerId, - ttl: Option, - ) -> Result<(), RegisterError> { + pub fn register(&mut self, namespace: Namespace, rendezvous_node: PeerId, ttl: Option) { self.pending_register_requests .push((namespace, rendezvous_node, ttl)); - Ok(()) } pub fn unregister(&mut self, namespace: Namespace, rendezvous_node: PeerId) { diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index d19bf94a016..91c08f06405 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -82,7 +82,7 @@ async fn given_successful_registration_then_refresh_ttl() { test.assert_successful_discovery(namespace.clone(), DEFAULT_TTL, *test.alice.local_peer_id()) .await; - let _ = test.alice.behaviour_mut().register( + test.alice.behaviour_mut().register( namespace.clone(), *test.robert.local_peer_id(), Some(refesh_ttl), @@ -109,7 +109,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { let namespace = Namespace::from_static("some-namespace"); - let _ = test.alice.behaviour_mut().register( + test.alice.behaviour_mut().register( namespace.clone(), *test.robert.local_peer_id(), Some(100_000_000), @@ -136,7 +136,7 @@ async fn eve_cannot_register() { let namespace = Namespace::from_static("some-namespace"); - let _ = test.eve.behaviour_mut().register( + test.eve.behaviour_mut().register( namespace.clone(), *test.robert.local_peer_id(), Some(100_000), From 952fde2e23e6c0e8fa7f83470f677933bdbcddd0 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Thu, 1 Jul 2021 18:10:06 +1000 Subject: [PATCH 213/242] Remove unwrap from example to fix build --- protocols/rendezvous/examples/register.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 643f46eb9b6..bde10ccffe8 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -78,8 +78,7 @@ async fn main() { swarm .behaviour_mut() .rendezvous - .register(Namespace::from_static("rendezvous"), rendezvous_point, None) - .unwrap(); + .register(Namespace::from_static("rendezvous"), rendezvous_point, None); } SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Registered { namespace, From da0e7614b06e63b8051b9bfa6c34435687160736 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 1 Jul 2021 18:06:44 +1000 Subject: [PATCH 214/242] Redefine rendezvous behaviour using generic substream handler --- protocols/rendezvous/src/behaviour.rs | 191 ++++--- protocols/rendezvous/src/handler.rs | 510 +----------------- protocols/rendezvous/src/handler/inbound.rs | 181 +++++++ protocols/rendezvous/src/handler/outbound.rs | 213 ++++++++ protocols/rendezvous/src/lib.rs | 3 +- protocols/rendezvous/src/protocol.rs | 48 -- protocols/rendezvous/src/substream.rs | 124 ----- protocols/rendezvous/src/substream_handler.rs | 412 ++++++++++++++ 8 files changed, 939 insertions(+), 743 deletions(-) create mode 100644 protocols/rendezvous/src/handler/inbound.rs create mode 100644 protocols/rendezvous/src/handler/outbound.rs delete mode 100644 protocols/rendezvous/src/protocol.rs delete mode 100644 protocols/rendezvous/src/substream.rs create mode 100644 protocols/rendezvous/src/substream_handler.rs diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 78d5b0a6049..9077484fe07 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -19,8 +19,10 @@ // DEALINGS IN THE SOFTWARE. use crate::codec::{Cookie, ErrorCode, Namespace, NewRegistration, Registration, Ttl}; -use crate::handler::{InEvent, OutEvent, RendezvousHandler}; -use crate::{handler, MAX_TTL, MIN_TTL}; +use crate::handler::outbound::OpenInfo; +use crate::handler::{inbound, outbound}; +use crate::substream_handler::SubstreamProtocolsHandler; +use crate::{substream_handler, MAX_TTL, MIN_TTL}; use bimap::BiMap; use futures::future::BoxFuture; use futures::ready; @@ -35,13 +37,15 @@ use libp2p_swarm::{ }; use std::collections::{HashMap, HashSet, VecDeque}; use std::iter::FromIterator; -use std::option::Option::None; use std::task::{Context, Poll}; use std::time::Duration; use uuid::Uuid; +use void::Void; pub struct Rendezvous { - events: VecDeque>, + events: VecDeque< + NetworkBehaviourAction, Event>, + >, registrations: Registrations, keypair: Keypair, pending_register_requests: Vec<(Namespace, PeerId, Option)>, @@ -92,7 +96,9 @@ impl Rendezvous { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: InEvent::UnregisterRequest(namespace), + event: substream_handler::InEvent::NewSubstream { + open_info: OpenInfo::UnregisterRequest(namespace), + }, handler: NotifyHandler::Any, }); } @@ -107,10 +113,12 @@ impl Rendezvous { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: InEvent::DiscoverRequest { - namespace: ns, - cookie, - limit, + event: substream_handler::InEvent::NewSubstream { + open_info: OpenInfo::DiscoverRequest { + namespace: ns, + cookie, + limit, + }, }, handler: NotifyHandler::Any, }); @@ -178,11 +186,12 @@ pub enum Event { } impl NetworkBehaviour for Rendezvous { - type ProtocolsHandler = RendezvousHandler; + type ProtocolsHandler = + SubstreamProtocolsHandler; type OutEvent = Event; fn new_handler(&mut self) -> Self::ProtocolsHandler { - RendezvousHandler::default() + SubstreamProtocolsHandler::new(b"/rendezvous/1.0.0") } fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { @@ -197,20 +206,29 @@ impl NetworkBehaviour for Rendezvous { &mut self, peer_id: PeerId, connection: ConnectionId, - event: handler::OutEvent, + event: substream_handler::OutEvent< + inbound::OutEvent, + outbound::OutEvent, + crate::handler::Error, + crate::handler::Error, + >, ) { let new_events = match event { // bad registration - OutEvent::RegistrationRequested(registration) - if registration.record.peer_id() != peer_id => - { + substream_handler::OutEvent::InboundEvent { + id, + message: inbound::OutEvent::RegistrationRequested(registration), + } if registration.record.peer_id() != peer_id => { let error = ErrorCode::NotAuthorized; vec![ NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: InEvent::DeclineRegisterRequest(error), + event: substream_handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::DeclineRegisterRequest(error), + }, }, NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { peer: peer_id, @@ -219,7 +237,10 @@ impl NetworkBehaviour for Rendezvous { }), ] } - OutEvent::RegistrationRequested(registration) => { + substream_handler::OutEvent::InboundEvent { + id, + message: inbound::OutEvent::RegistrationRequested(registration), + } => { let namespace = registration.namespace.clone(); match self.registrations.add(registration) { @@ -228,8 +249,11 @@ impl NetworkBehaviour for Rendezvous { NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: InEvent::RegisterResponse { - ttl: registration.ttl, + event: substream_handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::RegisterResponse { + ttl: registration.ttl, + }, }, }, NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { @@ -245,7 +269,10 @@ impl NetworkBehaviour for Rendezvous { NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: InEvent::DeclineRegisterRequest(error), + event: substream_handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::DeclineRegisterRequest(error), + }, }, NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { peer: peer_id, @@ -256,36 +283,14 @@ impl NetworkBehaviour for Rendezvous { } } } - OutEvent::Registered { namespace, ttl } => { - vec![NetworkBehaviourAction::GenerateEvent(Event::Registered { - rendezvous_node: peer_id, - ttl, - namespace, - })] - } - OutEvent::RegisterFailed(namespace, error) => { - vec![NetworkBehaviourAction::GenerateEvent( - Event::RegisterFailed(RegisterError::Remote { - rendezvous_node: peer_id, - namespace, - error, - }), - )] - } - OutEvent::UnregisterRequested(namespace) => { - self.registrations.remove(namespace.clone(), peer_id); - - vec![NetworkBehaviourAction::GenerateEvent( - Event::PeerUnregistered { - peer: peer_id, + substream_handler::OutEvent::InboundEvent { + id, + message: + inbound::OutEvent::DiscoverRequested { namespace, + cookie, + limit, }, - )] - } - OutEvent::DiscoverRequested { - namespace, - cookie, - limit, } => match self.registrations.get(namespace, cookie, limit) { Ok((registrations, cookie)) => { let discovered = registrations.cloned().collect::>(); @@ -294,9 +299,12 @@ impl NetworkBehaviour for Rendezvous { NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: InEvent::DiscoverResponse { - discovered: discovered.clone(), - cookie, + event: substream_handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::DiscoverResponse { + discovered: discovered.clone(), + cookie, + }, }, }, NetworkBehaviourAction::GenerateEvent(Event::DiscoverServed { @@ -312,7 +320,10 @@ impl NetworkBehaviour for Rendezvous { NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: InEvent::DeclineDiscoverRequest(error), + event: substream_handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::DeclineDiscoverRequest(error), + }, }, NetworkBehaviourAction::GenerateEvent(Event::DiscoverNotServed { enquirer: peer_id, @@ -321,9 +332,48 @@ impl NetworkBehaviour for Rendezvous { ] } }, - OutEvent::Discovered { - registrations, - cookie, + substream_handler::OutEvent::InboundEvent { + message: inbound::OutEvent::UnregisterRequested(namespace), + .. + } => { + self.registrations.remove(namespace.clone(), peer_id); + + vec![NetworkBehaviourAction::GenerateEvent( + Event::PeerUnregistered { + peer: peer_id, + namespace, + }, + )] + } + substream_handler::OutEvent::OutboundEvent { + message: outbound::OutEvent::Registered { namespace, ttl }, + .. + } => { + vec![NetworkBehaviourAction::GenerateEvent(Event::Registered { + rendezvous_node: peer_id, + ttl, + namespace, + })] + } + substream_handler::OutEvent::OutboundEvent { + message: outbound::OutEvent::RegisterFailed(namespace, error), + .. + } => { + vec![NetworkBehaviourAction::GenerateEvent( + Event::RegisterFailed(RegisterError::Remote { + rendezvous_node: peer_id, + namespace, + error, + }), + )] + } + substream_handler::OutEvent::OutboundEvent { + message: + outbound::OutEvent::Discovered { + registrations, + cookie, + }, + .. } => { vec![NetworkBehaviourAction::GenerateEvent(Event::Discovered { rendezvous_node: peer_id, @@ -331,7 +381,10 @@ impl NetworkBehaviour for Rendezvous { cookie, })] } - OutEvent::DiscoverFailed { namespace, error } => { + substream_handler::OutEvent::OutboundEvent { + message: outbound::OutEvent::DiscoverFailed { namespace, error }, + .. + } => { vec![NetworkBehaviourAction::GenerateEvent( Event::DiscoverFailed { rendezvous_node: peer_id, @@ -340,6 +393,14 @@ impl NetworkBehaviour for Rendezvous { }, )] } + substream_handler::OutEvent::InboundError { .. } => { + // TODO: log errors and close connection? + vec![] + } + substream_handler::OutEvent::OutboundError { .. } => { + // TODO: log errors and close connection? + vec![] + } }; self.events.extend(new_events); @@ -382,11 +443,13 @@ impl NetworkBehaviour for Rendezvous { let action = match PeerRecord::new(self.keypair.clone(), external_addresses) { Ok(peer_record) => NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: InEvent::RegisterRequest(NewRegistration { - namespace, - record: peer_record, - ttl, - }), + event: substream_handler::InEvent::NewSubstream { + open_info: OpenInfo::RegisterRequest(NewRegistration { + namespace, + record: peer_record, + ttl, + }), + }, handler: NotifyHandler::Any, }, Err(signing_error) => NetworkBehaviourAction::GenerateEvent(Event::RegisterFailed( @@ -604,11 +667,13 @@ pub struct CookieNamespaceMismatch; #[cfg(test)] mod tests { - use super::*; - use libp2p_core::identity; use std::option::Option::None; use std::time::SystemTime; + use libp2p_core::identity; + + use super::*; + #[test] fn given_cookie_from_discover_when_discover_again_then_only_get_diff() { let mut registrations = Registrations::default(); diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 456bf4dc9fe..ed89efec74f 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -18,148 +18,11 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::codec::{ - self, Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, RendezvousCodec, - Ttl, -}; -use crate::protocol; -use crate::substream::{Advance, Next, SubstreamState}; -use asynchronous_codec::Framed; -use futures::{SinkExt, StreamExt}; -use libp2p_core::{InboundUpgrade, OutboundUpgrade}; -use libp2p_swarm::{ - KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, - ProtocolsHandlerUpgrErr, SubstreamProtocol, -}; -use std::task::{Context, Poll}; -use std::{fmt, mem}; -use void::Void; +use crate::codec; +use crate::codec::Message; -pub struct RendezvousHandler { - outbound: SubstreamState, - outbound_history: MessageHistory, - inbound: SubstreamState, -} - -impl Default for RendezvousHandler { - fn default() -> Self { - Self { - outbound: SubstreamState::None, - outbound_history: Default::default(), - inbound: SubstreamState::None, - } - } -} - -#[derive(Default)] -struct MessageHistory { - sent: Vec, -} - -impl MessageHistory { - fn clear(&mut self) { - self.sent.clear(); - } -} - -#[derive(Debug, Clone)] -pub enum OutEvent { - RegistrationRequested(NewRegistration), - Registered { - namespace: Namespace, - ttl: Ttl, - }, - RegisterFailed(Namespace, ErrorCode), - UnregisterRequested(Namespace), - DiscoverRequested { - namespace: Option, - cookie: Option, - limit: Option, - }, - Discovered { - registrations: Vec, - cookie: Cookie, - }, - DiscoverFailed { - namespace: Option, - error: ErrorCode, - }, -} - -#[derive(Debug)] -pub enum InEvent { - RegisterRequest(NewRegistration), - DeclineRegisterRequest(ErrorCode), - UnregisterRequest(Namespace), - DiscoverRequest { - namespace: Option, - cookie: Option, - limit: Option, - }, - RegisterResponse { - ttl: Ttl, - }, - DiscoverResponse { - discovered: Vec, - cookie: Cookie, - }, - DeclineDiscoverRequest(ErrorCode), -} - -/// The state of an inbound substream (i.e. the remote node opened it). -enum Inbound { - /// 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 Inbound { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Inbound::PendingRead(_) => write!(f, "Inbound::PendingRead"), - Inbound::PendingBehaviour(_) => write!(f, "Inbound::PendingBehaviour"), - Inbound::PendingSend(_, _) => write!(f, "Inbound::PendingSend"), - Inbound::PendingClose(_) => write!(f, "Inbound::PendingClose"), - } - } -} - -/// The state of an outbound substream (i.e. we opened it). -enum Outbound { - /// We got a message to send from the behaviour. - PendingOpen(Message), - /// We've requested a substream and are waiting for it to be negotiated. - PendingNegotiate, - /// We got the substream, now we need to send the message. - PendingSend { - substream: Framed, - to_send: Message, - }, - /// We sent the message, now we need to flush the data out. - PendingFlush(Framed), - /// We are waiting for the response from the remote. - PendingRemote(Framed), - /// We are closing down the substream. - PendingClose(Framed), -} - -impl fmt::Debug for Outbound { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Outbound::PendingOpen(_) => write!(f, "Outbound::PendingOpen"), - Outbound::PendingNegotiate => write!(f, "Outbound::PendingNegotiate"), - Outbound::PendingSend { .. } => write!(f, "Outbound::PendingSend"), - Outbound::PendingFlush(_) => write!(f, "Outbound::PendingFlush"), - Outbound::PendingRemote(_) => write!(f, "Outbound::PendingRemote"), - Outbound::PendingClose(_) => write!(f, "Outbound::PendingClose"), - } - } -} +pub mod inbound; +pub mod outbound; /// Errors that can occur while interacting with a substream. #[derive(Debug, thiserror::Error)] @@ -173,368 +36,3 @@ pub enum Error { #[error("Substream ended unexpectedly mid-protocol")] UnexpectedEndOfStream, } - -impl<'handler> Advance<'handler> for Inbound { - type Event = OutEvent; - type Params = (); - type Error = Error; - type Protocol = SubstreamProtocol; - - fn advance( - self, - cx: &mut Context<'_>, - _: &mut Self::Params, - ) -> Result, Self::Error> { - Ok(match self { - Inbound::PendingRead(mut substream) => { - match substream.poll_next_unpin(cx).map_err(Error::ReadMessage)? { - Poll::Ready(Some(msg)) => { - let event = match msg.clone() { - 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: Inbound::PendingBehaviour(substream), - } - } - Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), - Poll::Pending => Next::Pending { - next_state: Inbound::PendingRead(substream), - }, - } - } - Inbound::PendingBehaviour(substream) => Next::Pending { - next_state: Inbound::PendingBehaviour(substream), - }, - Inbound::PendingSend(mut substream, message) => match substream - .poll_ready_unpin(cx) - .map_err(Error::WriteMessage)? - { - Poll::Ready(()) => { - substream - .start_send_unpin(message.clone()) - .map_err(Error::WriteMessage)?; - - Next::Continue { - next_state: Inbound::PendingClose(substream), - } - } - Poll::Pending => Next::Pending { - next_state: Inbound::PendingSend(substream, message), - }, - }, - Inbound::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: Inbound::PendingClose(substream), - }, - }, - }) - } -} - -struct OutboundPollParams<'handler> { - history: &'handler mut MessageHistory, -} - -impl<'handler> Advance<'handler> for Outbound { - type Event = OutEvent; - type Params = OutboundPollParams<'handler>; - type Error = Error; - type Protocol = SubstreamProtocol; - - fn advance( - self, - cx: &mut Context<'_>, - OutboundPollParams { history }: &mut OutboundPollParams, - ) -> Result, Self::Error> { - Ok(match self { - Outbound::PendingOpen(msg) => Next::OpenSubstream { - protocol: SubstreamProtocol::new(protocol::new(), msg), - next_state: Outbound::PendingNegotiate, - }, - Outbound::PendingNegotiate => Next::Pending { - next_state: Outbound::PendingNegotiate, - }, - Outbound::PendingSend { - mut substream, - to_send: message, - } => match substream - .poll_ready_unpin(cx) - .map_err(Error::WriteMessage)? - { - Poll::Ready(()) => { - substream - .start_send_unpin(message.clone()) - .map_err(Error::WriteMessage)?; - history.sent.push(message); - - Next::Continue { - next_state: Outbound::PendingFlush(substream), - } - } - Poll::Pending => Next::Pending { - next_state: Outbound::PendingSend { - substream, - to_send: message, - }, - }, - }, - Outbound::PendingFlush(mut substream) => match substream - .poll_flush_unpin(cx) - .map_err(Error::WriteMessage)? - { - Poll::Ready(()) => Next::Continue { - next_state: Outbound::PendingRemote(substream), - }, - Poll::Pending => Next::Pending { - next_state: Outbound::PendingFlush(substream), - }, - }, - Outbound::PendingRemote(mut substream) => match substream - .poll_next_unpin(cx) - .map_err(Error::ReadMessage)? - { - Poll::Ready(Some(received_message)) => { - use Message::*; - use OutEvent::*; - - // Absolutely amazing Rust pattern matching ahead! - // We match against the slice of historical messages and the received message. - // [, ..] effectively matches against the first message that we sent on this substream - let event = match (history.sent.as_slice(), received_message) { - ([Register(registration), ..], RegisterResponse(Ok(ttl))) => Registered { - namespace: registration.namespace.to_owned(), - ttl, - }, - ([Register(registration), ..], RegisterResponse(Err(error))) => { - RegisterFailed(registration.namespace.to_owned(), error) - } - ([Discover { .. }, ..], DiscoverResponse(Ok((registrations, cookie)))) => { - Discovered { - registrations, - cookie, - } - } - ([Discover { namespace, .. }, ..], DiscoverResponse(Err(error))) => { - DiscoverFailed { - namespace: namespace.to_owned(), - error, - } - } - (.., other) => return Err(Error::BadMessage(other)), - }; - - Next::EmitEvent { - event, - next_state: Outbound::PendingClose(substream), - } - } - Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), - Poll::Pending => Next::Pending { - next_state: Outbound::PendingRemote(substream), - }, - }, - Outbound::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: Outbound::PendingClose(substream), - }, - }, - }) - } -} - -impl ProtocolsHandler for RendezvousHandler { - type InEvent = InEvent; - type OutEvent = OutEvent; - type Error = Error; - type InboundProtocol = protocol::Rendezvous; - type OutboundProtocol = protocol::Rendezvous; - type InboundOpenInfo = (); - type OutboundOpenInfo = Message; - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(protocol::new(), ()) - } - - fn inject_fully_negotiated_inbound( - &mut self, - substream: >::Output, - _msg: Self::InboundOpenInfo, - ) { - match self.inbound { - SubstreamState::None => { - self.inbound = SubstreamState::Active(Inbound::PendingRead(substream)); - } - _ => { - log::warn!("Ignoring new inbound substream because existing one is still active") - } - } - } - - fn inject_fully_negotiated_outbound( - &mut self, - substream: >::Output, - msg: Self::OutboundOpenInfo, - ) { - match self.outbound { - SubstreamState::Active(Outbound::PendingNegotiate) => { - self.outbound = SubstreamState::Active(Outbound::PendingSend { - substream, - to_send: msg, - }); - self.outbound_history.clear(); - } - _ => { - log::warn!("Ignoring new outbound substream because existing one is still active") - } - } - } - - // event injected from NotifyHandler - fn inject_event(&mut self, req: InEvent) { - let (inbound, outbound) = match ( - req, - mem::replace(&mut self.inbound, SubstreamState::Poisoned), - mem::replace(&mut self.outbound, SubstreamState::Poisoned), - ) { - (InEvent::RegisterRequest(reggo), inbound, SubstreamState::None) => ( - inbound, - SubstreamState::Active(Outbound::PendingOpen(Message::Register(reggo))), - ), - (InEvent::UnregisterRequest(namespace), inbound, SubstreamState::None) => ( - inbound, - SubstreamState::Active(Outbound::PendingOpen(Message::Unregister(namespace))), - ), - ( - InEvent::DiscoverRequest { - namespace, - cookie, - limit, - }, - inbound, - SubstreamState::None, - ) => ( - inbound, - SubstreamState::Active(Outbound::PendingOpen(Message::Discover { - namespace, - cookie, - limit, - })), - ), - ( - InEvent::RegisterResponse { ttl }, - SubstreamState::Active(Inbound::PendingBehaviour(substream)), - outbound, - ) => ( - SubstreamState::Active(Inbound::PendingSend( - substream, - Message::RegisterResponse(Ok(ttl)), - )), - outbound, - ), - ( - InEvent::DeclineRegisterRequest(error), - SubstreamState::Active(Inbound::PendingBehaviour(substream)), - outbound, - ) => ( - SubstreamState::Active(Inbound::PendingSend( - substream, - Message::RegisterResponse(Err(error)), - )), - outbound, - ), - ( - InEvent::DiscoverResponse { discovered, cookie }, - SubstreamState::Active(Inbound::PendingBehaviour(substream)), - outbound, - ) => ( - SubstreamState::Active(Inbound::PendingSend( - substream, - Message::DiscoverResponse(Ok((discovered, cookie))), - )), - outbound, - ), - ( - InEvent::DeclineDiscoverRequest(error), - SubstreamState::Active(Inbound::PendingBehaviour(substream)), - outbound, - ) => ( - SubstreamState::Active(Inbound::PendingSend( - substream, - Message::DiscoverResponse(Err(error)), - )), - outbound, - ), - (event, inbound, outbound) => { - debug_assert!( - false, - "Neither {:?} nor {:?} can handle {:?}", - inbound, outbound, event - ); - - (inbound, outbound) - } - }; - - self.inbound = inbound; - self.outbound = outbound; - } - - fn inject_dial_upgrade_error( - &mut self, - _: Self::OutboundOpenInfo, - _: ProtocolsHandlerUpgrErr, - ) { - } - - fn connection_keep_alive(&self) -> KeepAlive { - KeepAlive::Yes - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ProtocolsHandlerEvent< - Self::OutboundProtocol, - Self::OutboundOpenInfo, - Self::OutEvent, - Self::Error, - >, - > { - if let Poll::Ready(event) = self.inbound.poll(cx, &mut ()) { - return Poll::Ready(event); - } - - if let Poll::Ready(event) = self.outbound.poll( - cx, - &mut OutboundPollParams { - history: &mut self.outbound_history, - }, - ) { - return Poll::Ready(event); - } - - Poll::Pending - } -} diff --git a/protocols/rendezvous/src/handler/inbound.rs b/protocols/rendezvous/src/handler/inbound.rs new file mode 100644 index 00000000000..d8941830226 --- /dev/null +++ b/protocols/rendezvous/src/handler/inbound.rs @@ -0,0 +1,181 @@ +// 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, SubstreamHandler}; +use asynchronous_codec::Framed; +use futures::{SinkExt, StreamExt}; +use libp2p_swarm::NegotiatedSubstream; +use std::fmt; +use std::task::{Context, Poll}; + +/// The state of an inbound substream (i.e. the remote node opened it). +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"), + } + } +} + +#[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 new(substream: NegotiatedSubstream, _: Self::OpenInfo) -> Self { + Stream::PendingRead(Framed::new(substream, RendezvousCodec::default())) + } + + fn inject_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, "{:?} cannot handle event {:?}", inbound, 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.clone() { + 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.clone()) + .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 new file mode 100644 index 00000000000..d91bf55f448 --- /dev/null +++ b/protocols/rendezvous/src/handler/outbound.rs @@ -0,0 +1,213 @@ +// 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::{Next, SubstreamHandler}; +use crate::{ErrorCode, Namespace, Registration, Ttl}; +use asynchronous_codec::Framed; +use futures::{SinkExt, StreamExt}; +use libp2p_swarm::NegotiatedSubstream; +use std::task::{Context, Poll}; +use void::Void; + +pub struct Stream { + history: MessageHistory, + state: State, +} + +#[derive(Default)] +struct MessageHistory { + sent: Vec, +} + +#[derive(Debug, Clone)] +pub enum OutEvent { + Registered { + namespace: Namespace, + ttl: Ttl, + }, + RegisterFailed(Namespace, ErrorCode), + Discovered { + registrations: Vec, + cookie: Cookie, + }, + DiscoverFailed { + namespace: Option, + error: ErrorCode, + }, +} + +#[derive(Debug)] +pub enum OpenInfo { + RegisterRequest(NewRegistration), + UnregisterRequest(Namespace), + DiscoverRequest { + namespace: Option, + cookie: Option, + limit: Option, + }, +} + +/// The state of an outbound substream (i.e. we opened it). +enum State { + /// We got the substream, now we need to send the message. + PendingSend { + substream: Framed, + to_send: Message, + }, + /// We sent the message, now we need to flush the data out. + PendingFlush(Framed), + /// We are waiting for the response from the remote. + PendingRemote(Framed), + /// We are closing down the substream. + PendingClose(Framed), +} + +impl SubstreamHandler for Stream { + type InEvent = Void; + type OutEvent = OutEvent; + type Error = Error; + type OpenInfo = OpenInfo; + + fn new(substream: NegotiatedSubstream, info: Self::OpenInfo) -> Self { + Stream { + history: Default::default(), + state: State::PendingSend { + substream: Framed::new(substream, RendezvousCodec::default()), + to_send: 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, + }, + }, + }, + } + } + + fn inject_event(self, event: Self::InEvent) -> Self { + void::unreachable(event) + } + + fn advance(self, cx: &mut Context<'_>) -> Result, Self::Error> { + let Stream { state, mut history } = self; + + let next = match state { + State::PendingSend { + mut substream, + to_send: message, + } => match substream + .poll_ready_unpin(cx) + .map_err(Error::WriteMessage)? + { + Poll::Ready(()) => { + substream + .start_send_unpin(message.clone()) + .map_err(Error::WriteMessage)?; + history.sent.push(message); + + Next::Continue { + next_state: State::PendingFlush(substream), + } + } + Poll::Pending => Next::Pending { + next_state: State::PendingSend { + substream, + to_send: message, + }, + }, + }, + State::PendingFlush(mut substream) => match substream + .poll_flush_unpin(cx) + .map_err(Error::WriteMessage)? + { + Poll::Ready(()) => Next::Continue { + next_state: State::PendingRemote(substream), + }, + Poll::Pending => Next::Pending { + next_state: State::PendingFlush(substream), + }, + }, + State::PendingRemote(mut substream) => match substream + .poll_next_unpin(cx) + .map_err(Error::ReadMessage)? + { + Poll::Ready(Some(received_message)) => { + use crate::codec::Message::*; + use OutEvent::*; + + // Absolutely amazing Rust pattern matching ahead! + // We match against the slice of historical messages and the received message. + // [, ..] effectively matches against the first message that we sent on this substream + let event = match (history.sent.as_slice(), received_message) { + ([Register(registration), ..], RegisterResponse(Ok(ttl))) => Registered { + namespace: registration.namespace.to_owned(), + ttl, + }, + ([Register(registration), ..], RegisterResponse(Err(error))) => { + RegisterFailed(registration.namespace.to_owned(), error) + } + ([Discover { .. }, ..], DiscoverResponse(Ok((registrations, cookie)))) => { + Discovered { + registrations, + cookie, + } + } + ([Discover { namespace, .. }, ..], DiscoverResponse(Err(error))) => { + DiscoverFailed { + namespace: namespace.to_owned(), + error, + } + } + (.., other) => return Err(Error::BadMessage(other)), + }; + + Next::EmitEvent { + event, + next_state: State::PendingClose(substream), + } + } + Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), + Poll::Pending => Next::Pending { + next_state: State::PendingRemote(substream), + }, + }, + State::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: State::PendingClose(substream), + }, + }, + }; + let next = next.map_state(|state| Stream { history, state }); + + Ok(next) + } +} diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index f431305f53a..de33bba2fed 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -24,8 +24,7 @@ pub use self::codec::{ErrorCode, Namespace, NamespaceTooLong, Registration, Ttl} mod behaviour; mod codec; mod handler; -mod protocol; -mod substream; +mod substream_handler; /// If unspecified, rendezvous nodes should assume a TTL of 2h. /// diff --git a/protocols/rendezvous/src/protocol.rs b/protocols/rendezvous/src/protocol.rs deleted file mode 100644 index 32bd12d54b9..00000000000 --- a/protocols/rendezvous/src/protocol.rs +++ /dev/null @@ -1,48 +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::RendezvousCodec; -use asynchronous_codec::Framed; -use libp2p_core::upgrade::FromFnUpgrade; -use libp2p_core::{upgrade, Endpoint}; -use libp2p_swarm::NegotiatedSubstream; -use std::future; -use upgrade::from_fn; -use void::Void; - -pub fn new() -> Rendezvous { - from_fn( - b"/rendezvous/1.0.0", - Box::new(|socket, _| future::ready(Ok(Framed::new(socket, RendezvousCodec::default())))), - ) -} - -pub type Rendezvous = FromFnUpgrade< - &'static [u8], - Box< - dyn Fn( - NegotiatedSubstream, - Endpoint, - ) - -> future::Ready, Void>> - + Send - + 'static, - >, ->; diff --git a/protocols/rendezvous/src/substream.rs b/protocols/rendezvous/src/substream.rs deleted file mode 100644 index 3dea3bbd15e..00000000000 --- a/protocols/rendezvous/src/substream.rs +++ /dev/null @@ -1,124 +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 libp2p_swarm::{ProtocolsHandlerEvent, SubstreamProtocol}; -use std::mem; -use std::task::{Context, Poll}; - -#[derive(Debug)] -pub enum SubstreamState { - /// There is no substream. - None, - /// The substream is in an active state. - Active(S), - /// Something went seriously wrong. - Poisoned, -} - -/// Advances a substream state machine. -/// -/// -pub trait Advance<'handler>: Sized { - type Event; - type Params; - type Error; - type Protocol; - - fn advance( - self, - cx: &mut Context<'_>, - params: &mut Self::Params, - ) -> Result, Self::Error>; -} - -/// Defines the results of advancing a state machine. -pub enum Next { - /// Return from the `poll` function to emit `event`. Set the state machine to `next_state`. - EmitEvent { event: TEvent, next_state: TState }, - /// Return from the `poll` function because we cannot do any more work. Set the state machine to `next_state`. - Pending { next_state: TState }, - /// Return from the `poll` function to open a new substream. Set the state machine to `next_state`. - OpenSubstream { - protocol: TProtocol, - next_state: TState, - }, - /// Continue with advancing the state machine. - Continue { next_state: TState }, - /// The state machine finished. - Done, -} - -impl SubstreamState { - pub fn poll<'handler, TEvent, TUpgrade, TInfo, TError>( - &mut self, - cx: &mut Context<'_>, - params: &mut TState::Params, - ) -> Poll> - where - TState: Advance< - 'handler, - Event = TEvent, - Protocol = SubstreamProtocol, - Error = TError, - >, - { - loop { - let state = match mem::replace(self, SubstreamState::Poisoned) { - SubstreamState::None => { - *self = SubstreamState::None; - return Poll::Pending; - } - SubstreamState::Active(state) => state, - SubstreamState::Poisoned => { - unreachable!("reached poisoned state") - } - }; - - match state.advance(cx, params) { - Ok(Next::Continue { next_state }) => { - *self = SubstreamState::Active(next_state); - continue; - } - Ok(Next::EmitEvent { event, next_state }) => { - *self = SubstreamState::Active(next_state); - return Poll::Ready(ProtocolsHandlerEvent::Custom(event)); - } - Ok(Next::Pending { next_state }) => { - *self = SubstreamState::Active(next_state); - return Poll::Pending; - } - Ok(Next::OpenSubstream { - protocol, - next_state, - }) => { - *self = SubstreamState::Active(next_state); - return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol, - }); - } - Ok(Next::Done) => { - *self = SubstreamState::None; - return Poll::Pending; - } - Err(e) => return Poll::Ready(ProtocolsHandlerEvent::Close(e)), - } - } - } -} diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs new file mode 100644 index 00000000000..b3b46fe7043 --- /dev/null +++ b/protocols/rendezvous/src/substream_handler.rs @@ -0,0 +1,412 @@ +// 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 libp2p_core::upgrade::FromFnUpgrade; +use libp2p_core::Endpoint; +use libp2p_swarm::protocols_handler::{InboundUpgradeSend, OutboundUpgradeSend}; +use libp2p_swarm::{ + KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, + ProtocolsHandlerUpgrErr, SubstreamProtocol, +}; +use std::collections::{HashMap, VecDeque}; +use std::fmt; +use std::future::Ready; +use std::hash::Hash; +use std::task::{Context, Poll}; +use std::time::{Duration, Instant}; +use void::Void; + +pub trait SubstreamHandler: Sized { + type InEvent; + type OutEvent; + type Error; + type OpenInfo; + + fn new(substream: NegotiatedSubstream, info: Self::OpenInfo) -> Self; + fn inject_event(self, event: Self::InEvent) -> Self; + fn advance(self, cx: &mut Context<'_>) -> Result, Self::Error>; +} + +/// Defines the results of advancing a state machine. +pub enum Next { + /// Return from the `poll` function to emit `event`. Set the state machine to `next_state`. + EmitEvent { event: TEvent, next_state: TState }, + /// Return from the `poll` function because we cannot do any more work. Set the state machine to `next_state`. + Pending { next_state: TState }, + /// Continue with advancing the state machine. + Continue { next_state: TState }, + /// The state machine 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) + } +} + +type ProtocolUpgradeFn = + Box Ready> + Send>; +type Protocol = FromFnUpgrade<&'static [u8], ProtocolUpgradeFn>; + +/// An implementation of [`ProtocolsHandler`] that delegates to individual [`SubstreamHandler`]s. +pub struct SubstreamProtocolsHandler { + inbound_substreams: HashMap, + outbound_substreams: HashMap, + protocol: &'static [u8], + next_inbound_substream_id: InboundSubstreamId, + next_outbound_substream_id: OutboundSubstreamId, + + new_substreams: VecDeque, + + initial_keep_alive_deadline: Instant, +} + +impl + SubstreamProtocolsHandler +{ + pub fn new(protocol: &'static [u8]) -> Self { + Self { + inbound_substreams: Default::default(), + outbound_substreams: Default::default(), + protocol, + next_inbound_substream_id: InboundSubstreamId(0), + next_outbound_substream_id: OutboundSubstreamId(0), + new_substreams: Default::default(), + initial_keep_alive_deadline: Instant::now() + Duration::from_secs(30), + } + } +} + +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 +} + +pub enum InEvent { + NewSubstream { + open_info: I, + }, + NotifyInboundSubstream { + id: InboundSubstreamId, + message: TInboundEvent, + }, + NotifyOutboundSubstream { + id: OutboundSubstreamId, + message: TOutboundEvent, + }, +} + +pub enum OutEvent { + InboundEvent { + id: InboundSubstreamId, + message: TInbound, + }, + OutboundEvent { + id: OutboundSubstreamId, + message: TOutbound, + }, + InboundError { + id: InboundSubstreamId, + error: TInboundError, + }, + OutboundError { + id: OutboundSubstreamId, + error: TOutboundError, + }, +} + +impl< + TInboundInEvent, + TInboundOutEvent, + TOutboundInEvent, + TOutboundOutEvent, + TOutboundOpenInfo, + TInboundError, + TOutboundError, + TInboundSubstreamHandler, + TOutboundSubstreamHandler, + > ProtocolsHandler + for SubstreamProtocolsHandler< + TInboundSubstreamHandler, + TOutboundSubstreamHandler, + TOutboundOpenInfo, + > +where + TInboundSubstreamHandler: SubstreamHandler< + InEvent = TInboundInEvent, + OutEvent = TInboundOutEvent, + Error = TInboundError, + OpenInfo = (), + >, + TOutboundSubstreamHandler: SubstreamHandler< + InEvent = TOutboundInEvent, + OutEvent = TOutboundOutEvent, + Error = TOutboundError, + OpenInfo = TOutboundOpenInfo, + >, + TInboundInEvent: Send + 'static, + TInboundOutEvent: Send + 'static, + TOutboundInEvent: Send + 'static, + TOutboundOutEvent: Send + 'static, + TOutboundOpenInfo: Send + 'static, + TInboundError: Send + 'static, + TOutboundError: Send + 'static, + TInboundSubstreamHandler: Send + 'static, + TOutboundSubstreamHandler: Send + 'static, +{ + type InEvent = InEvent; + type OutEvent = OutEvent; + type Error = Void; + type InboundProtocol = Protocol; + type OutboundProtocol = Protocol; + type InboundOpenInfo = (); + type OutboundOpenInfo = TOutboundOpenInfo; + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new( + libp2p_core::upgrade::from_fn( + self.protocol.clone(), + Box::new(|socket, _| std::future::ready(Ok(socket))), + ), + (), + ) + } + + fn inject_fully_negotiated_inbound( + &mut self, + protocol: ::Output, + _: Self::InboundOpenInfo, + ) { + self.inbound_substreams.insert( + self.next_inbound_substream_id.fetch_and_increment(), + TInboundSubstreamHandler::new(protocol, ()), + ); + } + + fn inject_fully_negotiated_outbound( + &mut self, + protocol: ::Output, + info: Self::OutboundOpenInfo, + ) { + self.outbound_substreams.insert( + self.next_outbound_substream_id.fetch_and_increment(), + TOutboundSubstreamHandler::new(protocol, info), + ); + } + + fn inject_event(&mut self, event: Self::InEvent) { + 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.inject_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.inject_event(message); + + self.outbound_substreams.insert(id, new_handler); + } + None => { + log::debug!("Substream with ID {} not found", id); + } + } + } + } + } + + fn inject_dial_upgrade_error( + &mut self, + _: Self::OutboundOpenInfo, + _: ProtocolsHandlerUpgrErr, + ) { + // TODO: Handle upgrade errors properly + } + + fn connection_keep_alive(&self) -> KeepAlive { + 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; + } + + return KeepAlive::Yes; + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ProtocolsHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + Self::OutEvent, + Self::Error, + >, + > { + if let Some(open_info) = self.new_substreams.pop_front() { + return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new( + libp2p_core::upgrade::from_fn( + self.protocol.clone(), + Box::new(|socket, _| std::future::ready(Ok(socket))), + ), + open_info, + ), + }); + } + + match poll_substreams(&mut self.inbound_substreams, cx) { + Poll::Ready(Ok((id, message))) => { + return Poll::Ready(ProtocolsHandlerEvent::Custom(OutEvent::InboundEvent { + id, + message, + })) + } + Poll::Ready(Err((id, error))) => { + return Poll::Ready(ProtocolsHandlerEvent::Custom(OutEvent::InboundError { + id, + error, + })) + } + Poll::Pending => {} + } + + match poll_substreams(&mut self.outbound_substreams, cx) { + Poll::Ready(Ok((id, message))) => { + return Poll::Ready(ProtocolsHandlerEvent::Custom(OutEvent::OutboundEvent { + id, + message, + })) + } + Poll::Ready(Err((id, error))) => { + return Poll::Ready(ProtocolsHandlerEvent::Custom(OutEvent::OutboundError { + id, + error, + })) + } + Poll::Pending => {} + } + + Poll::Pending + } +} From 082dfd05489e7deabb4dd45baa0a7ae5d0dd8661 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 1 Jul 2021 18:28:30 +1000 Subject: [PATCH 215/242] Simplify Outbound stream by implementing it as a future --- protocols/rendezvous/src/handler/outbound.rs | 199 ++++++------------- 1 file changed, 66 insertions(+), 133 deletions(-) diff --git a/protocols/rendezvous/src/handler/outbound.rs b/protocols/rendezvous/src/handler/outbound.rs index d91bf55f448..8b144e5b723 100644 --- a/protocols/rendezvous/src/handler/outbound.rs +++ b/protocols/rendezvous/src/handler/outbound.rs @@ -23,19 +23,14 @@ use crate::handler::Error; use crate::substream_handler::{Next, SubstreamHandler}; use crate::{ErrorCode, Namespace, Registration, Ttl}; use asynchronous_codec::Framed; -use futures::{SinkExt, StreamExt}; +use futures::future::{BoxFuture, Fuse, FusedFuture}; +use futures::{FutureExt, SinkExt, TryFutureExt, TryStreamExt}; use libp2p_swarm::NegotiatedSubstream; use std::task::{Context, Poll}; use void::Void; pub struct Stream { - history: MessageHistory, - state: State, -} - -#[derive(Default)] -struct MessageHistory { - sent: Vec, + future: Fuse>>, } #[derive(Debug, Clone)] @@ -66,21 +61,6 @@ pub enum OpenInfo { }, } -/// The state of an outbound substream (i.e. we opened it). -enum State { - /// We got the substream, now we need to send the message. - PendingSend { - substream: Framed, - to_send: Message, - }, - /// We sent the message, now we need to flush the data out. - PendingFlush(Framed), - /// We are waiting for the response from the remote. - PendingRemote(Framed), - /// We are closing down the substream. - PendingClose(Framed), -} - impl SubstreamHandler for Stream { type InEvent = Void; type OutEvent = OutEvent; @@ -88,26 +68,60 @@ impl SubstreamHandler for Stream { type OpenInfo = OpenInfo; fn new(substream: NegotiatedSubstream, 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, + }, + }; + Stream { - history: Default::default(), - state: State::PendingSend { - substream: Framed::new(substream, RendezvousCodec::default()), - to_send: match info { - OpenInfo::RegisterRequest(new_registration) => { - Message::Register(new_registration) + future: 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.to_owned(), + ttl, + }, + (Register(registration), RegisterResponse(Err(error))) => { + RegisterFailed(registration.namespace.to_owned(), error) + } + (Discover { .. }, DiscoverResponse(Ok((registrations, cookie)))) => { + Discovered { + registrations, + cookie, + } } - OpenInfo::UnregisterRequest(namespace) => Message::Unregister(namespace), - OpenInfo::DiscoverRequest { - namespace, - cookie, - limit, - } => Message::Discover { - namespace, - cookie, - limit, + (Discover { namespace, .. }, DiscoverResponse(Err(error))) => DiscoverFailed { + namespace: namespace.to_owned(), + error, }, - }, - }, + (.., other) => return Err(Error::BadMessage(other)), + }; + + stream.close().map_err(Error::WriteMessage).await?; + + Ok(event) + } + .boxed() + .fuse(), } } @@ -115,99 +129,18 @@ impl SubstreamHandler for Stream { void::unreachable(event) } - fn advance(self, cx: &mut Context<'_>) -> Result, Self::Error> { - let Stream { state, mut history } = self; - - let next = match state { - State::PendingSend { - mut substream, - to_send: message, - } => match substream - .poll_ready_unpin(cx) - .map_err(Error::WriteMessage)? - { - Poll::Ready(()) => { - substream - .start_send_unpin(message.clone()) - .map_err(Error::WriteMessage)?; - history.sent.push(message); - - Next::Continue { - next_state: State::PendingFlush(substream), - } - } - Poll::Pending => Next::Pending { - next_state: State::PendingSend { - substream, - to_send: message, - }, - }, - }, - State::PendingFlush(mut substream) => match substream - .poll_flush_unpin(cx) - .map_err(Error::WriteMessage)? - { - Poll::Ready(()) => Next::Continue { - next_state: State::PendingRemote(substream), - }, - Poll::Pending => Next::Pending { - next_state: State::PendingFlush(substream), - }, - }, - State::PendingRemote(mut substream) => match substream - .poll_next_unpin(cx) - .map_err(Error::ReadMessage)? - { - Poll::Ready(Some(received_message)) => { - use crate::codec::Message::*; - use OutEvent::*; - - // Absolutely amazing Rust pattern matching ahead! - // We match against the slice of historical messages and the received message. - // [, ..] effectively matches against the first message that we sent on this substream - let event = match (history.sent.as_slice(), received_message) { - ([Register(registration), ..], RegisterResponse(Ok(ttl))) => Registered { - namespace: registration.namespace.to_owned(), - ttl, - }, - ([Register(registration), ..], RegisterResponse(Err(error))) => { - RegisterFailed(registration.namespace.to_owned(), error) - } - ([Discover { .. }, ..], DiscoverResponse(Ok((registrations, cookie)))) => { - Discovered { - registrations, - cookie, - } - } - ([Discover { namespace, .. }, ..], DiscoverResponse(Err(error))) => { - DiscoverFailed { - namespace: namespace.to_owned(), - error, - } - } - (.., other) => return Err(Error::BadMessage(other)), - }; - - Next::EmitEvent { - event, - next_state: State::PendingClose(substream), - } - } - Poll::Ready(None) => return Err(Error::UnexpectedEndOfStream), - Poll::Pending => Next::Pending { - next_state: State::PendingRemote(substream), - }, - }, - State::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: State::PendingClose(substream), - }, - }, - }; - let next = next.map_state(|state| Stream { history, state }); + fn advance(mut self, cx: &mut Context<'_>) -> Result, Self::Error> { + if self.future.is_terminated() { + return Ok(Next::Done); + } - Ok(next) + 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 }), + } } } From d3628cf158b9352debbb137d9af36c33921ee1f8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 1 Jul 2021 18:45:37 +1000 Subject: [PATCH 216/242] Define helper struct for substreams that can be implemented as futures --- protocols/rendezvous/src/handler/outbound.rs | 148 ++++++++---------- protocols/rendezvous/src/substream_handler.rs | 48 +++++- 2 files changed, 105 insertions(+), 91 deletions(-) diff --git a/protocols/rendezvous/src/handler/outbound.rs b/protocols/rendezvous/src/handler/outbound.rs index 8b144e5b723..03e78daf6ad 100644 --- a/protocols/rendezvous/src/handler/outbound.rs +++ b/protocols/rendezvous/src/handler/outbound.rs @@ -20,46 +20,15 @@ use crate::codec::{Cookie, Message, NewRegistration, RendezvousCodec}; use crate::handler::Error; -use crate::substream_handler::{Next, SubstreamHandler}; +use crate::substream_handler::{FutureSubstream, Next, SubstreamHandler}; use crate::{ErrorCode, Namespace, Registration, Ttl}; use asynchronous_codec::Framed; -use futures::future::{BoxFuture, Fuse, FusedFuture}; -use futures::{FutureExt, SinkExt, TryFutureExt, TryStreamExt}; +use futures::{SinkExt, TryFutureExt, TryStreamExt}; use libp2p_swarm::NegotiatedSubstream; -use std::task::{Context, Poll}; +use std::task::Context; use void::Void; -pub struct Stream { - future: Fuse>>, -} - -#[derive(Debug, Clone)] -pub enum OutEvent { - Registered { - namespace: Namespace, - ttl: Ttl, - }, - RegisterFailed(Namespace, ErrorCode), - Discovered { - registrations: Vec, - cookie: Cookie, - }, - DiscoverFailed { - namespace: Option, - error: ErrorCode, - }, -} - -#[derive(Debug)] -pub enum OpenInfo { - RegisterRequest(NewRegistration), - UnregisterRequest(Namespace), - DiscoverRequest { - namespace: Option, - cookie: Option, - limit: Option, - }, -} +pub struct Stream(FutureSubstream); impl SubstreamHandler for Stream { type InEvent = Void; @@ -83,64 +52,75 @@ impl SubstreamHandler for Stream { }, }; - Stream { - future: async move { - use Message::*; - use OutEvent::*; + 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)?; + 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.to_owned(), - ttl, - }, - (Register(registration), RegisterResponse(Err(error))) => { - RegisterFailed(registration.namespace.to_owned(), error) - } - (Discover { .. }, DiscoverResponse(Ok((registrations, cookie)))) => { - Discovered { - registrations, - cookie, - } - } - (Discover { namespace, .. }, DiscoverResponse(Err(error))) => DiscoverFailed { - namespace: namespace.to_owned(), - error, - }, - (.., other) => return Err(Error::BadMessage(other)), - }; + let event = match (sent_message, received_message) { + (Register(registration), RegisterResponse(Ok(ttl))) => Registered { + namespace: registration.namespace.to_owned(), + ttl, + }, + (Register(registration), RegisterResponse(Err(error))) => { + RegisterFailed(registration.namespace.to_owned(), error) + } + (Discover { .. }, DiscoverResponse(Ok((registrations, cookie)))) => Discovered { + registrations, + cookie, + }, + (Discover { namespace, .. }, DiscoverResponse(Err(error))) => DiscoverFailed { + namespace: namespace.to_owned(), + error, + }, + (.., other) => return Err(Error::BadMessage(other)), + }; - stream.close().map_err(Error::WriteMessage).await?; + stream.close().map_err(Error::WriteMessage).await?; - Ok(event) - } - .boxed() - .fuse(), - } + Ok(event) + })) } fn inject_event(self, event: Self::InEvent) -> Self { void::unreachable(event) } - fn advance(mut self, cx: &mut Context<'_>) -> Result, Self::Error> { - 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 }), - } + 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, + }, +} + +#[derive(Debug)] +pub enum OpenInfo { + RegisterRequest(NewRegistration), + UnregisterRequest(Namespace), + DiscoverRequest { + namespace: Option, + cookie: Option, + limit: Option, + }, +} diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs index b3b46fe7043..567eb1a3c64 100644 --- a/protocols/rendezvous/src/substream_handler.rs +++ b/protocols/rendezvous/src/substream_handler.rs @@ -18,6 +18,17 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::collections::{HashMap, VecDeque}; +use std::fmt; +use std::future::{Future, Ready}; +use std::hash::Hash; +use std::task::{Context, Poll}; +use std::time::{Duration, Instant}; + +use futures::future::{BoxFuture, Fuse, FusedFuture}; +use futures::FutureExt; +use void::Void; + use libp2p_core::upgrade::FromFnUpgrade; use libp2p_core::Endpoint; use libp2p_swarm::protocols_handler::{InboundUpgradeSend, OutboundUpgradeSend}; @@ -25,13 +36,6 @@ use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; -use std::collections::{HashMap, VecDeque}; -use std::fmt; -use std::future::Ready; -use std::hash::Hash; -use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; -use void::Void; pub trait SubstreamHandler: Sized { type InEvent; @@ -410,3 +414,33 @@ where 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 struct FutureSubstream { + future: Fuse>>, +} + +impl FutureSubstream { + pub fn new(future: impl Future> + Send + 'static) -> Self { + Self { + future: future.boxed().fuse(), + } + } + + pub 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 }), + } + } +} From 8f46361d90c95ea340bb95c5edd7aa60324e2a75 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 2 Jul 2021 11:08:56 +1000 Subject: [PATCH 217/242] Add some more docs and split `inject_event` of `Rendezvous` behaviour --- protocols/rendezvous/examples/register.rs | 9 +- protocols/rendezvous/src/behaviour.rs | 384 +++++++++--------- protocols/rendezvous/src/handler.rs | 5 + protocols/rendezvous/src/substream_handler.rs | 53 ++- 4 files changed, 234 insertions(+), 217 deletions(-) diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index bde10ccffe8..946346ac15c 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -75,10 +75,11 @@ async fn main() { } // once `/identify` did its job, we know our external address and can register SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { .. })) => { - swarm - .behaviour_mut() - .rendezvous - .register(Namespace::from_static("rendezvous"), rendezvous_point, None); + swarm.behaviour_mut().rendezvous.register( + Namespace::from_static("rendezvous"), + rendezvous_point, + None, + ); } SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Registered { namespace, diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 9077484fe07..baa3ae12791 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -21,8 +21,8 @@ use crate::codec::{Cookie, ErrorCode, Namespace, NewRegistration, Registration, Ttl}; use crate::handler::outbound::OpenInfo; use crate::handler::{inbound, outbound}; -use crate::substream_handler::SubstreamProtocolsHandler; -use crate::{substream_handler, MAX_TTL, MIN_TTL}; +use crate::substream_handler::{InboundSubstreamId, SubstreamProtocolsHandler}; +use crate::{handler, MAX_TTL, MIN_TTL}; use bimap::BiMap; use futures::future::BoxFuture; use futures::ready; @@ -40,12 +40,9 @@ use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::Duration; use uuid::Uuid; -use void::Void; pub struct Rendezvous { - events: VecDeque< - NetworkBehaviourAction, Event>, - >, + events: VecDeque>, registrations: Registrations, keypair: Keypair, pending_register_requests: Vec<(Namespace, PeerId, Option)>, @@ -96,7 +93,7 @@ impl Rendezvous { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: substream_handler::InEvent::NewSubstream { + event: handler::InEvent::NewSubstream { open_info: OpenInfo::UnregisterRequest(namespace), }, handler: NotifyHandler::Any, @@ -113,7 +110,7 @@ impl Rendezvous { self.events .push_back(NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: substream_handler::InEvent::NewSubstream { + event: handler::InEvent::NewSubstream { open_info: OpenInfo::DiscoverRequest { namespace: ns, cookie, @@ -191,7 +188,9 @@ impl NetworkBehaviour for Rendezvous { type OutEvent = Event; fn new_handler(&mut self) -> Self::ProtocolsHandler { - SubstreamProtocolsHandler::new(b"/rendezvous/1.0.0") + let initial_keep_alive = Duration::from_secs(30); + + SubstreamProtocolsHandler::new(b"/rendezvous/1.0.0", initial_keep_alive) } fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { @@ -206,198 +205,20 @@ impl NetworkBehaviour for Rendezvous { &mut self, peer_id: PeerId, connection: ConnectionId, - event: substream_handler::OutEvent< - inbound::OutEvent, - outbound::OutEvent, - crate::handler::Error, - crate::handler::Error, - >, + event: handler::OutEvent, ) { let new_events = match event { - // bad registration - substream_handler::OutEvent::InboundEvent { - id, - message: inbound::OutEvent::RegistrationRequested(registration), - } if registration.record.peer_id() != peer_id => { - let error = ErrorCode::NotAuthorized; - - vec![ - NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: substream_handler::InEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::DeclineRegisterRequest(error), - }, - }, - NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { - peer: peer_id, - namespace: registration.namespace, - error, - }), - ] - } - substream_handler::OutEvent::InboundEvent { - id, - message: inbound::OutEvent::RegistrationRequested(registration), - } => { - let namespace = registration.namespace.clone(); - - match self.registrations.add(registration) { - Ok(registration) => { - vec![ - NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: substream_handler::InEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::RegisterResponse { - ttl: registration.ttl, - }, - }, - }, - NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { - peer: peer_id, - registration, - }), - ] - } - Err(TtlOutOfRange::TooLong { .. }) | Err(TtlOutOfRange::TooShort { .. }) => { - let error = ErrorCode::InvalidTtl; - - vec![ - NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: substream_handler::InEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::DeclineRegisterRequest(error), - }, - }, - NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { - peer: peer_id, - namespace, - error, - }), - ] - } - } - } - substream_handler::OutEvent::InboundEvent { - id, - message: - inbound::OutEvent::DiscoverRequested { - namespace, - cookie, - limit, - }, - } => match self.registrations.get(namespace, cookie, limit) { - Ok((registrations, cookie)) => { - let discovered = registrations.cloned().collect::>(); - - vec![ - NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: substream_handler::InEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::DiscoverResponse { - discovered: discovered.clone(), - cookie, - }, - }, - }, - NetworkBehaviourAction::GenerateEvent(Event::DiscoverServed { - enquirer: peer_id, - registrations: discovered, - }), - ] - } - Err(_) => { - let error = ErrorCode::InvalidCookie; - - vec![ - NetworkBehaviourAction::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection), - event: substream_handler::InEvent::NotifyInboundSubstream { - id, - message: inbound::InEvent::DeclineDiscoverRequest(error), - }, - }, - NetworkBehaviourAction::GenerateEvent(Event::DiscoverNotServed { - enquirer: peer_id, - error, - }), - ] - } - }, - substream_handler::OutEvent::InboundEvent { - message: inbound::OutEvent::UnregisterRequested(namespace), - .. - } => { - self.registrations.remove(namespace.clone(), peer_id); - - vec![NetworkBehaviourAction::GenerateEvent( - Event::PeerUnregistered { - peer: peer_id, - namespace, - }, - )] - } - substream_handler::OutEvent::OutboundEvent { - message: outbound::OutEvent::Registered { namespace, ttl }, - .. - } => { - vec![NetworkBehaviourAction::GenerateEvent(Event::Registered { - rendezvous_node: peer_id, - ttl, - namespace, - })] + handler::OutEvent::InboundEvent { id, message } => { + handle_inbound_event(message, peer_id, connection, id, &mut self.registrations) } - substream_handler::OutEvent::OutboundEvent { - message: outbound::OutEvent::RegisterFailed(namespace, error), - .. - } => { - vec![NetworkBehaviourAction::GenerateEvent( - Event::RegisterFailed(RegisterError::Remote { - rendezvous_node: peer_id, - namespace, - error, - }), - )] + handler::OutEvent::OutboundEvent { message, .. } => { + handle_outbound_event(message, peer_id) } - substream_handler::OutEvent::OutboundEvent { - message: - outbound::OutEvent::Discovered { - registrations, - cookie, - }, - .. - } => { - vec![NetworkBehaviourAction::GenerateEvent(Event::Discovered { - rendezvous_node: peer_id, - registrations, - cookie, - })] - } - substream_handler::OutEvent::OutboundEvent { - message: outbound::OutEvent::DiscoverFailed { namespace, error }, - .. - } => { - vec![NetworkBehaviourAction::GenerateEvent( - Event::DiscoverFailed { - rendezvous_node: peer_id, - namespace, - error, - }, - )] - } - substream_handler::OutEvent::InboundError { .. } => { + handler::OutEvent::InboundError { .. } => { // TODO: log errors and close connection? vec![] } - substream_handler::OutEvent::OutboundError { .. } => { + handler::OutEvent::OutboundError { .. } => { // TODO: log errors and close connection? vec![] } @@ -443,7 +264,7 @@ impl NetworkBehaviour for Rendezvous { let action = match PeerRecord::new(self.keypair.clone(), external_addresses) { Ok(peer_record) => NetworkBehaviourAction::NotifyHandler { peer_id: rendezvous_node, - event: substream_handler::InEvent::NewSubstream { + event: handler::InEvent::NewSubstream { open_info: OpenInfo::RegisterRequest(NewRegistration { namespace, record: peer_record, @@ -464,6 +285,179 @@ impl NetworkBehaviour for Rendezvous { } } +fn handle_inbound_event( + event: inbound::OutEvent, + peer_id: PeerId, + connection: ConnectionId, + id: InboundSubstreamId, + registrations: &mut Registrations, +) -> Vec> { + match event { + // bad registration + inbound::OutEvent::RegistrationRequested(registration) + if registration.record.peer_id() != peer_id => + { + let error = ErrorCode::NotAuthorized; + + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::DeclineRegisterRequest(error), + }, + }, + NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { + peer: peer_id, + namespace: registration.namespace, + error, + }), + ] + } + inbound::OutEvent::RegistrationRequested(registration) => { + let namespace = registration.namespace.clone(); + + match registrations.add(registration) { + Ok(registration) => { + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::RegisterResponse { + ttl: registration.ttl, + }, + }, + }, + NetworkBehaviourAction::GenerateEvent(Event::PeerRegistered { + peer: peer_id, + registration, + }), + ] + } + Err(TtlOutOfRange::TooLong { .. }) | Err(TtlOutOfRange::TooShort { .. }) => { + let error = ErrorCode::InvalidTtl; + + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::DeclineRegisterRequest(error), + }, + }, + NetworkBehaviourAction::GenerateEvent(Event::PeerNotRegistered { + peer: peer_id, + namespace, + error, + }), + ] + } + } + } + inbound::OutEvent::DiscoverRequested { + namespace, + cookie, + limit, + } => match registrations.get(namespace, cookie, limit) { + Ok((registrations, cookie)) => { + let discovered = registrations.cloned().collect::>(); + + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::DiscoverResponse { + discovered: discovered.clone(), + cookie, + }, + }, + }, + NetworkBehaviourAction::GenerateEvent(Event::DiscoverServed { + enquirer: peer_id, + registrations: discovered, + }), + ] + } + Err(_) => { + let error = ErrorCode::InvalidCookie; + + vec![ + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection), + event: handler::InEvent::NotifyInboundSubstream { + id, + message: inbound::InEvent::DeclineDiscoverRequest(error), + }, + }, + NetworkBehaviourAction::GenerateEvent(Event::DiscoverNotServed { + enquirer: peer_id, + error, + }), + ] + } + }, + inbound::OutEvent::UnregisterRequested(namespace) => { + registrations.remove(namespace.clone(), peer_id); + + vec![NetworkBehaviourAction::GenerateEvent( + Event::PeerUnregistered { + peer: peer_id, + namespace, + }, + )] + } + } +} +fn handle_outbound_event( + event: outbound::OutEvent, + peer_id: PeerId, +) -> Vec> { + match event { + outbound::OutEvent::Registered { namespace, ttl } => { + vec![NetworkBehaviourAction::GenerateEvent(Event::Registered { + rendezvous_node: peer_id, + ttl, + namespace, + })] + } + outbound::OutEvent::RegisterFailed(namespace, error) => { + vec![NetworkBehaviourAction::GenerateEvent( + Event::RegisterFailed(RegisterError::Remote { + rendezvous_node: peer_id, + namespace, + error, + }), + )] + } + outbound::OutEvent::Discovered { + registrations, + cookie, + } => { + vec![NetworkBehaviourAction::GenerateEvent(Event::Discovered { + rendezvous_node: peer_id, + registrations, + cookie, + })] + } + outbound::OutEvent::DiscoverFailed { namespace, error } => { + vec![NetworkBehaviourAction::GenerateEvent( + Event::DiscoverFailed { + rendezvous_node: peer_id, + namespace, + error, + }, + )] + } + } +} + #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] struct RegistrationId(Uuid); diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index ed89efec74f..55399424b29 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -20,6 +20,7 @@ use crate::codec; use crate::codec::Message; +use void::Void; pub mod inbound; pub mod outbound; @@ -36,3 +37,7 @@ pub enum Error { #[error("Substream ended unexpectedly mid-protocol")] UnexpectedEndOfStream, } + +pub type InEvent = crate::substream_handler::InEvent; +pub type OutEvent = + crate::substream_handler::OutEvent; diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs index 567eb1a3c64..e5dce1446aa 100644 --- a/protocols/rendezvous/src/substream_handler.rs +++ b/protocols/rendezvous/src/substream_handler.rs @@ -18,17 +18,15 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use std::collections::{HashMap, VecDeque}; -use std::fmt; -use std::future::{Future, Ready}; -use std::hash::Hash; -use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; +//! A generic [`ProtocolsHandler`] 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::{BoxFuture, Fuse, FusedFuture}; use futures::FutureExt; -use void::Void; - use libp2p_core::upgrade::FromFnUpgrade; use libp2p_core::Endpoint; use libp2p_swarm::protocols_handler::{InboundUpgradeSend, OutboundUpgradeSend}; @@ -36,7 +34,15 @@ use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, }; +use std::collections::{HashMap, VecDeque}; +use std::fmt; +use std::future::{Future, Ready}; +use std::hash::Hash; +use std::task::{Context, Poll}; +use std::time::{Duration, Instant}; +use void::Void; +/// Handles a substream throughout its lifetime. pub trait SubstreamHandler: Sized { type InEvent; type OutEvent; @@ -48,15 +54,17 @@ pub trait SubstreamHandler: Sized { fn advance(self, cx: &mut Context<'_>) -> Result, Self::Error>; } -/// Defines the results of advancing a state machine. +/// The result of advancing a [`SubstreamHandler`]. pub enum Next { - /// Return from the `poll` function to emit `event`. Set the state machine to `next_state`. + /// Return the given event and set the handler into `next_state`. EmitEvent { event: TEvent, next_state: TState }, - /// Return from the `poll` function because we cannot do any more work. Set the state machine to `next_state`. + /// The handler currently cannot do any more work, set its state back into `next_state`. Pending { next_state: TState }, - /// Continue with advancing the state machine. + /// 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 state machine finished. + /// The handler finished. Done, } @@ -137,7 +145,7 @@ pub struct SubstreamProtocolsHandler SubstreamProtocolsHandler { - pub fn new(protocol: &'static [u8]) -> Self { + pub fn new(protocol: &'static [u8], initial_keep_alive: Duration) -> Self { Self { inbound_substreams: Default::default(), outbound_substreams: Default::default(), @@ -145,7 +153,7 @@ impl next_inbound_substream_id: InboundSubstreamId(0), next_outbound_substream_id: OutboundSubstreamId(0), new_substreams: Default::default(), - initial_keep_alive_deadline: Instant::now() + Duration::from_secs(30), + initial_keep_alive_deadline: Instant::now() + initial_keep_alive, } } } @@ -191,10 +199,12 @@ where Poll::Pending } +/// Event sent from the [`NetworkBehaviour`] to the [`SubstreamProtocolsHandler`]. pub enum InEvent { - NewSubstream { - open_info: I, - }, + /// 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, @@ -205,19 +215,24 @@ pub enum InEvent { }, } +/// Event produced by the [`SubstreamProtocolsHandler`] for the corresponding [`NetworkBehaviour`]. 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, @@ -342,6 +357,8 @@ where } 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; } From 326c9b2aebbf22961dd5fa97b69ac2bda3dd0d0c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 2 Jul 2021 11:14:51 +1000 Subject: [PATCH 218/242] Fix clippy errors and warnings --- protocols/rendezvous/src/handler/inbound.rs | 4 ++-- protocols/rendezvous/src/handler/outbound.rs | 11 +++++------ protocols/rendezvous/src/substream_handler.rs | 6 +++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/protocols/rendezvous/src/handler/inbound.rs b/protocols/rendezvous/src/handler/inbound.rs index d8941830226..4eed3fb1db0 100644 --- a/protocols/rendezvous/src/handler/inbound.rs +++ b/protocols/rendezvous/src/handler/inbound.rs @@ -117,7 +117,7 @@ impl SubstreamHandler for Stream { Stream::PendingRead(mut substream) => { match substream.poll_next_unpin(cx).map_err(Error::ReadMessage)? { Poll::Ready(Some(msg)) => { - let event = match msg.clone() { + let event = match msg { Message::Register(registration) => { OutEvent::RegistrationRequested(registration) } @@ -156,7 +156,7 @@ impl SubstreamHandler for Stream { { Poll::Ready(()) => { substream - .start_send_unpin(message.clone()) + .start_send_unpin(message) .map_err(Error::WriteMessage)?; Next::Continue { diff --git a/protocols/rendezvous/src/handler/outbound.rs b/protocols/rendezvous/src/handler/outbound.rs index 03e78daf6ad..09f3bca0975 100644 --- a/protocols/rendezvous/src/handler/outbound.rs +++ b/protocols/rendezvous/src/handler/outbound.rs @@ -65,20 +65,19 @@ impl SubstreamHandler for Stream { let event = match (sent_message, received_message) { (Register(registration), RegisterResponse(Ok(ttl))) => Registered { - namespace: registration.namespace.to_owned(), + namespace: registration.namespace, ttl, }, (Register(registration), RegisterResponse(Err(error))) => { - RegisterFailed(registration.namespace.to_owned(), error) + RegisterFailed(registration.namespace, error) } (Discover { .. }, DiscoverResponse(Ok((registrations, cookie)))) => Discovered { registrations, cookie, }, - (Discover { namespace, .. }, DiscoverResponse(Err(error))) => DiscoverFailed { - namespace: namespace.to_owned(), - error, - }, + (Discover { namespace, .. }, DiscoverResponse(Err(error))) => { + DiscoverFailed { namespace, error } + } (.., other) => return Err(Error::BadMessage(other)), }; diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs index e5dce1446aa..6c27ec62af0 100644 --- a/protocols/rendezvous/src/substream_handler.rs +++ b/protocols/rendezvous/src/substream_handler.rs @@ -289,7 +289,7 @@ where fn listen_protocol(&self) -> SubstreamProtocol { SubstreamProtocol::new( libp2p_core::upgrade::from_fn( - self.protocol.clone(), + self.protocol, Box::new(|socket, _| std::future::ready(Ok(socket))), ), (), @@ -370,7 +370,7 @@ where return KeepAlive::No; } - return KeepAlive::Yes; + KeepAlive::Yes } fn poll( @@ -388,7 +388,7 @@ where return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol: SubstreamProtocol::new( libp2p_core::upgrade::from_fn( - self.protocol.clone(), + self.protocol, Box::new(|socket, _| std::future::ready(Ok(socket))), ), open_info, From 3df6e52e566c3bd9a4c1435bbb89f46da9b03557 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 2 Jul 2021 13:11:11 +1000 Subject: [PATCH 219/242] Fix cargo doc errors and warnings --- core/src/peer_record.rs | 2 +- core/src/signed_envelope.rs | 2 +- protocols/rendezvous/src/lib.rs | 6 +++--- protocols/rendezvous/src/substream_handler.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs index 4984a15627e..9c0b1ddc5e3 100644 --- a/core/src/peer_record.rs +++ b/core/src/peer_record.rs @@ -12,7 +12,7 @@ const DOMAIN_SEP: &str = "libp2p-routing-state"; /// Represents a peer routing record. /// /// Peer records are designed to be distributable and carry a signature by being wrapped in a signed envelope. -/// For more information see RFC0003 of the libp2p specifications: https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md +/// For more information see RFC0003 of the libp2p specifications: #[derive(Debug, PartialEq, Clone)] pub struct PeerRecord { peer_id: PeerId, diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index 4f9dc006f02..8fdf5fd04cb 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -7,7 +7,7 @@ use unsigned_varint::encode::usize_buffer; /// A signed envelope contains an arbitrary byte string payload, a signature of the payload, and the public key that can be used to verify the signature. /// -/// For more details see libp2p RFC0002: https://github.com/libp2p/specs/blob/master/RFC/0002-signed-envelopes.md +/// For more details see libp2p RFC0002: #[derive(Debug, Clone, PartialEq)] pub struct SignedEnvelope { key: PublicKey, diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index de33bba2fed..c80b7a5b1b6 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -28,15 +28,15 @@ mod substream_handler; /// If unspecified, rendezvous nodes should assume a TTL of 2h. /// -/// See https://github.com/libp2p/specs/blob/d21418638d5f09f2a4e5a1ceca17058df134a300/rendezvous/README.md#L116-L117. +/// See . pub const DEFAULT_TTL: Ttl = 60 * 60 * 2; /// By default, nodes should require a minimum TTL of 2h /// -/// https://github.com/libp2p/specs/tree/master/rendezvous#recommendations-for-rendezvous-points-configurations. +/// . pub const MIN_TTL: Ttl = 60 * 60 * 2; /// By default, nodes should allow a maximum TTL of 72h /// -/// https://github.com/libp2p/specs/tree/master/rendezvous#recommendations-for-rendezvous-points-configurations. +/// . pub const MAX_TTL: Ttl = 60 * 60 * 72; diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs index 6c27ec62af0..cdabcc0d908 100644 --- a/protocols/rendezvous/src/substream_handler.rs +++ b/protocols/rendezvous/src/substream_handler.rs @@ -199,7 +199,7 @@ where Poll::Pending } -/// Event sent from the [`NetworkBehaviour`] to the [`SubstreamProtocolsHandler`]. +/// Event sent from the [`libp2p_swarm::NetworkBehaviour`] to the [`SubstreamProtocolsHandler`]. pub enum InEvent { /// Open a new substream using the provided `open_info`. /// @@ -215,7 +215,7 @@ pub enum InEvent { }, } -/// Event produced by the [`SubstreamProtocolsHandler`] for the corresponding [`NetworkBehaviour`]. +/// Event produced by the [`SubstreamProtocolsHandler`] for the corresponding [`libp2p_swarm::NetworkBehaviour`]. pub enum OutEvent { /// An inbound substream produced an event. InboundEvent { From 802b1527dd2c062395542f3a15871561d1e8167e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 2 Jul 2021 13:15:20 +1000 Subject: [PATCH 220/242] Make it compile on wasm --- protocols/rendezvous/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 18bea813b30..5112cc6642b 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -25,6 +25,9 @@ sha2 = "0.9" rand = "0.8" wasm-timer = "0.2" +[target.'cfg(target_arch = "wasm32")'.dependencies] +uuid = { version = "0.8", features = ["wasm-bindgen"] } + [dev-dependencies] libp2p = { path = "../.." } rand = "0.8" From 74a39f45df6f2ea5e3e08a01fe765bd9b7128758 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 2 Jul 2021 20:57:25 +1000 Subject: [PATCH 221/242] Store discovered addresses inside behaviour This makes it easier for callers to directly interact with peers they discovered via a rendezvous point. --- protocols/rendezvous/src/behaviour.rs | 27 +++++++++-- protocols/rendezvous/tests/rendezvous.rs | 62 ++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index baa3ae12791..7a8fc7ad52e 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -46,6 +46,11 @@ pub struct Rendezvous { registrations: Registrations, keypair: Keypair, pending_register_requests: Vec<(Namespace, PeerId, Option)>, + + /// Hold addresses of all peers that we have discovered so far. + /// + /// Storing these internally allows us to assist the [`libp2p_swarm::Swarm`] in dialing by returning addresses from [`NetworkBehaviour::addresses_of_peer`]. + discovered_peers: HashMap<(PeerId, Namespace), Vec>, } pub struct Config { @@ -81,6 +86,7 @@ impl Rendezvous { registrations: Registrations::with_config(config), keypair, pending_register_requests: vec![], + discovered_peers: Default::default(), } } @@ -193,8 +199,13 @@ impl NetworkBehaviour for Rendezvous { SubstreamProtocolsHandler::new(b"/rendezvous/1.0.0", initial_keep_alive) } - fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { - Vec::new() + fn addresses_of_peer(&mut self, peer: &PeerId) -> Vec { + self.discovered_peers + .iter() + .filter_map(|((candidate, _), addresses)| (candidate == peer).then(|| addresses)) + .flatten() + .cloned() + .collect() } fn inject_connected(&mut self, _: &PeerId) {} @@ -212,7 +223,7 @@ impl NetworkBehaviour for Rendezvous { handle_inbound_event(message, peer_id, connection, id, &mut self.registrations) } handler::OutEvent::OutboundEvent { message, .. } => { - handle_outbound_event(message, peer_id) + handle_outbound_event(message, peer_id, &mut self.discovered_peers) } handler::OutEvent::InboundError { .. } => { // TODO: log errors and close connection? @@ -418,6 +429,7 @@ fn handle_inbound_event( fn handle_outbound_event( event: outbound::OutEvent, peer_id: PeerId, + discovered_peers: &mut HashMap<(PeerId, Namespace), Vec>, ) -> Vec> { match event { outbound::OutEvent::Registered { namespace, ttl } => { @@ -440,6 +452,15 @@ fn handle_outbound_event( 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) + })); + vec![NetworkBehaviourAction::GenerateEvent(Event::Discovered { rendezvous_node: peer_id, registrations, diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 91c08f06405..0e38fe42d80 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -21,6 +21,7 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; +use futures::StreamExt; use libp2p::rendezvous::{Namespace, Ttl}; use libp2p_core::identity; use libp2p_core::PeerId; @@ -129,6 +130,67 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { } } +#[tokio::test] +async fn discover_allows_for_dial_by_peer_id() { + let _ = env_logger::try_init(); + let RendezvousTest { + mut alice, + mut bob, + mut robert, + .. + } = RendezvousTest::setup().await; + let roberts_peer_id = *robert.local_peer_id(); + + // poll rendezvous point continuously + tokio::spawn(async move { + loop { + robert.next().await; + } + }); + + let namespace = Namespace::from_static("some-namespace"); + + alice + .behaviour_mut() + .register(namespace.clone(), roberts_peer_id, None); + bob.behaviour_mut() + .discover(Some(namespace), None, None, roberts_peer_id); + + match await_events_or_timeout(&mut alice, &mut bob).await { + ( + SwarmEvent::Behaviour(Event::Registered { .. }), + SwarmEvent::Behaviour(Event::Discovered { .. }), + ) => {} + _ => panic!("bad event combination emitted"), + }; + + let alices_peer_id = *alice.local_peer_id(); + let bobs_peer_id = *bob.local_peer_id(); + + bob.dial(&alices_peer_id).unwrap(); + + let alice_connected_to = tokio::spawn(async move { + loop { + if let SwarmEvent::ConnectionEstablished { peer_id, .. } = + alice.select_next_some().await + { + break peer_id; + } + } + }); + let bob_connected_to = tokio::spawn(async move { + loop { + if let SwarmEvent::ConnectionEstablished { peer_id, .. } = bob.select_next_some().await + { + break peer_id; + } + } + }); + + assert_eq!(alice_connected_to.await.unwrap(), bobs_peer_id); + assert_eq!(bob_connected_to.await.unwrap(), alices_peer_id); +} + #[tokio::test] async fn eve_cannot_register() { let _ = env_logger::try_init(); From 4af8af780ba3b1c8344b3d19a146d9628ad5bdb2 Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 8 Jul 2021 12:46:59 +1000 Subject: [PATCH 222/242] Add register example that doesnt use the Identify protocol The external address is specified manually. --- protocols/rendezvous/examples/register.rs | 24 ++- .../examples/register_with_identify.rs | 147 ++++++++++++++++++ .../rendezvous/examples/rendezvous_point.rs | 13 -- 3 files changed, 156 insertions(+), 28 deletions(-) create mode 100644 protocols/rendezvous/examples/register_with_identify.rs diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 946346ac15c..3299d80fed2 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -21,7 +21,6 @@ use futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; -use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p::ping::{Ping, PingConfig, PingEvent, PingSuccess}; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; @@ -29,6 +28,7 @@ use libp2p::swarm::SwarmEvent; use libp2p::{development_transport, rendezvous}; use libp2p::{Multiaddr, NetworkBehaviour}; use libp2p_rendezvous::Namespace; +use libp2p_swarm::AddressScore; use std::time::Duration; #[async_std::main] @@ -45,16 +45,17 @@ async fn main() { let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), MyBehaviour { - identify: Identify::new(IdentifyConfig::new( - "rendezvous-example/1.0.0".to_string(), - identity.public(), - )), rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), ); + // In production the external address should be the publicly facing IP address of the rendezvous point. + // This address is recorded in the registration entry by the rendezvous point. + let external_address = "/ip4/127.0.0.1/tcp/0".parse::().unwrap(); + swarm.add_external_address(external_address, AddressScore::Infinite); + log::info!("Local peer id: {}", swarm.local_peer_id()); let _ = swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()); @@ -73,14 +74,15 @@ async fn main() { } if peer_id == rendezvous_point => { log::error!("Lost connection to rendezvous point {}", error); } - // once `/identify` did its job, we know our external address and can register - SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { .. })) => { + SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == rendezvous_point => { swarm.behaviour_mut().rendezvous.register( Namespace::from_static("rendezvous"), rendezvous_point, None, ); + log::info!("Connection established with rendezvous point {}", peer_id); } + // once `/identify` did its job, we know our external address and can register SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Registered { namespace, ttl, @@ -115,7 +117,6 @@ async fn main() { #[derive(Debug)] enum MyEvent { Rendezvous(rendezvous::Event), - Identify(IdentifyEvent), Ping(PingEvent), } @@ -125,12 +126,6 @@ impl From for MyEvent { } } -impl From for MyEvent { - fn from(event: IdentifyEvent) -> Self { - MyEvent::Identify(event) - } -} - impl From for MyEvent { fn from(event: PingEvent) -> Self { MyEvent::Ping(event) @@ -141,7 +136,6 @@ impl From for MyEvent { #[behaviour(event_process = false)] #[behaviour(out_event = "MyEvent")] struct MyBehaviour { - identify: Identify, rendezvous: Rendezvous, ping: Ping, } diff --git a/protocols/rendezvous/examples/register_with_identify.rs b/protocols/rendezvous/examples/register_with_identify.rs new file mode 100644 index 00000000000..946346ac15c --- /dev/null +++ b/protocols/rendezvous/examples/register_with_identify.rs @@ -0,0 +1,147 @@ +// 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 futures::StreamExt; +use libp2p::core::identity; +use libp2p::core::PeerId; +use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; +use libp2p::ping::{Ping, PingConfig, PingEvent, PingSuccess}; +use libp2p::rendezvous::Rendezvous; +use libp2p::swarm::Swarm; +use libp2p::swarm::SwarmEvent; +use libp2p::{development_transport, rendezvous}; +use libp2p::{Multiaddr, NetworkBehaviour}; +use libp2p_rendezvous::Namespace; +use std::time::Duration; + +#[async_std::main] +async fn main() { + env_logger::init(); + + let rendezvous_point_address = "/ip4/127.0.0.1/tcp/62649".parse::().unwrap(); + let rendezvous_point = "12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN" + .parse() + .unwrap(); + + let identity = identity::Keypair::generate_ed25519(); + + let mut swarm = Swarm::new( + development_transport(identity.clone()).await.unwrap(), + MyBehaviour { + identify: Identify::new(IdentifyConfig::new( + "rendezvous-example/1.0.0".to_string(), + identity.public(), + )), + rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), + ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), + }, + PeerId::from(identity.public()), + ); + + log::info!("Local peer id: {}", swarm.local_peer_id()); + + let _ = swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()); + + swarm.dial_addr(rendezvous_point_address).unwrap(); + + while let Some(event) = swarm.next().await { + match event { + SwarmEvent::NewListenAddr(addr) => { + log::info!("Listening on {}", addr); + } + SwarmEvent::ConnectionClosed { + peer_id, + cause: Some(error), + .. + } if peer_id == rendezvous_point => { + log::error!("Lost connection to rendezvous point {}", error); + } + // once `/identify` did its job, we know our external address and can register + SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { .. })) => { + swarm.behaviour_mut().rendezvous.register( + Namespace::from_static("rendezvous"), + rendezvous_point, + None, + ); + } + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Registered { + namespace, + ttl, + rendezvous_node, + })) => { + log::info!( + "Registered for namespace '{}' at rendezvous point {} for the next {} seconds", + namespace, + rendezvous_node, + ttl + ); + } + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::RegisterFailed( + error, + ))) => { + log::error!("Failed to register {}", error); + return; + } + SwarmEvent::Behaviour(MyEvent::Ping(PingEvent { + peer, + result: Ok(PingSuccess::Ping { rtt }), + })) if peer != rendezvous_point => { + log::info!("Ping to {} is {}ms", peer, rtt.as_millis()) + } + other => { + log::debug!("Unhandled {:?}", other); + } + } + } +} + +#[derive(Debug)] +enum MyEvent { + Rendezvous(rendezvous::Event), + Identify(IdentifyEvent), + Ping(PingEvent), +} + +impl From for MyEvent { + fn from(event: rendezvous::Event) -> Self { + MyEvent::Rendezvous(event) + } +} + +impl From for MyEvent { + fn from(event: IdentifyEvent) -> Self { + MyEvent::Identify(event) + } +} + +impl From for MyEvent { + fn from(event: PingEvent) -> Self { + MyEvent::Ping(event) + } +} + +#[derive(NetworkBehaviour)] +#[behaviour(event_process = false)] +#[behaviour(out_event = "MyEvent")] +struct MyBehaviour { + identify: Identify, + rendezvous: Rendezvous, + ping: Ping, +} diff --git a/protocols/rendezvous/examples/rendezvous_point.rs b/protocols/rendezvous/examples/rendezvous_point.rs index 93bddef4ed0..478d786542e 100644 --- a/protocols/rendezvous/examples/rendezvous_point.rs +++ b/protocols/rendezvous/examples/rendezvous_point.rs @@ -21,7 +21,6 @@ use futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; -use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p::ping::{Ping, PingEvent}; use libp2p::rendezvous::Rendezvous; use libp2p::swarm::{Swarm, SwarmEvent}; @@ -39,10 +38,6 @@ async fn main() { let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), MyBehaviour { - identify: Identify::new(IdentifyConfig::new( - "rendezvous-example/1.0.0".to_string(), - identity.public(), - )), rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), ping: Ping::default(), }, @@ -93,7 +88,6 @@ async fn main() { #[derive(Debug)] enum MyEvent { Rendezvous(rendezvous::Event), - Identify(IdentifyEvent), Ping(PingEvent), } @@ -103,12 +97,6 @@ impl From for MyEvent { } } -impl From for MyEvent { - fn from(event: IdentifyEvent) -> Self { - MyEvent::Identify(event) - } -} - impl From for MyEvent { fn from(event: PingEvent) -> Self { MyEvent::Ping(event) @@ -119,7 +107,6 @@ impl From for MyEvent { #[behaviour(event_process = false)] #[behaviour(out_event = "MyEvent")] struct MyBehaviour { - identify: Identify, rendezvous: Rendezvous, ping: Ping, } From 5f10abf52cb0a8ba9fe61e6bb6de4043036374e8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 26 Aug 2021 12:40:56 +1000 Subject: [PATCH 223/242] Fix test compile errors --- protocols/rendezvous/examples/register.rs | 4 ++-- .../rendezvous/examples/register_with_identify.rs | 4 ++-- protocols/rendezvous/tests/harness/mod.rs | 15 ++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 3299d80fed2..ce14dc9d12a 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -64,8 +64,8 @@ async fn main() { while let Some(event) = swarm.next().await { match event { - SwarmEvent::NewListenAddr(addr) => { - log::info!("Listening on {}", addr); + SwarmEvent::NewListenAddr { address, .. } => { + log::info!("Listening on {}", address); } SwarmEvent::ConnectionClosed { peer_id, diff --git a/protocols/rendezvous/examples/register_with_identify.rs b/protocols/rendezvous/examples/register_with_identify.rs index 946346ac15c..ecc216cbe9b 100644 --- a/protocols/rendezvous/examples/register_with_identify.rs +++ b/protocols/rendezvous/examples/register_with_identify.rs @@ -63,8 +63,8 @@ async fn main() { while let Some(event) = swarm.next().await { match event { - SwarmEvent::NewListenAddr(addr) => { - log::info!("Listening on {}", addr); + SwarmEvent::NewListenAddr { address, .. } => { + log::info!("Listening on {}", address); } SwarmEvent::ConnectionClosed { peer_id, diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index e0a4bb3661b..b34dce01173 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -179,15 +179,16 @@ where } async fn listen_on_random_memory_address(&mut self) -> Multiaddr { - let multiaddr = get_rand_memory_address(); - - self.listen_on(multiaddr.clone()).unwrap(); + let memory_addr_listener_id = self.listen_on(get_rand_memory_address()).unwrap(); // block until we are actually listening - loop { + let multiaddr = loop { match self.select_next_some().await { - SwarmEvent::NewListenAddr(addr) if addr == multiaddr => { - break; + SwarmEvent::NewListenAddr { + address, + listener_id, + } if listener_id == memory_addr_listener_id => { + break address; } other => { log::debug!( @@ -196,7 +197,7 @@ where ); } } - } + }; // Memory addresses are externally reachable because they all share the same memory-space. self.add_external_address(multiaddr.clone(), AddressScore::Infinite); From 7ec294f24a3eb187f805b3bd31639683d7807b54 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 26 Aug 2021 12:41:11 +1000 Subject: [PATCH 224/242] Fix warnings in tests --- muxers/mplex/src/io.rs | 1 - protocols/identify/src/protocol.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/muxers/mplex/src/io.rs b/muxers/mplex/src/io.rs index 80da197a965..e475b15763b 100644 --- a/muxers/mplex/src/io.rs +++ b/muxers/mplex/src/io.rs @@ -1104,7 +1104,6 @@ mod tests { use async_std::task; use asynchronous_codec::{Decoder, Encoder}; use bytes::BytesMut; - use futures::prelude::*; use quickcheck::*; use rand::prelude::*; use std::collections::HashSet; diff --git a/protocols/identify/src/protocol.rs b/protocols/identify/src/protocol.rs index 9604e660e9f..2df49bf54f3 100644 --- a/protocols/identify/src/protocol.rs +++ b/protocols/identify/src/protocol.rs @@ -258,7 +258,7 @@ fn parse_proto_msg(msg: impl AsRef<[u8]>) -> Result { #[cfg(test)] mod tests { use super::*; - use futures::{channel::oneshot, prelude::*}; + use futures::channel::oneshot; use libp2p_core::{ identity, upgrade::{self, apply_inbound, apply_outbound}, From c56ab55ec4fe7bfa27dfb29b0c96dc44d3ae4f87 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 26 Aug 2021 12:42:33 +1000 Subject: [PATCH 225/242] Remove GlobalSpawnTokioExecutor --- protocols/rendezvous/tests/harness/mod.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index b34dce01173..90ab7f1548d 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -36,16 +36,6 @@ use std::fmt::Debug; use std::pin::Pin; use std::time::Duration; -/// An adaptor struct for libp2p that spawns futures into the current -/// thread-local runtime. -struct GlobalSpawnTokioExecutor; - -impl Executor for GlobalSpawnTokioExecutor { - fn exec(&self, future: Pin + Send>>) { - let _ = tokio::spawn(future); - } -} - pub fn new_swarm(behaviour_fn: F) -> Swarm where B: NetworkBehaviour, @@ -73,7 +63,9 @@ where .boxed(); SwarmBuilder::new(transport, behaviour_fn(peer_id, identity), peer_id) - .executor(Box::new(GlobalSpawnTokioExecutor)) + .executor(Box::new(|future| { + let _ = tokio::spawn(future); + })) .build() } From 88fa94bea9d7ec64d9460e975c0d1cd3c9696789 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 26 Aug 2021 12:45:37 +1000 Subject: [PATCH 226/242] Document design of `poll_substreams` --- protocols/rendezvous/src/substream_handler.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs index 9504d9cc9d9..f3a683f15f8 100644 --- a/protocols/rendezvous/src/substream_handler.rs +++ b/protocols/rendezvous/src/substream_handler.rs @@ -158,6 +158,9 @@ impl } } +/// Poll all substreams within the given HashMap. +/// +/// This is defined as a separate function because we call it with two different fields stored within [`SubstreamProtocolsHandler`]. fn poll_substreams( substreams: &mut HashMap, cx: &mut Context<'_>, From f1b61a65c33ab3ec0a41bd898c1fff2ae2e3e025 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 26 Aug 2021 13:18:15 +1000 Subject: [PATCH 227/242] Minor formatting fixes --- protocols/rendezvous/src/behaviour.rs | 1 + protocols/rendezvous/src/codec.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index e532870e1b7..26d6afac5a8 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -427,6 +427,7 @@ fn handle_inbound_event( } } } + fn handle_outbound_event( event: outbound::OutEvent, peer_id: PeerId, diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 55cc358f6d7..6375c11e3ca 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -230,7 +230,7 @@ impl Encoder for RendezvousCodec { .encode(&mut buf) .expect("Buffer has sufficient capacity"); - // length prefix the protobuf message, ensuring the max limit is not hit + // Length prefix the protobuf message, ensuring the max limit is not hit self.length_codec.encode(Bytes::from(buf), dst)?; Ok(()) From c3fdd0b8ca3308990d87cdcecd209755329744b3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 26 Aug 2021 13:19:40 +1000 Subject: [PATCH 228/242] Use a u64 to represent `RegistrationId` --- protocols/rendezvous/src/behaviour.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 26d6afac5a8..d0c16c21cbb 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -39,7 +39,6 @@ use std::collections::{HashMap, HashSet, VecDeque}; use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::Duration; -use uuid::Uuid; pub struct Rendezvous { events: VecDeque>, @@ -482,11 +481,11 @@ fn handle_outbound_event( } #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] -struct RegistrationId(Uuid); +struct RegistrationId(u64); impl RegistrationId { fn new() -> Self { - Self(Uuid::new_v4()) + Self(rand::random()) } } From 9b1dda8f1c43c8e69820b92b753ccc9a2f18d09b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 26 Aug 2021 13:28:10 +1000 Subject: [PATCH 229/242] Add docs to public functions of rendezvous --- protocols/rendezvous/src/behaviour.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index d0c16c21cbb..682a5fb0242 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -79,6 +79,7 @@ impl Default for Config { } impl Rendezvous { + /// Create a new instance of the rendezvous [`NetworkBehaviour`]. pub fn new(keypair: Keypair, config: Config) -> Self { Self { events: Default::default(), @@ -89,11 +90,16 @@ impl Rendezvous { } } + /// Register our external addresses in the given namespace with the given rendezvous peer. + /// + /// External addresses are either manually added via [`libp2p_swarm::Swarm::add_external_address`] or reported + /// by other [`NetworkBehaviour`]s via [`NetworkBehaviourAction::ReportObservedAddr`]. pub fn register(&mut self, namespace: Namespace, rendezvous_node: PeerId, ttl: Option) { self.pending_register_requests .push((namespace, rendezvous_node, ttl)); } + /// 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(NetworkBehaviourAction::NotifyHandler { @@ -105,6 +111,13 @@ impl Rendezvous { }); } + /// Discover other peers at a given rendezvous peer. + /// + /// If desired, the registrations can be filtered by a namespace. + /// If no namespace is given, peers from all namespaces will be returned. + /// A successfully discovery returns a cookie within [`Event::Discovered`]. + /// Such a cookie can be used to only fetch the _delta_ of registrations since + /// the cookie was acquired. pub fn discover( &mut self, ns: Option, From 433b3b5cf18b8e8aef1dc8ebd06e2223c6e7e8f3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 26 Aug 2021 13:39:43 +1000 Subject: [PATCH 230/242] Use naming as per https://github.com/libp2p/rust-libp2p/discussions/2174 --- protocols/rendezvous/examples/discover.rs | 11 +-- protocols/rendezvous/examples/register.rs | 8 +- .../examples/register_with_identify.rs | 8 +- .../rendezvous/examples/rendezvous_point.rs | 5 +- protocols/rendezvous/src/behaviour.rs | 6 +- protocols/rendezvous/src/lib.rs | 2 +- protocols/rendezvous/tests/harness/mod.rs | 4 +- protocols/rendezvous/tests/rendezvous.rs | 93 +++++++++++-------- 8 files changed, 71 insertions(+), 66 deletions(-) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 7cf60dac998..b47601ea65d 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -23,13 +23,12 @@ use libp2p::core::identity; use libp2p::core::PeerId; use libp2p::multiaddr::Protocol; use libp2p::ping::{Ping, PingConfig, PingEvent, PingSuccess}; -use libp2p::rendezvous::{Namespace, Rendezvous}; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; use libp2p::{development_transport, rendezvous, Multiaddr}; use std::time::Duration; -const NAMESPACE: &'static str = "rendezvous"; +const NAMESPACE: &str = "rendezvous"; #[async_std::main] async fn main() { @@ -44,7 +43,7 @@ async fn main() { let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), MyBehaviour { - rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), + rendezvous: rendezvous::Behaviour::new(identity.clone(), rendezvous::Config::default()), ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), @@ -63,7 +62,7 @@ async fn main() { ); swarm.behaviour_mut().rendezvous.discover( - Some(Namespace::new(NAMESPACE.to_string()).unwrap()), + Some(rendezvous::Namespace::new(NAMESPACE.to_string()).unwrap()), None, None, rendezvous_point, @@ -89,7 +88,7 @@ async fn main() { let peer = registration.record.peer_id(); log::info!("Discovered peer {} at {}", peer, address); - let p2p_suffix = Protocol::P2p(peer.as_ref().clone()); + let p2p_suffix = Protocol::P2p(*peer.as_ref()); let address_with_p2p = if !address.ends_with(&Multiaddr::empty().with(p2p_suffix.clone())) { address.clone().with(p2p_suffix) @@ -136,6 +135,6 @@ impl From for MyEvent { #[behaviour(event_process = false)] #[behaviour(out_event = "MyEvent")] struct MyBehaviour { - rendezvous: Rendezvous, + rendezvous: rendezvous::Behaviour, ping: Ping, } diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index ce14dc9d12a..2e000ac48e2 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -22,12 +22,10 @@ use futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; use libp2p::ping::{Ping, PingConfig, PingEvent, PingSuccess}; -use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; use libp2p::{development_transport, rendezvous}; use libp2p::{Multiaddr, NetworkBehaviour}; -use libp2p_rendezvous::Namespace; use libp2p_swarm::AddressScore; use std::time::Duration; @@ -45,7 +43,7 @@ async fn main() { let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), MyBehaviour { - rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), + rendezvous: rendezvous::Behaviour::new(identity.clone(), rendezvous::Config::default()), ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), @@ -76,7 +74,7 @@ async fn main() { } SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == rendezvous_point => { swarm.behaviour_mut().rendezvous.register( - Namespace::from_static("rendezvous"), + rendezvous::Namespace::from_static("rendezvous"), rendezvous_point, None, ); @@ -136,6 +134,6 @@ impl From for MyEvent { #[behaviour(event_process = false)] #[behaviour(out_event = "MyEvent")] struct MyBehaviour { - rendezvous: Rendezvous, + rendezvous: rendezvous::Behaviour, ping: Ping, } diff --git a/protocols/rendezvous/examples/register_with_identify.rs b/protocols/rendezvous/examples/register_with_identify.rs index ecc216cbe9b..84f7be24a99 100644 --- a/protocols/rendezvous/examples/register_with_identify.rs +++ b/protocols/rendezvous/examples/register_with_identify.rs @@ -23,12 +23,10 @@ use libp2p::core::identity; use libp2p::core::PeerId; use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent}; use libp2p::ping::{Ping, PingConfig, PingEvent, PingSuccess}; -use libp2p::rendezvous::Rendezvous; use libp2p::swarm::Swarm; use libp2p::swarm::SwarmEvent; use libp2p::{development_transport, rendezvous}; use libp2p::{Multiaddr, NetworkBehaviour}; -use libp2p_rendezvous::Namespace; use std::time::Duration; #[async_std::main] @@ -49,7 +47,7 @@ async fn main() { "rendezvous-example/1.0.0".to_string(), identity.public(), )), - rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), + rendezvous: rendezvous::Behaviour::new(identity.clone(), rendezvous::Config::default()), ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), @@ -76,7 +74,7 @@ async fn main() { // once `/identify` did its job, we know our external address and can register SwarmEvent::Behaviour(MyEvent::Identify(IdentifyEvent::Received { .. })) => { swarm.behaviour_mut().rendezvous.register( - Namespace::from_static("rendezvous"), + rendezvous::Namespace::from_static("rendezvous"), rendezvous_point, None, ); @@ -142,6 +140,6 @@ impl From for MyEvent { #[behaviour(out_event = "MyEvent")] struct MyBehaviour { identify: Identify, - rendezvous: Rendezvous, + rendezvous: rendezvous::Behaviour, ping: Ping, } diff --git a/protocols/rendezvous/examples/rendezvous_point.rs b/protocols/rendezvous/examples/rendezvous_point.rs index 478d786542e..a161dd757f9 100644 --- a/protocols/rendezvous/examples/rendezvous_point.rs +++ b/protocols/rendezvous/examples/rendezvous_point.rs @@ -22,7 +22,6 @@ use futures::StreamExt; use libp2p::core::identity; use libp2p::core::PeerId; use libp2p::ping::{Ping, PingEvent}; -use libp2p::rendezvous::Rendezvous; use libp2p::swarm::{Swarm, SwarmEvent}; use libp2p::NetworkBehaviour; use libp2p::{development_transport, rendezvous}; @@ -38,7 +37,7 @@ async fn main() { let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), MyBehaviour { - rendezvous: Rendezvous::new(identity.clone(), rendezvous::Config::default()), + rendezvous: rendezvous::Behaviour::new(identity.clone(), rendezvous::Config::default()), ping: Ping::default(), }, PeerId::from(identity.public()), @@ -107,6 +106,6 @@ impl From for MyEvent { #[behaviour(event_process = false)] #[behaviour(out_event = "MyEvent")] struct MyBehaviour { - rendezvous: Rendezvous, + rendezvous: rendezvous::Behaviour, ping: Ping, } diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/behaviour.rs index 682a5fb0242..3ec45e164f6 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/behaviour.rs @@ -40,7 +40,7 @@ use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::Duration; -pub struct Rendezvous { +pub struct Behaviour { events: VecDeque>, registrations: Registrations, keypair: Keypair, @@ -78,7 +78,7 @@ impl Default for Config { } } -impl Rendezvous { +impl Behaviour { /// Create a new instance of the rendezvous [`NetworkBehaviour`]. pub fn new(keypair: Keypair, config: Config) -> Self { Self { @@ -201,7 +201,7 @@ pub enum Event { RegistrationExpired(Registration), } -impl NetworkBehaviour for Rendezvous { +impl NetworkBehaviour for Behaviour { type ProtocolsHandler = SubstreamProtocolsHandler; type OutEvent = Event; diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index c80b7a5b1b6..e86a620e859 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -pub use self::behaviour::{Config, Event, RegisterError, Rendezvous}; +pub use self::behaviour::{Behaviour, Config, Event, RegisterError}; pub use self::codec::{ErrorCode, Namespace, NamespaceTooLong, Registration, Ttl}; mod behaviour; diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 90ab7f1548d..d57992c5f8b 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -20,20 +20,18 @@ use async_trait::async_trait; use futures::stream::FusedStream; -use futures::Future; use futures::StreamExt; use futures::{future, Stream}; use libp2p::core::muxing::StreamMuxerBox; use libp2p::core::transport::upgrade::Version; use libp2p::core::transport::MemoryTransport; use libp2p::core::upgrade::SelectUpgrade; -use libp2p::core::{identity, Executor, Multiaddr, PeerId, Transport}; +use libp2p::core::{identity, Multiaddr, PeerId, Transport}; use libp2p::mplex::MplexConfig; use libp2p::noise::{Keypair, NoiseConfig, X25519Spec}; use libp2p::swarm::{AddressScore, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; use libp2p::yamux::YamuxConfig; use std::fmt::Debug; -use std::pin::Pin; use std::time::Duration; pub fn new_swarm(behaviour_fn: F) -> Swarm diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 0e38fe42d80..43ac231c806 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -22,12 +22,9 @@ pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; use futures::StreamExt; -use libp2p::rendezvous::{Namespace, Ttl}; use libp2p_core::identity; use libp2p_core::PeerId; -use libp2p_rendezvous::{ - Config, ErrorCode, Event, RegisterError, Registration, Rendezvous, DEFAULT_TTL, -}; +use libp2p_rendezvous as rendezvous; use libp2p_swarm::{Swarm, SwarmEvent}; #[tokio::test] @@ -35,14 +32,14 @@ async fn given_successful_registration_then_successful_discovery() { let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; - let namespace = Namespace::from_static("some-namespace"); + let namespace = rendezvous::Namespace::from_static("some-namespace"); let _ = test.alice .behaviour_mut() .register(namespace.clone(), *test.robert.local_peer_id(), None); - test.assert_successful_registration(namespace.clone(), DEFAULT_TTL) + test.assert_successful_registration(namespace.clone(), rendezvous::DEFAULT_TTL) .await; test.bob.behaviour_mut().discover( @@ -52,8 +49,12 @@ async fn given_successful_registration_then_successful_discovery() { *test.robert.local_peer_id(), ); - test.assert_successful_discovery(namespace.clone(), DEFAULT_TTL, *test.alice.local_peer_id()) - .await; + test.assert_successful_discovery( + namespace.clone(), + rendezvous::DEFAULT_TTL, + *test.alice.local_peer_id(), + ) + .await; } #[tokio::test] @@ -61,7 +62,7 @@ async fn given_successful_registration_then_refresh_ttl() { let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; - let namespace = Namespace::from_static("some-namespace"); + let namespace = rendezvous::Namespace::from_static("some-namespace"); let refesh_ttl = 10_000; @@ -70,7 +71,7 @@ async fn given_successful_registration_then_refresh_ttl() { .behaviour_mut() .register(namespace.clone(), *test.robert.local_peer_id(), None); - test.assert_successful_registration(namespace.clone(), DEFAULT_TTL) + test.assert_successful_registration(namespace.clone(), rendezvous::DEFAULT_TTL) .await; test.bob.behaviour_mut().discover( @@ -80,8 +81,12 @@ async fn given_successful_registration_then_refresh_ttl() { *test.robert.local_peer_id(), ); - test.assert_successful_discovery(namespace.clone(), DEFAULT_TTL, *test.alice.local_peer_id()) - .await; + test.assert_successful_discovery( + namespace.clone(), + rendezvous::DEFAULT_TTL, + *test.alice.local_peer_id(), + ) + .await; test.alice.behaviour_mut().register( namespace.clone(), @@ -108,7 +113,7 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; - let namespace = Namespace::from_static("some-namespace"); + let namespace = rendezvous::Namespace::from_static("some-namespace"); test.alice.behaviour_mut().register( namespace.clone(), @@ -118,10 +123,10 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { match await_events_or_timeout(&mut test.robert, &mut test.alice).await { ( - SwarmEvent::Behaviour(Event::PeerNotRegistered { .. }), - SwarmEvent::Behaviour(Event::RegisterFailed(RegisterError::Remote {error , ..})), + SwarmEvent::Behaviour(rendezvous::Event::PeerNotRegistered { .. }), + SwarmEvent::Behaviour(rendezvous::Event::RegisterFailed(rendezvous::RegisterError::Remote {error , ..})), ) => { - assert_eq!(error, ErrorCode::InvalidTtl); + assert_eq!(error, rendezvous::ErrorCode::InvalidTtl); } (rendezvous_swarm_event, registration_swarm_event) => panic!( "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", @@ -148,7 +153,7 @@ async fn discover_allows_for_dial_by_peer_id() { } }); - let namespace = Namespace::from_static("some-namespace"); + let namespace = rendezvous::Namespace::from_static("some-namespace"); alice .behaviour_mut() @@ -158,8 +163,8 @@ async fn discover_allows_for_dial_by_peer_id() { match await_events_or_timeout(&mut alice, &mut bob).await { ( - SwarmEvent::Behaviour(Event::Registered { .. }), - SwarmEvent::Behaviour(Event::Discovered { .. }), + SwarmEvent::Behaviour(rendezvous::Event::Registered { .. }), + SwarmEvent::Behaviour(rendezvous::Event::Discovered { .. }), ) => {} _ => panic!("bad event combination emitted"), }; @@ -196,7 +201,7 @@ async fn eve_cannot_register() { let _ = env_logger::try_init(); let mut test = RendezvousTest::setup().await; - let namespace = Namespace::from_static("some-namespace"); + let namespace = rendezvous::Namespace::from_static("some-namespace"); test.eve.behaviour_mut().register( namespace.clone(), @@ -206,10 +211,10 @@ async fn eve_cannot_register() { match await_events_or_timeout(&mut test.robert, &mut test.eve).await { ( - SwarmEvent::Behaviour(Event::PeerNotRegistered { .. }), - SwarmEvent::Behaviour(Event::RegisterFailed(RegisterError::Remote { error: err_code , ..})), + SwarmEvent::Behaviour(rendezvous::Event::PeerNotRegistered { .. }), + SwarmEvent::Behaviour(rendezvous::Event::RegisterFailed(rendezvous::RegisterError::Remote { error: err_code , ..})), ) => { - assert_eq!(err_code, ErrorCode::NotAuthorized); + assert_eq!(err_code, rendezvous::ErrorCode::NotAuthorized); } (rendezvous_swarm_event, registration_swarm_event) => panic!( "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", @@ -223,21 +228,27 @@ async fn eve_cannot_register() { /// In all cases, Alice would like to connect to Bob with Robert acting as a rendezvous point. /// Eve is an evil actor that tries to act maliciously. struct RendezvousTest { - pub alice: Swarm, - pub bob: Swarm, - pub eve: Swarm, - pub robert: Swarm, + pub alice: Swarm, + pub bob: Swarm, + pub eve: Swarm, + pub robert: Swarm, } impl RendezvousTest { pub async fn setup() -> Self { - let mut alice = new_swarm(|_, identity| Rendezvous::new(identity, Config::default())); + let mut alice = new_swarm(|_, identity| { + rendezvous::Behaviour::new(identity, rendezvous::Config::default()) + }); alice.listen_on_random_memory_address().await; - let mut bob = new_swarm(|_, identity| Rendezvous::new(identity, Config::default())); + let mut bob = new_swarm(|_, identity| { + rendezvous::Behaviour::new(identity, rendezvous::Config::default()) + }); bob.listen_on_random_memory_address().await; - let mut robert = new_swarm(|_, identity| Rendezvous::new(identity, Config::default())); + let mut robert = new_swarm(|_, identity| { + rendezvous::Behaviour::new(identity, rendezvous::Config::default()) + }); robert.listen_on_random_memory_address().await; let mut eve = { @@ -245,7 +256,9 @@ impl RendezvousTest { // Due to the type-safe API of the `Rendezvous` behaviour and `PeerRecord`, we actually cannot construct a bad `PeerRecord` (i.e. one that is claims to be someone else). // As such, the best we can do is hand eve a completely different keypair from what she is using to authenticate her connection. let someone_else = identity::Keypair::generate_ed25519(); - let mut eve = new_swarm(move |_, _| Rendezvous::new(someone_else, Config::default())); + let mut eve = new_swarm(move |_, _| { + rendezvous::Behaviour::new(someone_else, rendezvous::Config::default()) + }); eve.listen_on_random_memory_address().await; eve @@ -265,13 +278,13 @@ impl RendezvousTest { pub async fn assert_successful_registration( &mut self, - expected_namespace: Namespace, - expected_ttl: Ttl, + expected_namespace: rendezvous::Namespace, + expected_ttl: rendezvous::Ttl, ) { match await_events_or_timeout(&mut self.robert, &mut self.alice).await { ( - SwarmEvent::Behaviour(Event::PeerRegistered { peer, registration }), - SwarmEvent::Behaviour(Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), + SwarmEvent::Behaviour(rendezvous::Event::PeerRegistered { peer, registration }), + SwarmEvent::Behaviour(rendezvous::Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), ) => { assert_eq!(&peer, self.alice.local_peer_id()); assert_eq!(&rendezvous_node, self.robert.local_peer_id()); @@ -288,16 +301,16 @@ impl RendezvousTest { pub async fn assert_successful_discovery( &mut self, - expected_namespace: Namespace, - expected_ttl: Ttl, + expected_namespace: rendezvous::Namespace, + expected_ttl: rendezvous::Ttl, expected_peer_id: PeerId, ) { match await_events_or_timeout(&mut self.robert, &mut self.bob).await { ( - SwarmEvent::Behaviour(Event::DiscoverServed { .. }), - SwarmEvent::Behaviour(Event::Discovered { registrations, .. }), + SwarmEvent::Behaviour(rendezvous::Event::DiscoverServed { .. }), + SwarmEvent::Behaviour(rendezvous::Event::Discovered { registrations, .. }), ) => match registrations.as_slice() { - [Registration { + [rendezvous::Registration { namespace, record, ttl, From d85e38b19582a9703147fbfd18dfa98e2681cedd Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 1 Sep 2021 13:50:33 +1000 Subject: [PATCH 231/242] Expose `wasm-bindgen` feature instead of automatically activating it --- Cargo.toml | 2 +- protocols/rendezvous/Cargo.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8bf6c6fcf86..fb96acf0ed6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ rendezvous = ["libp2p-rendezvous"] tcp-async-io = ["libp2p-tcp", "libp2p-tcp/async-io"] tcp-tokio = ["libp2p-tcp", "libp2p-tcp/tokio"] uds = ["libp2p-uds"] -wasm-bindgen = ["parking_lot/wasm-bindgen"] +wasm-bindgen = ["parking_lot/wasm-bindgen", "libp2p-rendezvous/wasm-bindgen"] wasm-ext = ["libp2p-wasm-ext"] wasm-ext-websocket = ["wasm-ext", "libp2p-wasm-ext/websocket"] websocket = ["libp2p-websocket"] diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 07d0bd8a95d..4b91a62a392 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -25,9 +25,6 @@ sha2 = "0.9" rand = "0.8" wasm-timer = "0.2" -[target.'cfg(target_arch = "wasm32")'.dependencies] -uuid = { version = "0.8", features = ["wasm-bindgen"] } - [dev-dependencies] libp2p = { path = "../.." } rand = "0.8" @@ -36,5 +33,8 @@ env_logger = "0.8" async-trait = "0.1" tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } # TODO: REMOVE FOR PRODUCTION USE +[features] +wasm-bindgen = ["uuid/wasm-bindgen"] + [build-dependencies] prost-build = "0.7" From d5cb328199914a4ccd874a1f2ca0a405651c5036 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 27 Aug 2021 19:20:20 +1000 Subject: [PATCH 232/242] Split behaviour into client and server --- protocols/rendezvous/examples/discover.rs | 12 +- protocols/rendezvous/examples/register.rs | 18 +- .../examples/register_with_identify.rs | 18 +- .../rendezvous/examples/rendezvous_point.rs | 27 +- protocols/rendezvous/src/client.rs | 294 ++++++++++++++++++ protocols/rendezvous/src/handler.rs | 11 +- protocols/rendezvous/src/handler/inbound.rs | 11 +- protocols/rendezvous/src/handler/outbound.rs | 11 +- protocols/rendezvous/src/lib.rs | 5 +- .../src/{behaviour.rs => server.rs} | 238 ++------------ protocols/rendezvous/src/substream_handler.rs | 135 ++++++-- protocols/rendezvous/tests/rendezvous.rs | 125 ++++++-- 12 files changed, 590 insertions(+), 315 deletions(-) create mode 100644 protocols/rendezvous/src/client.rs rename protocols/rendezvous/src/{behaviour.rs => server.rs} (74%) diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index b47601ea65d..28abdf6d102 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -43,7 +43,7 @@ async fn main() { let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), MyBehaviour { - rendezvous: rendezvous::Behaviour::new(identity.clone(), rendezvous::Config::default()), + rendezvous: rendezvous::client::Behaviour::new(identity.clone()), ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), @@ -79,7 +79,7 @@ async fn main() { ); return; } - SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Discovered { + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::client::Event::Discovered { registrations, .. })) => { @@ -115,12 +115,12 @@ async fn main() { #[derive(Debug)] enum MyEvent { - Rendezvous(rendezvous::Event), + Rendezvous(rendezvous::client::Event), Ping(PingEvent), } -impl From for MyEvent { - fn from(event: rendezvous::Event) -> Self { +impl From for MyEvent { + fn from(event: rendezvous::client::Event) -> Self { MyEvent::Rendezvous(event) } } @@ -135,6 +135,6 @@ impl From for MyEvent { #[behaviour(event_process = false)] #[behaviour(out_event = "MyEvent")] struct MyBehaviour { - rendezvous: rendezvous::Behaviour, + rendezvous: rendezvous::client::Behaviour, ping: Ping, } diff --git a/protocols/rendezvous/examples/register.rs b/protocols/rendezvous/examples/register.rs index 2e000ac48e2..2c02bca12ef 100644 --- a/protocols/rendezvous/examples/register.rs +++ b/protocols/rendezvous/examples/register.rs @@ -43,7 +43,7 @@ async fn main() { let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), MyBehaviour { - rendezvous: rendezvous::Behaviour::new(identity.clone(), rendezvous::Config::default()), + rendezvous: rendezvous::client::Behaviour::new(identity.clone()), ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), @@ -81,7 +81,7 @@ async fn main() { log::info!("Connection established with rendezvous point {}", peer_id); } // once `/identify` did its job, we know our external address and can register - SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Registered { + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::client::Event::Registered { namespace, ttl, rendezvous_node, @@ -93,9 +93,9 @@ async fn main() { ttl ); } - SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::RegisterFailed( - error, - ))) => { + SwarmEvent::Behaviour(MyEvent::Rendezvous( + rendezvous::client::Event::RegisterFailed(error), + )) => { log::error!("Failed to register {}", error); return; } @@ -114,12 +114,12 @@ async fn main() { #[derive(Debug)] enum MyEvent { - Rendezvous(rendezvous::Event), + Rendezvous(rendezvous::client::Event), Ping(PingEvent), } -impl From for MyEvent { - fn from(event: rendezvous::Event) -> Self { +impl From for MyEvent { + fn from(event: rendezvous::client::Event) -> Self { MyEvent::Rendezvous(event) } } @@ -134,6 +134,6 @@ impl From for MyEvent { #[behaviour(event_process = false)] #[behaviour(out_event = "MyEvent")] struct MyBehaviour { - rendezvous: rendezvous::Behaviour, + rendezvous: rendezvous::client::Behaviour, ping: Ping, } diff --git a/protocols/rendezvous/examples/register_with_identify.rs b/protocols/rendezvous/examples/register_with_identify.rs index 84f7be24a99..25450d4ab02 100644 --- a/protocols/rendezvous/examples/register_with_identify.rs +++ b/protocols/rendezvous/examples/register_with_identify.rs @@ -47,7 +47,7 @@ async fn main() { "rendezvous-example/1.0.0".to_string(), identity.public(), )), - rendezvous: rendezvous::Behaviour::new(identity.clone(), rendezvous::Config::default()), + rendezvous: rendezvous::client::Behaviour::new(identity.clone()), ping: Ping::new(PingConfig::new().with_interval(Duration::from_secs(1))), }, PeerId::from(identity.public()), @@ -79,7 +79,7 @@ async fn main() { None, ); } - SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::Registered { + SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::client::Event::Registered { namespace, ttl, rendezvous_node, @@ -91,9 +91,9 @@ async fn main() { ttl ); } - SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::RegisterFailed( - error, - ))) => { + SwarmEvent::Behaviour(MyEvent::Rendezvous( + rendezvous::client::Event::RegisterFailed(error), + )) => { log::error!("Failed to register {}", error); return; } @@ -112,13 +112,13 @@ async fn main() { #[derive(Debug)] enum MyEvent { - Rendezvous(rendezvous::Event), + Rendezvous(rendezvous::client::Event), Identify(IdentifyEvent), Ping(PingEvent), } -impl From for MyEvent { - fn from(event: rendezvous::Event) -> Self { +impl From for MyEvent { + fn from(event: rendezvous::client::Event) -> Self { MyEvent::Rendezvous(event) } } @@ -140,6 +140,6 @@ impl From for MyEvent { #[behaviour(out_event = "MyEvent")] struct MyBehaviour { identify: Identify, - rendezvous: rendezvous::Behaviour, + rendezvous: rendezvous::client::Behaviour, ping: Ping, } diff --git a/protocols/rendezvous/examples/rendezvous_point.rs b/protocols/rendezvous/examples/rendezvous_point.rs index a161dd757f9..bbbe9b973be 100644 --- a/protocols/rendezvous/examples/rendezvous_point.rs +++ b/protocols/rendezvous/examples/rendezvous_point.rs @@ -37,7 +37,7 @@ async fn main() { let mut swarm = Swarm::new( development_transport(identity.clone()).await.unwrap(), MyBehaviour { - rendezvous: rendezvous::Behaviour::new(identity.clone(), rendezvous::Config::default()), + rendezvous: rendezvous::server::Behaviour::new(rendezvous::server::Config::default()), ping: Ping::default(), }, PeerId::from(identity.public()), @@ -57,20 +57,21 @@ async fn main() { SwarmEvent::ConnectionClosed { peer_id, .. } => { log::info!("Disconnected from {}", peer_id); } - SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::PeerRegistered { - peer, - registration, - })) => { + SwarmEvent::Behaviour(MyEvent::Rendezvous( + rendezvous::server::Event::PeerRegistered { peer, registration }, + )) => { log::info!( "Peer {} registered for namespace '{}'", peer, registration.namespace ); } - SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::Event::DiscoverServed { - enquirer, - registrations, - })) => { + SwarmEvent::Behaviour(MyEvent::Rendezvous( + rendezvous::server::Event::DiscoverServed { + enquirer, + registrations, + }, + )) => { log::info!( "Served peer {} with {} registrations", enquirer, @@ -86,12 +87,12 @@ async fn main() { #[derive(Debug)] enum MyEvent { - Rendezvous(rendezvous::Event), + Rendezvous(rendezvous::server::Event), Ping(PingEvent), } -impl From for MyEvent { - fn from(event: rendezvous::Event) -> Self { +impl From for MyEvent { + fn from(event: rendezvous::server::Event) -> Self { MyEvent::Rendezvous(event) } } @@ -106,6 +107,6 @@ impl From for MyEvent { #[behaviour(event_process = false)] #[behaviour(out_event = "MyEvent")] struct MyBehaviour { - rendezvous: rendezvous::Behaviour, + rendezvous: rendezvous::server::Behaviour, ping: Ping, } diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs new file mode 100644 index 00000000000..e51f6542907 --- /dev/null +++ b/protocols/rendezvous/src/client.rs @@ -0,0 +1,294 @@ +// 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, Namespace, NewRegistration, Registration, Ttl}; +use crate::handler; +use crate::handler::outbound; +use crate::handler::outbound::OpenInfo; +use crate::substream_handler::SubstreamProtocolsHandler; +use libp2p_core::connection::ConnectionId; +use libp2p_core::identity::error::SigningError; +use libp2p_core::identity::Keypair; +use libp2p_core::{Multiaddr, PeerId, PeerRecord}; +use libp2p_swarm::{ + NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, +}; +use std::collections::{HashMap, VecDeque}; +use std::task::{Context, Poll}; +use std::time::Duration; + +pub struct Behaviour { + events: VecDeque>, + keypair: Keypair, + pending_register_requests: Vec<(Namespace, PeerId, Option)>, + + /// Hold addresses of all peers that we have discovered so far. + /// + /// Storing these internally allows us to assist the [`libp2p_swarm::Swarm`] in dialing by returning addresses from [`NetworkBehaviour::addresses_of_peer`]. + discovered_peers: HashMap<(PeerId, Namespace), Vec>, +} + +impl Behaviour { + /// Create a new instance of the rendezvous [`NetworkBehaviour`]. + pub fn new(keypair: Keypair) -> Self { + Self { + events: Default::default(), + keypair, + pending_register_requests: vec![], + discovered_peers: Default::default(), + } + } + + /// Register our external addresses in the given namespace with the given rendezvous peer. + /// + /// External addresses are either manually added via [`libp2p_swarm::Swarm::add_external_address`] or reported + /// by other [`NetworkBehaviour`]s via [`NetworkBehaviourAction::ReportObservedAddr`]. + pub fn register(&mut self, namespace: Namespace, rendezvous_node: PeerId, ttl: Option) { + self.pending_register_requests + .push((namespace, rendezvous_node, ttl)); + } + + /// 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(NetworkBehaviourAction::NotifyHandler { + peer_id: rendezvous_node, + event: handler::OutboundInEvent::NewSubstream { + open_info: OpenInfo::UnregisterRequest(namespace), + }, + handler: NotifyHandler::Any, + }); + } + + /// Discover other peers at a given rendezvous peer. + /// + /// If desired, the registrations can be filtered by a namespace. + /// If no namespace is given, peers from all namespaces will be returned. + /// A successfully discovery returns a cookie within [`Event::Discovered`]. + /// Such a cookie can be used to only fetch the _delta_ of registrations since + /// the cookie was acquired. + pub fn discover( + &mut self, + ns: Option, + cookie: Option, + limit: Option, + rendezvous_node: PeerId, + ) { + self.events + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id: rendezvous_node, + event: handler::OutboundInEvent::NewSubstream { + open_info: OpenInfo::DiscoverRequest { + namespace: ns, + cookie, + 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), +} + +impl NetworkBehaviour for Behaviour { + type ProtocolsHandler = + SubstreamProtocolsHandler; + type OutEvent = Event; + + fn new_handler(&mut self) -> Self::ProtocolsHandler { + let initial_keep_alive = Duration::from_secs(30); + + SubstreamProtocolsHandler::new_outbound_only(initial_keep_alive) + } + + fn addresses_of_peer(&mut self, peer: &PeerId) -> Vec { + self.discovered_peers + .iter() + .filter_map(|((candidate, _), addresses)| (candidate == peer).then(|| addresses)) + .flatten() + .cloned() + .collect() + } + + fn inject_connected(&mut self, _: &PeerId) {} + + fn inject_disconnected(&mut self, _: &PeerId) {} + + fn inject_event(&mut self, peer_id: PeerId, _: ConnectionId, event: handler::OutboundOutEvent) { + 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) + } + handler::OutboundOutEvent::InboundError { .. } => { + // TODO: log errors and close connection? + vec![] + } + handler::OutboundOutEvent::OutboundError { .. } => { + // TODO: log errors and close connection? + vec![] + } + }; + + self.events.extend(new_events); + } + + fn poll( + &mut self, + _: &mut Context<'_>, + poll_params: &mut impl PollParameters, + ) -> Poll< + NetworkBehaviourAction< + ::InEvent, + Self::OutEvent, + >, + > { + if let Some(event) = self.events.pop_front() { + return Poll::Ready(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 external_addresses = poll_params + .external_addresses() + .map(|r| r.addr) + .collect::>(); + + if external_addresses.is_empty() { + return Poll::Ready(NetworkBehaviourAction::GenerateEvent( + Event::RegisterFailed(RegisterError::NoExternalAddresses), + )); + } + + let action = match PeerRecord::new(self.keypair.clone(), external_addresses) { + Ok(peer_record) => NetworkBehaviourAction::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) => NetworkBehaviourAction::GenerateEvent(Event::RegisterFailed( + RegisterError::FailedToMakeRecord(signing_error), + )), + }; + + return Poll::Ready(action); + } + + Poll::Pending + } +} + +fn handle_outbound_event( + event: outbound::OutEvent, + peer_id: PeerId, + discovered_peers: &mut HashMap<(PeerId, Namespace), Vec>, +) -> Vec> { + match event { + outbound::OutEvent::Registered { namespace, ttl } => { + vec![NetworkBehaviourAction::GenerateEvent(Event::Registered { + rendezvous_node: peer_id, + ttl, + namespace, + })] + } + outbound::OutEvent::RegisterFailed(namespace, error) => { + vec![NetworkBehaviourAction::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) + })); + + vec![NetworkBehaviourAction::GenerateEvent(Event::Discovered { + rendezvous_node: peer_id, + registrations, + cookie, + })] + } + outbound::OutEvent::DiscoverFailed { namespace, error } => { + vec![NetworkBehaviourAction::GenerateEvent( + Event::DiscoverFailed { + rendezvous_node: peer_id, + namespace, + error, + }, + )] + } + } +} diff --git a/protocols/rendezvous/src/handler.rs b/protocols/rendezvous/src/handler.rs index 55399424b29..b4883825e25 100644 --- a/protocols/rendezvous/src/handler.rs +++ b/protocols/rendezvous/src/handler.rs @@ -22,6 +22,8 @@ use crate::codec; use crate::codec::Message; use void::Void; +const PROTOCOL_IDENT: &[u8] = b"/rendezvous/1.0.0"; + pub mod inbound; pub mod outbound; @@ -38,6 +40,9 @@ pub enum Error { UnexpectedEndOfStream, } -pub type InEvent = crate::substream_handler::InEvent; -pub type OutEvent = - crate::substream_handler::OutEvent; +pub type OutboundInEvent = crate::substream_handler::InEvent; +pub type OutboundOutEvent = + crate::substream_handler::OutEvent; + +pub type InboundInEvent = crate::substream_handler::InEvent<(), inbound::InEvent, Void>; +pub type InboundOutEvent = crate::substream_handler::OutEvent; diff --git a/protocols/rendezvous/src/handler/inbound.rs b/protocols/rendezvous/src/handler/inbound.rs index 59952bf2526..8a18f366c68 100644 --- a/protocols/rendezvous/src/handler/inbound.rs +++ b/protocols/rendezvous/src/handler/inbound.rs @@ -22,10 +22,11 @@ use crate::codec::{ Cookie, ErrorCode, Message, Namespace, NewRegistration, Registration, RendezvousCodec, Ttl, }; use crate::handler::Error; -use crate::substream_handler::{Next, SubstreamHandler}; +use crate::handler::PROTOCOL_IDENT; +use crate::substream_handler::{Next, PassthroughProtocol, SubstreamHandler}; use asynchronous_codec::Framed; use futures::{SinkExt, StreamExt}; -use libp2p_swarm::NegotiatedSubstream; +use libp2p_swarm::{NegotiatedSubstream, SubstreamProtocol}; use std::fmt; use std::task::{Context, Poll}; @@ -83,6 +84,12 @@ impl SubstreamHandler for Stream { type Error = Error; type OpenInfo = (); + fn upgrade( + open_info: Self::OpenInfo, + ) -> SubstreamProtocol { + SubstreamProtocol::new(PassthroughProtocol::new(PROTOCOL_IDENT), open_info) + } + fn new(substream: NegotiatedSubstream, _: Self::OpenInfo) -> Self { Stream::PendingRead(Framed::new(substream, RendezvousCodec::default())) } diff --git a/protocols/rendezvous/src/handler/outbound.rs b/protocols/rendezvous/src/handler/outbound.rs index 09f3bca0975..ab06040ca19 100644 --- a/protocols/rendezvous/src/handler/outbound.rs +++ b/protocols/rendezvous/src/handler/outbound.rs @@ -20,11 +20,12 @@ use crate::codec::{Cookie, Message, NewRegistration, RendezvousCodec}; use crate::handler::Error; -use crate::substream_handler::{FutureSubstream, Next, SubstreamHandler}; +use crate::handler::PROTOCOL_IDENT; +use crate::substream_handler::{FutureSubstream, Next, PassthroughProtocol, SubstreamHandler}; use crate::{ErrorCode, Namespace, Registration, Ttl}; use asynchronous_codec::Framed; use futures::{SinkExt, TryFutureExt, TryStreamExt}; -use libp2p_swarm::NegotiatedSubstream; +use libp2p_swarm::{NegotiatedSubstream, SubstreamProtocol}; use std::task::Context; use void::Void; @@ -36,6 +37,12 @@ impl SubstreamHandler for Stream { type Error = Error; type OpenInfo = OpenInfo; + fn upgrade( + open_info: Self::OpenInfo, + ) -> SubstreamProtocol { + SubstreamProtocol::new(PassthroughProtocol::new(PROTOCOL_IDENT), open_info) + } + fn new(substream: NegotiatedSubstream, info: Self::OpenInfo) -> Self { let mut stream = Framed::new(substream, RendezvousCodec::default()); let sent_message = match info { diff --git a/protocols/rendezvous/src/lib.rs b/protocols/rendezvous/src/lib.rs index e86a620e859..87c88434db3 100644 --- a/protocols/rendezvous/src/lib.rs +++ b/protocols/rendezvous/src/lib.rs @@ -18,10 +18,8 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -pub use self::behaviour::{Behaviour, Config, Event, RegisterError}; pub use self::codec::{ErrorCode, Namespace, NamespaceTooLong, Registration, Ttl}; -mod behaviour; mod codec; mod handler; mod substream_handler; @@ -40,3 +38,6 @@ pub const MIN_TTL: Ttl = 60 * 60 * 2; /// /// . pub const MAX_TTL: Ttl = 60 * 60 * 72; + +pub mod client; +pub mod server; diff --git a/protocols/rendezvous/src/behaviour.rs b/protocols/rendezvous/src/server.rs similarity index 74% rename from protocols/rendezvous/src/behaviour.rs rename to protocols/rendezvous/src/server.rs index 3ec45e164f6..17d6f22c01c 100644 --- a/protocols/rendezvous/src/behaviour.rs +++ b/protocols/rendezvous/src/server.rs @@ -19,8 +19,7 @@ // DEALINGS IN THE SOFTWARE. use crate::codec::{Cookie, ErrorCode, Namespace, NewRegistration, Registration, Ttl}; -use crate::handler::outbound::OpenInfo; -use crate::handler::{inbound, outbound}; +use crate::handler::inbound; use crate::substream_handler::{InboundSubstreamId, SubstreamProtocolsHandler}; use crate::{handler, MAX_TTL, MIN_TTL}; use bimap::BiMap; @@ -29,9 +28,7 @@ use futures::ready; use futures::stream::FuturesUnordered; use futures::{FutureExt, StreamExt}; use libp2p_core::connection::ConnectionId; -use libp2p_core::identity::error::SigningError; -use libp2p_core::identity::Keypair; -use libp2p_core::{Multiaddr, PeerId, PeerRecord}; +use libp2p_core::PeerId; use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; @@ -39,17 +36,11 @@ use std::collections::{HashMap, HashSet, VecDeque}; use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::Duration; +use void::Void; pub struct Behaviour { - events: VecDeque>, + events: VecDeque>, registrations: Registrations, - keypair: Keypair, - pending_register_requests: Vec<(Namespace, PeerId, Option)>, - - /// Hold addresses of all peers that we have discovered so far. - /// - /// Storing these internally allows us to assist the [`libp2p_swarm::Swarm`] in dialing by returning addresses from [`NetworkBehaviour::addresses_of_peer`]. - discovered_peers: HashMap<(PeerId, Namespace), Vec>, } pub struct Config { @@ -80,103 +71,17 @@ impl Default for Config { impl Behaviour { /// Create a new instance of the rendezvous [`NetworkBehaviour`]. - pub fn new(keypair: Keypair, config: Config) -> Self { + pub fn new(config: Config) -> Self { Self { events: Default::default(), registrations: Registrations::with_config(config), - keypair, - pending_register_requests: vec![], - discovered_peers: Default::default(), } } - - /// Register our external addresses in the given namespace with the given rendezvous peer. - /// - /// External addresses are either manually added via [`libp2p_swarm::Swarm::add_external_address`] or reported - /// by other [`NetworkBehaviour`]s via [`NetworkBehaviourAction::ReportObservedAddr`]. - pub fn register(&mut self, namespace: Namespace, rendezvous_node: PeerId, ttl: Option) { - self.pending_register_requests - .push((namespace, rendezvous_node, ttl)); - } - - /// 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(NetworkBehaviourAction::NotifyHandler { - peer_id: rendezvous_node, - event: handler::InEvent::NewSubstream { - open_info: OpenInfo::UnregisterRequest(namespace), - }, - handler: NotifyHandler::Any, - }); - } - - /// Discover other peers at a given rendezvous peer. - /// - /// If desired, the registrations can be filtered by a namespace. - /// If no namespace is given, peers from all namespaces will be returned. - /// A successfully discovery returns a cookie within [`Event::Discovered`]. - /// Such a cookie can be used to only fetch the _delta_ of registrations since - /// the cookie was acquired. - pub fn discover( - &mut self, - ns: Option, - cookie: Option, - limit: Option, - rendezvous_node: PeerId, - ) { - self.events - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: rendezvous_node, - event: handler::InEvent::NewSubstream { - open_info: OpenInfo::DiscoverRequest { - namespace: ns, - cookie, - 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), /// We successfully served a discover request from a peer. DiscoverServed { enquirer: PeerId, @@ -202,47 +107,31 @@ pub enum Event { } impl NetworkBehaviour for Behaviour { - type ProtocolsHandler = - SubstreamProtocolsHandler; + type ProtocolsHandler = SubstreamProtocolsHandler; type OutEvent = Event; fn new_handler(&mut self) -> Self::ProtocolsHandler { let initial_keep_alive = Duration::from_secs(30); - SubstreamProtocolsHandler::new(b"/rendezvous/1.0.0", initial_keep_alive) - } - - fn addresses_of_peer(&mut self, peer: &PeerId) -> Vec { - self.discovered_peers - .iter() - .filter_map(|((candidate, _), addresses)| (candidate == peer).then(|| addresses)) - .flatten() - .cloned() - .collect() + SubstreamProtocolsHandler::new_inbound_only(initial_keep_alive) } - fn inject_connected(&mut self, _: &PeerId) {} - - fn inject_disconnected(&mut self, _: &PeerId) {} - fn inject_event( &mut self, peer_id: PeerId, connection: ConnectionId, - event: handler::OutEvent, + event: handler::InboundOutEvent, ) { let new_events = match event { - handler::OutEvent::InboundEvent { id, message } => { + handler::InboundOutEvent::InboundEvent { id, message } => { handle_inbound_event(message, peer_id, connection, id, &mut self.registrations) } - handler::OutEvent::OutboundEvent { message, .. } => { - handle_outbound_event(message, peer_id, &mut self.discovered_peers) - } - handler::OutEvent::InboundError { .. } => { + handler::InboundOutEvent::OutboundEvent { message, .. } => void::unreachable(message), + handler::InboundOutEvent::InboundError { .. } => { // TODO: log errors and close connection? vec![] } - handler::OutEvent::OutboundError { .. } => { + handler::InboundOutEvent::OutboundError { .. } => { // TODO: log errors and close connection? vec![] } @@ -254,7 +143,7 @@ impl NetworkBehaviour for Behaviour { fn poll( &mut self, cx: &mut Context<'_>, - poll_params: &mut impl PollParameters, + _: &mut impl PollParameters, ) -> Poll< NetworkBehaviourAction< ::InEvent, @@ -271,40 +160,6 @@ impl NetworkBehaviour for Behaviour { return Poll::Ready(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 external_addresses = poll_params - .external_addresses() - .map(|r| r.addr) - .collect::>(); - - if external_addresses.is_empty() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent( - Event::RegisterFailed(RegisterError::NoExternalAddresses), - )); - } - - let action = match PeerRecord::new(self.keypair.clone(), external_addresses) { - Ok(peer_record) => NetworkBehaviourAction::NotifyHandler { - peer_id: rendezvous_node, - event: handler::InEvent::NewSubstream { - open_info: OpenInfo::RegisterRequest(NewRegistration { - namespace, - record: peer_record, - ttl, - }), - }, - handler: NotifyHandler::Any, - }, - Err(signing_error) => NetworkBehaviourAction::GenerateEvent(Event::RegisterFailed( - RegisterError::FailedToMakeRecord(signing_error), - )), - }; - - return Poll::Ready(action); - } - Poll::Pending } } @@ -315,7 +170,7 @@ fn handle_inbound_event( connection: ConnectionId, id: InboundSubstreamId, registrations: &mut Registrations, -) -> Vec> { +) -> Vec> { match event { // bad registration inbound::OutEvent::RegistrationRequested(registration) @@ -327,7 +182,7 @@ fn handle_inbound_event( NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: handler::InEvent::NotifyInboundSubstream { + event: handler::InboundInEvent::NotifyInboundSubstream { id, message: inbound::InEvent::DeclineRegisterRequest(error), }, @@ -348,7 +203,7 @@ fn handle_inbound_event( NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: handler::InEvent::NotifyInboundSubstream { + event: handler::InboundInEvent::NotifyInboundSubstream { id, message: inbound::InEvent::RegisterResponse { ttl: registration.ttl, @@ -368,7 +223,7 @@ fn handle_inbound_event( NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: handler::InEvent::NotifyInboundSubstream { + event: handler::InboundInEvent::NotifyInboundSubstream { id, message: inbound::InEvent::DeclineRegisterRequest(error), }, @@ -394,7 +249,7 @@ fn handle_inbound_event( NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: handler::InEvent::NotifyInboundSubstream { + event: handler::InboundInEvent::NotifyInboundSubstream { id, message: inbound::InEvent::DiscoverResponse { discovered: discovered.clone(), @@ -415,7 +270,7 @@ fn handle_inbound_event( NetworkBehaviourAction::NotifyHandler { peer_id, handler: NotifyHandler::One(connection), - event: handler::InEvent::NotifyInboundSubstream { + event: handler::InboundInEvent::NotifyInboundSubstream { id, message: inbound::InEvent::DeclineDiscoverRequest(error), }, @@ -440,59 +295,6 @@ fn handle_inbound_event( } } -fn handle_outbound_event( - event: outbound::OutEvent, - peer_id: PeerId, - discovered_peers: &mut HashMap<(PeerId, Namespace), Vec>, -) -> Vec> { - match event { - outbound::OutEvent::Registered { namespace, ttl } => { - vec![NetworkBehaviourAction::GenerateEvent(Event::Registered { - rendezvous_node: peer_id, - ttl, - namespace, - })] - } - outbound::OutEvent::RegisterFailed(namespace, error) => { - vec![NetworkBehaviourAction::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) - })); - - vec![NetworkBehaviourAction::GenerateEvent(Event::Discovered { - rendezvous_node: peer_id, - registrations, - cookie, - })] - } - outbound::OutEvent::DiscoverFailed { namespace, error } => { - vec![NetworkBehaviourAction::GenerateEvent( - Event::DiscoverFailed { - rendezvous_node: peer_id, - namespace, - error, - }, - )] - } - } -} - #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] struct RegistrationId(u64); @@ -699,7 +501,7 @@ mod tests { use std::option::Option::None; use std::time::SystemTime; - use libp2p_core::identity; + use libp2p_core::{identity, PeerRecord}; use super::*; diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs index f3a683f15f8..efd7956b13c 100644 --- a/protocols/rendezvous/src/substream_handler.rs +++ b/protocols/rendezvous/src/substream_handler.rs @@ -25,10 +25,9 @@ //! //! 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::{BoxFuture, Fuse, FusedFuture}; +use futures::future::{self, BoxFuture, Fuse, FusedFuture}; use futures::FutureExt; -use libp2p_core::upgrade::FromFnUpgrade; -use libp2p_core::Endpoint; +use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; use libp2p_swarm::protocols_handler::{InboundUpgradeSend, OutboundUpgradeSend}; use libp2p_swarm::{ KeepAlive, NegotiatedSubstream, ProtocolsHandler, ProtocolsHandlerEvent, @@ -36,7 +35,7 @@ use libp2p_swarm::{ }; use std::collections::{HashMap, VecDeque}; use std::fmt; -use std::future::{Future, Ready}; +use std::future::Future; use std::hash::Hash; use std::task::{Context, Poll}; use std::time::{Duration, Instant}; @@ -49,6 +48,8 @@ pub trait SubstreamHandler: Sized { type Error; type OpenInfo; + fn upgrade(open_info: Self::OpenInfo) + -> SubstreamProtocol; fn new(substream: NegotiatedSubstream, info: Self::OpenInfo) -> Self; fn inject_event(self, event: Self::InEvent) -> Self; fn advance(self, cx: &mut Context<'_>) -> Result, Self::Error>; @@ -125,15 +126,55 @@ impl fmt::Display for OutboundSubstreamId { } } -type ProtocolUpgradeFn = - Box Ready> + Send>; -type Protocol = FromFnUpgrade<&'static [u8], ProtocolUpgradeFn>; +pub struct PassthroughProtocol { + ident: Option<&'static [u8]>, +} + +impl PassthroughProtocol { + pub fn new(ident: &'static [u8]) -> Self { + Self { ident: Some(ident) } + } +} + +impl UpgradeInfo for PassthroughProtocol { + type Info = &'static [u8]; + type InfoIter = std::option::IntoIter; + + fn protocol_info(&self) -> Self::InfoIter { + self.ident.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 [`ProtocolsHandler`] that delegates to individual [`SubstreamHandler`]s. pub struct SubstreamProtocolsHandler { inbound_substreams: HashMap, outbound_substreams: HashMap, - protocol: &'static [u8], next_inbound_substream_id: InboundSubstreamId, next_outbound_substream_id: OutboundSubstreamId, @@ -145,11 +186,40 @@ pub struct SubstreamProtocolsHandler SubstreamProtocolsHandler { - pub fn new(protocol: &'static [u8], initial_keep_alive: Duration) -> Self { + 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 + SubstreamProtocolsHandler +{ + 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 + SubstreamProtocolsHandler +{ + pub fn new_inbound_only(initial_keep_alive: Duration) -> Self { Self { inbound_substreams: Default::default(), outbound_substreams: Default::default(), - protocol, next_inbound_substream_id: InboundSubstreamId(0), next_outbound_substream_id: OutboundSubstreamId(0), new_substreams: Default::default(), @@ -286,19 +356,13 @@ where type InEvent = InEvent; type OutEvent = OutEvent; type Error = Void; - type InboundProtocol = Protocol; - type OutboundProtocol = Protocol; + type InboundProtocol = PassthroughProtocol; + type OutboundProtocol = PassthroughProtocol; type InboundOpenInfo = (); type OutboundOpenInfo = TOutboundOpenInfo; fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new( - libp2p_core::upgrade::from_fn( - self.protocol, - Box::new(|socket, _| std::future::ready(Ok(socket))), - ), - (), - ) + TInboundSubstreamHandler::upgrade(()) } fn inject_fully_negotiated_inbound( @@ -391,13 +455,7 @@ where > { if let Some(open_info) = self.new_substreams.pop_front() { return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new( - libp2p_core::upgrade::from_fn( - self.protocol, - Box::new(|socket, _| std::future::ready(Ok(socket))), - ), - open_info, - ), + protocol: TOutboundSubstreamHandler::upgrade(open_info), }); } @@ -466,3 +524,28 @@ impl FutureSubstream { } } } + +impl SubstreamHandler for void::Void { + type InEvent = void::Void; + type OutEvent = void::Void; + type Error = void::Void; + type OpenInfo = (); + + fn new(_: NegotiatedSubstream, _: Self::OpenInfo) -> Self { + unreachable!("we should never yield a substream") + } + + fn inject_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) + } +} diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 43ac231c806..8d359bb1b5c 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -123,8 +123,8 @@ async fn given_invalid_ttl_then_unsuccessful_registration() { match await_events_or_timeout(&mut test.robert, &mut test.alice).await { ( - SwarmEvent::Behaviour(rendezvous::Event::PeerNotRegistered { .. }), - SwarmEvent::Behaviour(rendezvous::Event::RegisterFailed(rendezvous::RegisterError::Remote {error , ..})), + SwarmEvent::Behaviour(rendezvous::server::Event::PeerNotRegistered { .. }), + SwarmEvent::Behaviour(rendezvous::client::Event::RegisterFailed(rendezvous::client::RegisterError::Remote {error , ..})), ) => { assert_eq!(error, rendezvous::ErrorCode::InvalidTtl); } @@ -142,6 +142,7 @@ async fn discover_allows_for_dial_by_peer_id() { mut alice, mut bob, mut robert, + charlie: _charlie, .. } = RendezvousTest::setup().await; let roberts_peer_id = *robert.local_peer_id(); @@ -163,8 +164,8 @@ async fn discover_allows_for_dial_by_peer_id() { match await_events_or_timeout(&mut alice, &mut bob).await { ( - SwarmEvent::Behaviour(rendezvous::Event::Registered { .. }), - SwarmEvent::Behaviour(rendezvous::Event::Discovered { .. }), + SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }), + SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { .. }), ) => {} _ => panic!("bad event combination emitted"), }; @@ -211,8 +212,8 @@ async fn eve_cannot_register() { match await_events_or_timeout(&mut test.robert, &mut test.eve).await { ( - SwarmEvent::Behaviour(rendezvous::Event::PeerNotRegistered { .. }), - SwarmEvent::Behaviour(rendezvous::Event::RegisterFailed(rendezvous::RegisterError::Remote { error: err_code , ..})), + SwarmEvent::Behaviour(rendezvous::server::Event::PeerNotRegistered { .. }), + SwarmEvent::Behaviour(rendezvous::client::Event::RegisterFailed(rendezvous::client::RegisterError::Remote { error: err_code , ..})), ) => { assert_eq!(err_code, rendezvous::ErrorCode::NotAuthorized); } @@ -223,31 +224,71 @@ async fn eve_cannot_register() { } } +// test if charlie can operate as client and server simultaneously +#[tokio::test] +async fn can_combine_client_and_server() { + let _ = env_logger::try_init(); + let mut test = RendezvousTest::setup().await; + + let namespace = rendezvous::Namespace::from_static("some-namespace"); + + test.charlie.behaviour_mut().client.register( + namespace.clone(), + *test.robert.local_peer_id(), + None, + ); + + match await_events_or_timeout(&mut test.robert, &mut test.charlie).await { + ( + SwarmEvent::Behaviour(rendezvous::server::Event::PeerRegistered { .. }), + SwarmEvent::Behaviour(CombinedEvent::Client(rendezvous::client::Event::Registered { .. })), + ) => { + } + (rendezvous_swarm_event, registration_swarm_event) => panic!( + "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", + rendezvous_swarm_event, registration_swarm_event + ), + } + + test.alice + .behaviour_mut() + .register(namespace, *test.charlie.local_peer_id(), None); + + match await_events_or_timeout(&mut test.alice, &mut test.charlie).await { + ( + SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }), + SwarmEvent::Behaviour(CombinedEvent::Server(rendezvous::server::Event::PeerRegistered { .. })), + ) => { + } + (rendezvous_swarm_event, registration_swarm_event) => panic!( + "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", + rendezvous_swarm_event, registration_swarm_event + ), + } +} + /// Holds a network of nodes that is used to test certain rendezvous functionality. /// /// In all cases, Alice would like to connect to Bob with Robert acting as a rendezvous point. /// Eve is an evil actor that tries to act maliciously. struct RendezvousTest { - pub alice: Swarm, - pub bob: Swarm, - pub eve: Swarm, - pub robert: Swarm, + pub alice: Swarm, + pub bob: Swarm, + pub charlie: Swarm, + pub eve: Swarm, + pub robert: Swarm, } impl RendezvousTest { pub async fn setup() -> Self { - let mut alice = new_swarm(|_, identity| { - rendezvous::Behaviour::new(identity, rendezvous::Config::default()) - }); + let mut alice = new_swarm(|_, identity| rendezvous::client::Behaviour::new(identity)); alice.listen_on_random_memory_address().await; - let mut bob = new_swarm(|_, identity| { - rendezvous::Behaviour::new(identity, rendezvous::Config::default()) - }); + let mut bob = new_swarm(|_, identity| rendezvous::client::Behaviour::new(identity)); bob.listen_on_random_memory_address().await; - let mut robert = new_swarm(|_, identity| { - rendezvous::Behaviour::new(identity, rendezvous::Config::default()) + let mut robert = new_swarm(|_, _| { + rendezvous::server::Behaviour::new(rendezvous::server::Config::default()) }); robert.listen_on_random_memory_address().await; @@ -256,21 +297,28 @@ impl RendezvousTest { // Due to the type-safe API of the `Rendezvous` behaviour and `PeerRecord`, we actually cannot construct a bad `PeerRecord` (i.e. one that is claims to be someone else). // As such, the best we can do is hand eve a completely different keypair from what she is using to authenticate her connection. let someone_else = identity::Keypair::generate_ed25519(); - let mut eve = new_swarm(move |_, _| { - rendezvous::Behaviour::new(someone_else, rendezvous::Config::default()) - }); + let mut eve = new_swarm(move |_, _| rendezvous::client::Behaviour::new(someone_else)); eve.listen_on_random_memory_address().await; eve }; + let mut charlie = new_swarm(|_, identity| CombinedBehaviour { + client: rendezvous::client::Behaviour::new(identity), + server: rendezvous::server::Behaviour::new(rendezvous::server::Config::default()), + }); + charlie.listen_on_random_memory_address().await; + alice.block_on_connection(&mut robert).await; bob.block_on_connection(&mut robert).await; + charlie.block_on_connection(&mut robert).await; eve.block_on_connection(&mut robert).await; + alice.block_on_connection(&mut charlie).await; Self { alice, bob, + charlie, eve, robert, } @@ -283,8 +331,8 @@ impl RendezvousTest { ) { match await_events_or_timeout(&mut self.robert, &mut self.alice).await { ( - SwarmEvent::Behaviour(rendezvous::Event::PeerRegistered { peer, registration }), - SwarmEvent::Behaviour(rendezvous::Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), + SwarmEvent::Behaviour(rendezvous::server::Event::PeerRegistered { peer, registration }), + SwarmEvent::Behaviour(rendezvous::client::Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), ) => { assert_eq!(&peer, self.alice.local_peer_id()); assert_eq!(&rendezvous_node, self.robert.local_peer_id()); @@ -307,8 +355,10 @@ impl RendezvousTest { ) { match await_events_or_timeout(&mut self.robert, &mut self.bob).await { ( - SwarmEvent::Behaviour(rendezvous::Event::DiscoverServed { .. }), - SwarmEvent::Behaviour(rendezvous::Event::Discovered { registrations, .. }), + SwarmEvent::Behaviour(rendezvous::server::Event::DiscoverServed { .. }), + SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { + registrations, .. + }), ) => match registrations.as_slice() { [rendezvous::Registration { namespace, @@ -325,3 +375,28 @@ impl RendezvousTest { } } } + +#[derive(libp2p::NetworkBehaviour)] +#[behaviour(event_process = false, out_event = "CombinedEvent")] +struct CombinedBehaviour { + client: rendezvous::client::Behaviour, + server: rendezvous::server::Behaviour, +} + +#[derive(Debug)] +enum CombinedEvent { + Client(rendezvous::client::Event), + Server(rendezvous::server::Event), +} + +impl From for CombinedEvent { + fn from(v: rendezvous::server::Event) -> Self { + Self::Server(v) + } +} + +impl From for CombinedEvent { + fn from(v: rendezvous::client::Event) -> Self { + Self::Client(v) + } +} From 8115ec3a591576e7095fe0d8556fea64d0377f34 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 1 Sep 2021 15:03:31 +1000 Subject: [PATCH 233/242] Allow registrations on client to expire This avoids a memory leak of addresses that are no longer relevant. --- protocols/rendezvous/src/client.rs | 44 +++++++++++++++++-- protocols/rendezvous/tests/rendezvous.rs | 54 +++++++++++++++++++++++- 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index e51f6542907..64d535a82d9 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -23,6 +23,10 @@ use crate::handler; use crate::handler::outbound; use crate::handler::outbound::OpenInfo; use crate::substream_handler::SubstreamProtocolsHandler; +use futures::future::BoxFuture; +use futures::future::FutureExt; +use futures::stream::FuturesUnordered; +use futures::stream::StreamExt; use libp2p_core::connection::ConnectionId; use libp2p_core::identity::error::SigningError; use libp2p_core::identity::Keypair; @@ -31,6 +35,7 @@ use libp2p_swarm::{ NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, }; use std::collections::{HashMap, VecDeque}; +use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::Duration; @@ -43,6 +48,9 @@ pub struct Behaviour { /// /// Storing these internally allows us to assist the [`libp2p_swarm::Swarm`] in dialing by returning addresses from [`NetworkBehaviour::addresses_of_peer`]. discovered_peers: HashMap<(PeerId, Namespace), Vec>, + + /// Tracks the expiry of registrations that we have discovered and stored in `discovered_peers` otherwise we have a memory leak. + expiring_registrations: FuturesUnordered>, } impl Behaviour { @@ -53,6 +61,9 @@ impl Behaviour { keypair, pending_register_requests: vec![], discovered_peers: Default::default(), + expiring_registrations: FuturesUnordered::from_iter(vec![ + futures::future::pending().boxed() + ]), } } @@ -143,6 +154,8 @@ pub enum Event { }, /// 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 { @@ -172,9 +185,12 @@ impl NetworkBehaviour for Behaviour { fn inject_event(&mut self, peer_id: PeerId, _: ConnectionId, event: handler::OutboundOutEvent) { 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) - } + handler::OutboundOutEvent::OutboundEvent { message, .. } => handle_outbound_event( + message, + peer_id, + &mut self.discovered_peers, + &mut self.expiring_registrations, + ), handler::OutboundOutEvent::InboundError { .. } => { // TODO: log errors and close connection? vec![] @@ -190,7 +206,7 @@ impl NetworkBehaviour for Behaviour { fn poll( &mut self, - _: &mut Context<'_>, + cx: &mut Context<'_>, poll_params: &mut impl PollParameters, ) -> Poll< NetworkBehaviourAction< @@ -236,6 +252,15 @@ impl NetworkBehaviour for Behaviour { 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(NetworkBehaviourAction::GenerateEvent(Event::Expired { + peer: expired_registration.0, + })); + } + Poll::Pending } } @@ -244,6 +269,7 @@ 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 } => { @@ -274,6 +300,16 @@ fn handle_outbound_event( ((peer_id, namespace), addresses) })); + expiring_registrations.extend(registrations.iter().cloned().map(|registration| { + async move { + // if the timer errors we consider it expired + let _ = + wasm_timer::Delay::new(Duration::from_secs(registration.ttl as u64)).await; + + (registration.record.peer_id(), registration.namespace) + } + .boxed() + })); vec![NetworkBehaviourAction::GenerateEvent(Event::Discovered { rendezvous_node: peer_id, diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 8d359bb1b5c..57a59b844cc 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -25,7 +25,9 @@ use futures::StreamExt; use libp2p_core::identity; use libp2p_core::PeerId; use libp2p_rendezvous as rendezvous; +use libp2p_swarm::DialError; use libp2p_swarm::{Swarm, SwarmEvent}; +use std::time::Duration; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { @@ -267,6 +269,54 @@ async fn can_combine_client_and_server() { } } +#[tokio::test] +async fn registration_on_clients_expire() { + let _ = env_logger::try_init(); + let RendezvousTest { + mut alice, + mut bob, + mut robert, + charlie: _charlie, + .. + } = RendezvousTest::setup().await; + let roberts_peer_id = *robert.local_peer_id(); + + // poll rendezvous point continuously + tokio::spawn(async move { + loop { + robert.next().await; + } + }); + + let namespace = rendezvous::Namespace::from_static("some-namespace"); + let registration_ttl = 3; + + alice + .behaviour_mut() + .register(namespace.clone(), roberts_peer_id, Some(registration_ttl)); + bob.behaviour_mut() + .discover(Some(namespace), None, None, roberts_peer_id); + + match await_events_or_timeout(&mut alice, &mut bob).await { + ( + SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }), + SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { .. }), + ) => {} + _ => panic!("bad event combination emitted"), + }; + + tokio::time::sleep(Duration::from_secs(registration_ttl + 5)).await; + + let event = bob.select_next_some().await; + let error = bob.dial(alice.local_peer_id()).unwrap_err(); + + assert!(matches!( + event, + SwarmEvent::Behaviour(rendezvous::client::Event::Expired { .. }) + )); + assert!(matches!(error, DialError::NoAddresses)); +} + /// Holds a network of nodes that is used to test certain rendezvous functionality. /// /// In all cases, Alice would like to connect to Bob with Robert acting as a rendezvous point. @@ -288,7 +338,9 @@ impl RendezvousTest { bob.listen_on_random_memory_address().await; let mut robert = new_swarm(|_, _| { - rendezvous::server::Behaviour::new(rendezvous::server::Config::default()) + rendezvous::server::Behaviour::new( + rendezvous::server::Config::default().with_min_ttl(2), + ) }); robert.listen_on_random_memory_address().await; From 7d6bf5929039664f0153177fb800b072ff3e4394 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 1 Sep 2021 17:14:46 +1000 Subject: [PATCH 234/242] Migrate rendezvous protocol to new NetworkBehaviourAction --- protocols/rendezvous/src/client.rs | 25 ++++++++++++++----------- protocols/rendezvous/src/server.rs | 17 ++++++----------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 64d535a82d9..7a6e5abb96b 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -31,16 +31,19 @@ use libp2p_core::connection::ConnectionId; use libp2p_core::identity::error::SigningError; use libp2p_core::identity::Keypair; use libp2p_core::{Multiaddr, PeerId, PeerRecord}; -use libp2p_swarm::{ - NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, -}; +use libp2p_swarm::{NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters}; use std::collections::{HashMap, VecDeque}; use std::iter::FromIterator; use std::task::{Context, Poll}; use std::time::Duration; pub struct Behaviour { - events: VecDeque>, + events: VecDeque< + NetworkBehaviourAction< + Event, + SubstreamProtocolsHandler, + >, + >, keypair: Keypair, pending_register_requests: Vec<(Namespace, PeerId, Option)>, @@ -208,12 +211,7 @@ impl NetworkBehaviour for Behaviour { &mut self, cx: &mut Context<'_>, poll_params: &mut impl PollParameters, - ) -> Poll< - NetworkBehaviourAction< - ::InEvent, - Self::OutEvent, - >, - > { + ) -> Poll> { if let Some(event) = self.events.pop_front() { return Poll::Ready(event); } @@ -270,7 +268,12 @@ fn handle_outbound_event( peer_id: PeerId, discovered_peers: &mut HashMap<(PeerId, Namespace), Vec>, expiring_registrations: &mut FuturesUnordered>, -) -> Vec> { +) -> Vec< + NetworkBehaviourAction< + Event, + SubstreamProtocolsHandler, + >, +> { match event { outbound::OutEvent::Registered { namespace, ttl } => { vec![NetworkBehaviourAction::GenerateEvent(Event::Registered { diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 17d6f22c01c..1c24ae508ea 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -29,9 +29,7 @@ use futures::stream::FuturesUnordered; use futures::{FutureExt, StreamExt}; use libp2p_core::connection::ConnectionId; use libp2p_core::PeerId; -use libp2p_swarm::{ - NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, ProtocolsHandler, -}; +use libp2p_swarm::{NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters}; use std::collections::{HashMap, HashSet, VecDeque}; use std::iter::FromIterator; use std::task::{Context, Poll}; @@ -39,7 +37,9 @@ use std::time::Duration; use void::Void; pub struct Behaviour { - events: VecDeque>, + events: VecDeque< + NetworkBehaviourAction>, + >, registrations: Registrations, } @@ -144,12 +144,7 @@ impl NetworkBehaviour for Behaviour { &mut self, cx: &mut Context<'_>, _: &mut impl PollParameters, - ) -> Poll< - NetworkBehaviourAction< - ::InEvent, - Self::OutEvent, - >, - > { + ) -> Poll> { if let Poll::Ready(ExpiredRegistration(registration)) = self.registrations.poll(cx) { return Poll::Ready(NetworkBehaviourAction::GenerateEvent( Event::RegistrationExpired(registration), @@ -170,7 +165,7 @@ fn handle_inbound_event( connection: ConnectionId, id: InboundSubstreamId, registrations: &mut Registrations, -) -> Vec> { +) -> Vec>> { match event { // bad registration inbound::OutEvent::RegistrationRequested(registration) From e3ff6c20f4f7ad9cfcd7a1e83b5b800f34024e4a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 1 Sep 2021 17:35:17 +1000 Subject: [PATCH 235/242] Make it work without uuid This makes compilation on wasm simpler. --- Cargo.toml | 2 +- protocols/rendezvous/Cargo.toml | 4 ---- protocols/rendezvous/src/codec.rs | 28 +++++++++++++--------------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1dd6803d690..05b5ccc8860 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ rendezvous = ["libp2p-rendezvous"] tcp-async-io = ["libp2p-tcp", "libp2p-tcp/async-io"] tcp-tokio = ["libp2p-tcp", "libp2p-tcp/tokio"] uds = ["libp2p-uds"] -wasm-bindgen = ["parking_lot/wasm-bindgen", "libp2p-rendezvous/wasm-bindgen"] +wasm-bindgen = ["parking_lot/wasm-bindgen"] wasm-ext = ["libp2p-wasm-ext"] wasm-ext-websocket = ["wasm-ext", "libp2p-wasm-ext/websocket"] websocket = ["libp2p-websocket"] diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 4b91a62a392..0c3ad8e31c5 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -19,7 +19,6 @@ log = "0.4" futures = { version = "0.3", default-features = false, features = ["std"] } thiserror = "1" unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } -uuid = { version = "0.8", features = ["v4"] } bimap = "0.6.1" sha2 = "0.9" rand = "0.8" @@ -33,8 +32,5 @@ env_logger = "0.8" async-trait = "0.1" tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } # TODO: REMOVE FOR PRODUCTION USE -[features] -wasm-bindgen = ["uuid/wasm-bindgen"] - [build-dependencies] prost-build = "0.7" diff --git a/protocols/rendezvous/src/codec.rs b/protocols/rendezvous/src/codec.rs index 6375c11e3ca..d050ff8ca9a 100644 --- a/protocols/rendezvous/src/codec.rs +++ b/protocols/rendezvous/src/codec.rs @@ -21,10 +21,10 @@ use crate::DEFAULT_TTL; use asynchronous_codec::{Bytes, BytesMut, Decoder, Encoder}; use libp2p_core::{peer_record, signed_envelope, PeerRecord, SignedEnvelope}; +use rand::RngCore; use std::convert::{TryFrom, TryInto}; use std::fmt; use unsigned_varint::codec::UviBytes; -use uuid::Uuid; pub type Ttl = u64; @@ -95,7 +95,7 @@ pub struct NamespaceTooLong; #[derive(Debug, Eq, PartialEq, Hash, Clone)] pub struct Cookie { - id: Uuid, + id: u64, namespace: Option, } @@ -105,7 +105,7 @@ impl Cookie { /// This cookie will only be valid for subsequent DISCOVER requests targeting the same namespace. pub fn for_namespace(namespace: Namespace) -> Self { Self { - id: Uuid::new_v4(), + id: rand::thread_rng().next_u64(), namespace: Some(namespace), } } @@ -113,16 +113,17 @@ impl Cookie { /// Construct a new [`Cookie`] for a DISCOVER request that inquires about all namespaces. pub fn for_all_namespaces() -> Self { Self { - id: Uuid::new_v4(), + id: rand::random(), namespace: None, } } pub fn into_wire_encoding(self) -> Vec { + let id_bytes = self.id.to_be_bytes(); let namespace = self.namespace.map(|ns| ns.0).unwrap_or_default(); - let mut buffer = Vec::with_capacity(16 + namespace.len()); - buffer.extend_from_slice(self.id.as_bytes()); + let mut buffer = Vec::with_capacity(id_bytes.len() + namespace.len()); + buffer.extend_from_slice(&id_bytes); buffer.extend_from_slice(namespace.as_bytes()); buffer @@ -130,11 +131,11 @@ impl Cookie { pub fn from_wire_encoding(mut bytes: Vec) -> Result { // check length early to avoid panic during slicing - if bytes.len() < 16 { + if bytes.len() < 8 { return Err(InvalidCookie); } - let namespace = bytes.split_off(16); + let namespace = bytes.split_off(8); let namespace = if namespace.is_empty() { None } else { @@ -144,13 +145,10 @@ impl Cookie { ) }; - let bytes = <[u8; 16]>::try_from(bytes).map_err(|_| InvalidCookie)?; - let uuid = Uuid::from_bytes(bytes); + let bytes = <[u8; 8]>::try_from(bytes).map_err(|_| InvalidCookie)?; + let id = u64::from_be_bytes(bytes); - Ok(Self { - id: uuid, - namespace, - }) + Ok(Self { id, namespace }) } pub fn namespace(&self) -> Option<&Namespace> { @@ -619,6 +617,6 @@ mod tests { let bytes = cookie.into_wire_encoding(); - assert_eq!(bytes.len(), 16 + 3) + assert_eq!(bytes.len(), 8 + 3) } } From 8f46f8415025ae11d2f9d00d80581f71e69d4138 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 1 Sep 2021 17:43:02 +1000 Subject: [PATCH 236/242] Log errors and close connection as a result --- protocols/rendezvous/src/client.rs | 26 +++++++++++++++++--------- protocols/rendezvous/src/server.rs | 19 +++++++++++-------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 7a6e5abb96b..dde0da885e3 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -31,7 +31,9 @@ use libp2p_core::connection::ConnectionId; use libp2p_core::identity::error::SigningError; use libp2p_core::identity::Keypair; use libp2p_core::{Multiaddr, PeerId, PeerRecord}; -use libp2p_swarm::{NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters}; +use libp2p_swarm::{ + CloseConnection, NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, +}; use std::collections::{HashMap, VecDeque}; use std::iter::FromIterator; use std::task::{Context, Poll}; @@ -185,7 +187,12 @@ impl NetworkBehaviour for Behaviour { fn inject_disconnected(&mut self, _: &PeerId) {} - fn inject_event(&mut self, peer_id: PeerId, _: ConnectionId, event: handler::OutboundOutEvent) { + fn inject_event( + &mut self, + peer_id: PeerId, + connection_id: ConnectionId, + event: handler::OutboundOutEvent, + ) { let new_events = match event { handler::OutboundOutEvent::InboundEvent { message, .. } => void::unreachable(message), handler::OutboundOutEvent::OutboundEvent { message, .. } => handle_outbound_event( @@ -194,13 +201,14 @@ impl NetworkBehaviour for Behaviour { &mut self.discovered_peers, &mut self.expiring_registrations, ), - handler::OutboundOutEvent::InboundError { .. } => { - // TODO: log errors and close connection? - vec![] - } - handler::OutboundOutEvent::OutboundError { .. } => { - // TODO: log errors and close connection? - vec![] + handler::OutboundOutEvent::InboundError { error, .. } => void::unreachable(error), + handler::OutboundOutEvent::OutboundError { error, .. } => { + log::warn!("Connection with peer {} failed: {}", peer_id, error); + + vec![NetworkBehaviourAction::CloseConnection { + peer_id, + connection: CloseConnection::One(connection_id), + }] } }; diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 1c24ae508ea..93682dda422 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -29,7 +29,9 @@ use futures::stream::FuturesUnordered; use futures::{FutureExt, StreamExt}; use libp2p_core::connection::ConnectionId; use libp2p_core::PeerId; -use libp2p_swarm::{NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters}; +use libp2p_swarm::{ + CloseConnection, NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, PollParameters, +}; use std::collections::{HashMap, HashSet, VecDeque}; use std::iter::FromIterator; use std::task::{Context, Poll}; @@ -127,14 +129,15 @@ impl NetworkBehaviour for Behaviour { handle_inbound_event(message, peer_id, connection, id, &mut self.registrations) } handler::InboundOutEvent::OutboundEvent { message, .. } => void::unreachable(message), - handler::InboundOutEvent::InboundError { .. } => { - // TODO: log errors and close connection? - vec![] - } - handler::InboundOutEvent::OutboundError { .. } => { - // TODO: log errors and close connection? - vec![] + handler::InboundOutEvent::InboundError { error, .. } => { + log::warn!("Connection with peer {} failed: {}", peer_id, error); + + vec![NetworkBehaviourAction::CloseConnection { + peer_id, + connection: CloseConnection::One(connection), + }] } + handler::InboundOutEvent::OutboundError { error, .. } => void::unreachable(error), }; self.events.extend(new_events); From 5e3d4a141b2e47addb5b60ff5f86e52b5308df1e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 1 Sep 2021 17:44:43 +1000 Subject: [PATCH 237/242] Remove unnecessary implementation of default fn --- protocols/rendezvous/src/client.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index dde0da885e3..73eb90edb38 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -183,10 +183,6 @@ impl NetworkBehaviour for Behaviour { .collect() } - fn inject_connected(&mut self, _: &PeerId) {} - - fn inject_disconnected(&mut self, _: &PeerId) {} - fn inject_event( &mut self, peer_id: PeerId, From 5696a7e3f21489236ede3d9eb62970a6b9e5fe84 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 1 Sep 2021 17:49:39 +1000 Subject: [PATCH 238/242] Reduce duplication in test setup --- protocols/rendezvous/tests/rendezvous.rs | 71 ++++++++++++++---------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 57a59b844cc..8a2b4ab5255 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -329,37 +329,50 @@ struct RendezvousTest { pub robert: Swarm, } +async fn new_client() -> Swarm { + let mut client = new_swarm(|_, identity| rendezvous::client::Behaviour::new(identity)); + client.listen_on_random_memory_address().await; // we need to listen otherwise we don't have addresses to register + + client +} + +async fn new_server(config: rendezvous::server::Config) -> Swarm { + let mut server = new_swarm(|_, _| { + rendezvous::server::Behaviour::new(config) + }); + + server.listen_on_random_memory_address().await; + + server +} + +async fn new_combined_node() -> Swarm { + let mut node = new_swarm(|_, identity| CombinedBehaviour { + client: rendezvous::client::Behaviour::new(identity), + server: rendezvous::server::Behaviour::new(rendezvous::server::Config::default()), + }); + node.listen_on_random_memory_address().await; + + node +} + +async fn new_impersonating_client() -> Swarm { + // In reality, if Eve were to try and fake someones identity, she would obviously only know the public key. + // Due to the type-safe API of the `Rendezvous` behaviour and `PeerRecord`, we actually cannot construct a bad `PeerRecord` (i.e. one that is claims to be someone else). + // As such, the best we can do is hand eve a completely different keypair from what she is using to authenticate her connection. + let someone_else = identity::Keypair::generate_ed25519(); + let mut eve = new_swarm(move |_, _| rendezvous::client::Behaviour::new(someone_else)); + eve.listen_on_random_memory_address().await; + + eve +} + impl RendezvousTest { pub async fn setup() -> Self { - let mut alice = new_swarm(|_, identity| rendezvous::client::Behaviour::new(identity)); - alice.listen_on_random_memory_address().await; - - let mut bob = new_swarm(|_, identity| rendezvous::client::Behaviour::new(identity)); - bob.listen_on_random_memory_address().await; - - let mut robert = new_swarm(|_, _| { - rendezvous::server::Behaviour::new( - rendezvous::server::Config::default().with_min_ttl(2), - ) - }); - robert.listen_on_random_memory_address().await; - - let mut eve = { - // In reality, if Eve were to try and fake someones identity, she would obviously only know the public key. - // Due to the type-safe API of the `Rendezvous` behaviour and `PeerRecord`, we actually cannot construct a bad `PeerRecord` (i.e. one that is claims to be someone else). - // As such, the best we can do is hand eve a completely different keypair from what she is using to authenticate her connection. - let someone_else = identity::Keypair::generate_ed25519(); - let mut eve = new_swarm(move |_, _| rendezvous::client::Behaviour::new(someone_else)); - eve.listen_on_random_memory_address().await; - - eve - }; - - let mut charlie = new_swarm(|_, identity| CombinedBehaviour { - client: rendezvous::client::Behaviour::new(identity), - server: rendezvous::server::Behaviour::new(rendezvous::server::Config::default()), - }); - charlie.listen_on_random_memory_address().await; + let mut alice = new_client().await; + let mut bob = new_client().await; + let mut robert = new_server(rendezvous::server::Config::default().with_min_ttl(2)).await; + let mut charlie = new_combined_node().await; alice.block_on_connection(&mut robert).await; bob.block_on_connection(&mut robert).await; From 2637e45288102d52e354cd118adeeee6121d2a81 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 1 Sep 2021 18:03:57 +1000 Subject: [PATCH 239/242] Make tests more readable --- protocols/rendezvous/tests/harness/mod.rs | 24 ++ protocols/rendezvous/tests/rendezvous.rs | 421 +++++++++------------- 2 files changed, 193 insertions(+), 252 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index d57992c5f8b..5747f7d19a6 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -99,6 +99,19 @@ where .expect("network behaviours to emit an event within 10 seconds") } +#[macro_export] +macro_rules! assert_behaviour_events { + ($swarm1: ident: $pat1: pat, $swarm2: ident: $pat2: pat, || $body: block) => { + match await_events_or_timeout(&mut $swarm1, &mut $swarm2).await { + ( + libp2p::swarm::SwarmEvent::Behaviour($pat1), + libp2p::swarm::SwarmEvent::Behaviour($pat2), + ) => $body, + _ => panic!("Unexpected combination of events emitted, check logs for details"), + } + }; +} + /// An extension trait for [`Swarm`] that makes it easier to set up a network of [`Swarm`]s for tests. #[async_trait] pub trait SwarmExt { @@ -110,6 +123,9 @@ pub trait SwarmExt { /// Listens on a random memory address, polling the [`Swarm`] until the transport is ready to accept connections. async fn listen_on_random_memory_address(&mut self) -> Multiaddr; + + /// Spawns the given [`Swarm`] into a runtime, polling it endlessly. + fn spawn_into_runtime(self); } #[async_trait] @@ -194,4 +210,12 @@ where multiaddr } + + fn spawn_into_runtime(mut self) { + tokio::spawn(async move { + loop { + self.next().await; + } + }); + } } diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index 8a2b4ab5255..e6ba5fcc39a 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -18,158 +18,165 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#[macro_use] pub mod harness; use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; +use futures::stream::FuturesUnordered; use futures::StreamExt; use libp2p_core::identity; -use libp2p_core::PeerId; use libp2p_rendezvous as rendezvous; use libp2p_swarm::DialError; use libp2p_swarm::{Swarm, SwarmEvent}; +use std::convert::TryInto; use std::time::Duration; #[tokio::test] async fn given_successful_registration_then_successful_discovery() { let _ = env_logger::try_init(); - let mut test = RendezvousTest::setup().await; - let namespace = rendezvous::Namespace::from_static("some-namespace"); + let ([mut alice, mut bob], mut robert) = + new_server_with_connected_clients(rendezvous::server::Config::default()).await; - let _ = - test.alice - .behaviour_mut() - .register(namespace.clone(), *test.robert.local_peer_id(), None); - - test.assert_successful_registration(namespace.clone(), rendezvous::DEFAULT_TTL) - .await; + let _ = alice + .behaviour_mut() + .register(namespace.clone(), *robert.local_peer_id(), None); + + assert_behaviour_events! { + alice: rendezvous::client::Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }, + robert: rendezvous::server::Event::PeerRegistered { peer, registration }, + || { + assert_eq!(&peer, alice.local_peer_id()); + assert_eq!(&rendezvous_node, robert.local_peer_id()); + assert_eq!(registration.namespace, namespace); + assert_eq!(register_node_namespace, namespace); + assert_eq!(ttl, rendezvous::DEFAULT_TTL); + } + }; - test.bob.behaviour_mut().discover( - Some(namespace.clone()), - None, - None, - *test.robert.local_peer_id(), - ); + bob.behaviour_mut() + .discover(Some(namespace.clone()), None, None, *robert.local_peer_id()); - test.assert_successful_discovery( - namespace.clone(), - rendezvous::DEFAULT_TTL, - *test.alice.local_peer_id(), - ) - .await; + assert_behaviour_events! { + bob: rendezvous::client::Event::Discovered { registrations, .. }, + robert: rendezvous::server::Event::DiscoverServed { .. }, + || { + match registrations.as_slice() { + [rendezvous::Registration { + namespace: registered_namespace, + record, + ttl, + }] => { + assert_eq!(*ttl, rendezvous::DEFAULT_TTL); + assert_eq!(record.peer_id(), *alice.local_peer_id()); + assert_eq!(*registered_namespace, namespace); + } + _ => panic!("Expected exactly one registration to be returned from discover"), + } + } + }; } #[tokio::test] async fn given_successful_registration_then_refresh_ttl() { let _ = env_logger::try_init(); - let mut test = RendezvousTest::setup().await; - let namespace = rendezvous::Namespace::from_static("some-namespace"); + let ([mut alice, mut bob], mut robert) = + new_server_with_connected_clients(rendezvous::server::Config::default()).await; - let refesh_ttl = 10_000; - - let _ = - test.alice - .behaviour_mut() - .register(namespace.clone(), *test.robert.local_peer_id(), None); + let roberts_peer_id = *robert.local_peer_id(); + let refresh_ttl = 10_000; - test.assert_successful_registration(namespace.clone(), rendezvous::DEFAULT_TTL) - .await; + let _ = alice + .behaviour_mut() + .register(namespace.clone(), roberts_peer_id, None); - test.bob.behaviour_mut().discover( - Some(namespace.clone()), - None, - None, - *test.robert.local_peer_id(), - ); + assert_behaviour_events! { + alice: rendezvous::client::Event::Registered { .. }, + robert: rendezvous::server::Event::PeerRegistered { .. }, + || { } + }; - test.assert_successful_discovery( - namespace.clone(), - rendezvous::DEFAULT_TTL, - *test.alice.local_peer_id(), - ) - .await; + bob.behaviour_mut() + .discover(Some(namespace.clone()), None, None, roberts_peer_id); - test.alice.behaviour_mut().register( - namespace.clone(), - *test.robert.local_peer_id(), - Some(refesh_ttl), - ); + assert_behaviour_events! { + bob: rendezvous::client::Event::Discovered { .. }, + robert: rendezvous::server::Event::DiscoverServed { .. }, + || { } + }; - test.assert_successful_registration(namespace.clone(), refesh_ttl) - .await; + alice + .behaviour_mut() + .register(namespace.clone(), roberts_peer_id, Some(refresh_ttl)); - test.bob.behaviour_mut().discover( - Some(namespace.clone()), - None, - None, - *test.robert.local_peer_id(), - ); + assert_behaviour_events! { + alice: rendezvous::client::Event::Registered { ttl, .. }, + robert: rendezvous::server::Event::PeerRegistered { .. }, + || { + assert_eq!(ttl, refresh_ttl); + } + }; - test.assert_successful_discovery(namespace.clone(), refesh_ttl, *test.alice.local_peer_id()) - .await; + bob.behaviour_mut() + .discover(Some(namespace.clone()), None, None, *robert.local_peer_id()); + + assert_behaviour_events! { + bob: rendezvous::client::Event::Discovered { registrations, .. }, + robert: rendezvous::server::Event::DiscoverServed { .. }, + || { + match registrations.as_slice() { + [rendezvous::Registration { ttl, .. }] => { + assert_eq!(*ttl, refresh_ttl); + } + _ => panic!("Expected exactly one registration to be returned from discover"), + } + } + }; } #[tokio::test] async fn given_invalid_ttl_then_unsuccessful_registration() { let _ = env_logger::try_init(); - let mut test = RendezvousTest::setup().await; - let namespace = rendezvous::Namespace::from_static("some-namespace"); + let ([mut alice], mut robert) = + new_server_with_connected_clients(rendezvous::server::Config::default()).await; - test.alice.behaviour_mut().register( + alice.behaviour_mut().register( namespace.clone(), - *test.robert.local_peer_id(), + *robert.local_peer_id(), Some(100_000_000), ); - match await_events_or_timeout(&mut test.robert, &mut test.alice).await { - ( - SwarmEvent::Behaviour(rendezvous::server::Event::PeerNotRegistered { .. }), - SwarmEvent::Behaviour(rendezvous::client::Event::RegisterFailed(rendezvous::client::RegisterError::Remote {error , ..})), - ) => { + assert_behaviour_events! { + alice: rendezvous::client::Event::RegisterFailed(rendezvous::client::RegisterError::Remote {error , ..}), + robert: rendezvous::server::Event::PeerNotRegistered { .. }, + || { assert_eq!(error, rendezvous::ErrorCode::InvalidTtl); } - (rendezvous_swarm_event, registration_swarm_event) => panic!( - "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", - rendezvous_swarm_event, registration_swarm_event - ), - } + }; } #[tokio::test] async fn discover_allows_for_dial_by_peer_id() { let _ = env_logger::try_init(); - let RendezvousTest { - mut alice, - mut bob, - mut robert, - charlie: _charlie, - .. - } = RendezvousTest::setup().await; - let roberts_peer_id = *robert.local_peer_id(); - - // poll rendezvous point continuously - tokio::spawn(async move { - loop { - robert.next().await; - } - }); - let namespace = rendezvous::Namespace::from_static("some-namespace"); + let ([mut alice, mut bob], robert) = + new_server_with_connected_clients(rendezvous::server::Config::default()).await; + + let roberts_peer_id = *robert.local_peer_id(); + robert.spawn_into_runtime(); alice .behaviour_mut() .register(namespace.clone(), roberts_peer_id, None); bob.behaviour_mut() - .discover(Some(namespace), None, None, roberts_peer_id); + .discover(Some(namespace.clone()), None, None, roberts_peer_id); - match await_events_or_timeout(&mut alice, &mut bob).await { - ( - SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }), - SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { .. }), - ) => {} - _ => panic!("bad event combination emitted"), + assert_behaviour_events! { + alice: rendezvous::client::Event::Registered { .. }, + bob: rendezvous::client::Event::Discovered { .. }, + || { } }; let alices_peer_id = *alice.local_peer_id(); @@ -202,93 +209,67 @@ async fn discover_allows_for_dial_by_peer_id() { #[tokio::test] async fn eve_cannot_register() { let _ = env_logger::try_init(); - let mut test = RendezvousTest::setup().await; - let namespace = rendezvous::Namespace::from_static("some-namespace"); + let mut robert = new_server(rendezvous::server::Config::default()).await; + let mut eve = new_impersonating_client().await; + eve.block_on_connection(&mut robert).await; - test.eve.behaviour_mut().register( - namespace.clone(), - *test.robert.local_peer_id(), - Some(100_000), - ); + eve.behaviour_mut() + .register(namespace.clone(), *robert.local_peer_id(), None); - match await_events_or_timeout(&mut test.robert, &mut test.eve).await { - ( - SwarmEvent::Behaviour(rendezvous::server::Event::PeerNotRegistered { .. }), - SwarmEvent::Behaviour(rendezvous::client::Event::RegisterFailed(rendezvous::client::RegisterError::Remote { error: err_code , ..})), - ) => { + assert_behaviour_events! { + eve: rendezvous::client::Event::RegisterFailed(rendezvous::client::RegisterError::Remote { error: err_code , ..}), + robert: rendezvous::server::Event::PeerNotRegistered { .. }, + || { assert_eq!(err_code, rendezvous::ErrorCode::NotAuthorized); } - (rendezvous_swarm_event, registration_swarm_event) => panic!( - "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", - rendezvous_swarm_event, registration_swarm_event - ), - } + }; } // test if charlie can operate as client and server simultaneously #[tokio::test] async fn can_combine_client_and_server() { let _ = env_logger::try_init(); - let mut test = RendezvousTest::setup().await; - let namespace = rendezvous::Namespace::from_static("some-namespace"); + let ([mut alice], mut robert) = + new_server_with_connected_clients(rendezvous::server::Config::default()).await; + let mut charlie = new_combined_node().await; + charlie.block_on_connection(&mut robert).await; + alice.block_on_connection(&mut charlie).await; - test.charlie.behaviour_mut().client.register( - namespace.clone(), - *test.robert.local_peer_id(), - None, - ); + charlie + .behaviour_mut() + .client + .register(namespace.clone(), *robert.local_peer_id(), None); - match await_events_or_timeout(&mut test.robert, &mut test.charlie).await { - ( - SwarmEvent::Behaviour(rendezvous::server::Event::PeerRegistered { .. }), - SwarmEvent::Behaviour(CombinedEvent::Client(rendezvous::client::Event::Registered { .. })), - ) => { - } - (rendezvous_swarm_event, registration_swarm_event) => panic!( - "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", - rendezvous_swarm_event, registration_swarm_event - ), - } + assert_behaviour_events! { + charlie: CombinedEvent::Client(rendezvous::client::Event::Registered { .. }), + robert: rendezvous::server::Event::PeerRegistered { .. }, + || { } + }; - test.alice + alice .behaviour_mut() - .register(namespace, *test.charlie.local_peer_id(), None); + .register(namespace, *charlie.local_peer_id(), None); - match await_events_or_timeout(&mut test.alice, &mut test.charlie).await { - ( - SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }), - SwarmEvent::Behaviour(CombinedEvent::Server(rendezvous::server::Event::PeerRegistered { .. })), - ) => { - } - (rendezvous_swarm_event, registration_swarm_event) => panic!( - "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", - rendezvous_swarm_event, registration_swarm_event - ), - } + assert_behaviour_events! { + charlie: CombinedEvent::Server(rendezvous::server::Event::PeerRegistered { .. }), + alice: rendezvous::client::Event::Registered { .. }, + || { } + }; } #[tokio::test] async fn registration_on_clients_expire() { let _ = env_logger::try_init(); - let RendezvousTest { - mut alice, - mut bob, - mut robert, - charlie: _charlie, - .. - } = RendezvousTest::setup().await; - let roberts_peer_id = *robert.local_peer_id(); + let namespace = rendezvous::Namespace::from_static("some-namespace"); + let ([mut alice, mut bob], robert) = + new_server_with_connected_clients(rendezvous::server::Config::default().with_min_ttl(1)) + .await; - // poll rendezvous point continuously - tokio::spawn(async move { - loop { - robert.next().await; - } - }); + let roberts_peer_id = *robert.local_peer_id(); + robert.spawn_into_runtime(); - let namespace = rendezvous::Namespace::from_static("some-namespace"); let registration_ttl = 3; alice @@ -297,12 +278,10 @@ async fn registration_on_clients_expire() { bob.behaviour_mut() .discover(Some(namespace), None, None, roberts_peer_id); - match await_events_or_timeout(&mut alice, &mut bob).await { - ( - SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }), - SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { .. }), - ) => {} - _ => panic!("bad event combination emitted"), + assert_behaviour_events! { + alice: rendezvous::client::Event::Registered { .. }, + bob: rendezvous::client::Event::Discovered { .. }, + || { } }; tokio::time::sleep(Duration::from_secs(registration_ttl + 5)).await; @@ -317,16 +296,30 @@ async fn registration_on_clients_expire() { assert!(matches!(error, DialError::NoAddresses)); } -/// Holds a network of nodes that is used to test certain rendezvous functionality. -/// -/// In all cases, Alice would like to connect to Bob with Robert acting as a rendezvous point. -/// Eve is an evil actor that tries to act maliciously. -struct RendezvousTest { - pub alice: Swarm, - pub bob: Swarm, - pub charlie: Swarm, - pub eve: Swarm, - pub robert: Swarm, +async fn new_server_with_connected_clients( + config: rendezvous::server::Config, +) -> ( + [Swarm; N], + Swarm, +) { + let mut server = new_server(config).await; + + let mut clients: [Swarm<_>; N] = match (0usize..N) + .map(|_| new_client()) + .collect::>() + .collect::>() + .await + .try_into() + { + Ok(clients) => clients, + Err(_) => panic!("Vec is of size N"), + }; + + for client in &mut clients { + client.block_on_connection(&mut server).await; + } + + (clients, server) } async fn new_client() -> Swarm { @@ -337,9 +330,7 @@ async fn new_client() -> Swarm { } async fn new_server(config: rendezvous::server::Config) -> Swarm { - let mut server = new_swarm(|_, _| { - rendezvous::server::Behaviour::new(config) - }); + let mut server = new_swarm(|_, _| rendezvous::server::Behaviour::new(config)); server.listen_on_random_memory_address().await; @@ -367,80 +358,6 @@ async fn new_impersonating_client() -> Swarm { eve } -impl RendezvousTest { - pub async fn setup() -> Self { - let mut alice = new_client().await; - let mut bob = new_client().await; - let mut robert = new_server(rendezvous::server::Config::default().with_min_ttl(2)).await; - let mut charlie = new_combined_node().await; - - alice.block_on_connection(&mut robert).await; - bob.block_on_connection(&mut robert).await; - charlie.block_on_connection(&mut robert).await; - eve.block_on_connection(&mut robert).await; - alice.block_on_connection(&mut charlie).await; - - Self { - alice, - bob, - charlie, - eve, - robert, - } - } - - pub async fn assert_successful_registration( - &mut self, - expected_namespace: rendezvous::Namespace, - expected_ttl: rendezvous::Ttl, - ) { - match await_events_or_timeout(&mut self.robert, &mut self.alice).await { - ( - SwarmEvent::Behaviour(rendezvous::server::Event::PeerRegistered { peer, registration }), - SwarmEvent::Behaviour(rendezvous::client::Event::Registered { rendezvous_node, ttl, namespace: register_node_namespace }), - ) => { - assert_eq!(&peer, self.alice.local_peer_id()); - assert_eq!(&rendezvous_node, self.robert.local_peer_id()); - assert_eq!(registration.namespace, expected_namespace); - assert_eq!(register_node_namespace, expected_namespace); - assert_eq!(ttl, expected_ttl); - } - (rendezvous_swarm_event, registration_swarm_event) => panic!( - "Received unexpected event, rendezvous swarm emitted {:?} and registration swarm emitted {:?}", - rendezvous_swarm_event, registration_swarm_event - ), - } - } - - pub async fn assert_successful_discovery( - &mut self, - expected_namespace: rendezvous::Namespace, - expected_ttl: rendezvous::Ttl, - expected_peer_id: PeerId, - ) { - match await_events_or_timeout(&mut self.robert, &mut self.bob).await { - ( - SwarmEvent::Behaviour(rendezvous::server::Event::DiscoverServed { .. }), - SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { - registrations, .. - }), - ) => match registrations.as_slice() { - [rendezvous::Registration { - namespace, - record, - ttl, - }] => { - assert_eq!(*ttl, expected_ttl); - assert_eq!(record.peer_id(), expected_peer_id); - assert_eq!(*namespace, expected_namespace); - } - _ => panic!("Expected exactly one registration to be returned from discover"), - }, - (e1, e2) => panic!("Unexpected events {:?} {:?}", e1, e2), - } - } -} - #[derive(libp2p::NetworkBehaviour)] #[behaviour(event_process = false, out_event = "CombinedEvent")] struct CombinedBehaviour { From f8edf2f7dc0283434e3498965e7eafc480071146 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 7 Sep 2021 11:46:03 +1000 Subject: [PATCH 240/242] Final polish - Changelogs - No default features of `libp2p-core` - Cargo metadata update --- CHANGELOG.md | 1 + core/CHANGELOG.md | 6 ++++++ protocols/rendezvous/CHANGELOG.md | 3 +++ protocols/rendezvous/Cargo.toml | 6 +++--- 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 protocols/rendezvous/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c9284c09eb8..c28e4afe1e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - [`libp2p-ping` CHANGELOG](protocols/ping/CHANGELOG.md) - [`libp2p-relay` CHANGELOG](protocols/relay/CHANGELOG.md) - [`libp2p-request-response` CHANGELOG](protocols/request-response/CHANGELOG.md) +- [`libp2p-rendezvous` CHANGELOG](protocols/rendezvous/CHANGELOG.md) ## Transport Protocols & Upgrades diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 3b1ec3c43e1..b283644a8fd 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -30,12 +30,18 @@ - Report abortion of pending connection through `DialError`, `UnknownPeerDialError` or `IncomingConnectionError` (see [PR 2191]). +- Add `SignedEnvelope` and `PeerRecord` according to [RFC0002] and [RFC0003]. + See [PR 2107]. + [PR 2145]: https://github.com/libp2p/rust-libp2p/pull/2145 [PR 2142]: https://github.com/libp2p/rust-libp2p/pull/2142 [PR 2137]: https://github.com/libp2p/rust-libp2p/pull/2137 [PR 2183]: https://github.com/libp2p/rust-libp2p/pull/2183 [PR 2191]: https://github.com/libp2p/rust-libp2p/pull/2191 [PR 2195]: https://github.com/libp2p/rust-libp2p/pull/2195 +[PR 2107]: https://github.com/libp2p/rust-libp2p/pull/2107 +[RFC0002]: https://github.com/libp2p/specs/blob/master/RFC/0002-signed-envelopes.md +[RFC0003]: https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md # 0.29.0 [2021-07-12] diff --git a/protocols/rendezvous/CHANGELOG.md b/protocols/rendezvous/CHANGELOG.md new file mode 100644 index 00000000000..ab400cdaf64 --- /dev/null +++ b/protocols/rendezvous/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0 [unreleased] + +- Initial release. diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 0c3ad8e31c5..1ebfae9d3b7 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-rendezvous" edition = "2018" description = "Rendezvous protocol for libp2p" version = "0.1.0" -authors = ["Comit Guys "] +authors = ["The COMIT guys "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] @@ -11,7 +11,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] asynchronous-codec = "0.6" -libp2p-core = { version = "0.30.0", path = "../../core" } +libp2p-core = { version = "0.30.0", path = "../../core", default-features = false } libp2p-swarm = { version = "0.31.0", path = "../../swarm" } prost = "0.7" void = "1" @@ -30,7 +30,7 @@ rand = "0.8" async-std = { version = "1", features = ["attributes"] } env_logger = "0.8" async-trait = "0.1" -tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } # TODO: REMOVE FOR PRODUCTION USE +tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } [build-dependencies] prost-build = "0.7" From 2215f028fd7037c1e5400039dc9fbe6752e22f30 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 7 Sep 2021 11:48:15 +1000 Subject: [PATCH 241/242] Adhere to formatting convention of changelog --- core/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 150edf3eb53..0ec8117275d 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -33,8 +33,8 @@ - Remove deprecated functions `upgrade::write_one`, `upgrade::write_with_len_prefix` and `upgrade::read_one` (see [PR 2213]). -- Add `SignedEnvelope` and `PeerRecord` according to [RFC0002] and [RFC0003]. - See [PR 2107]. +- Add `SignedEnvelope` and `PeerRecord` according to [RFC0002] and [RFC0003] + (see [PR 2107). [PR 2145]: https://github.com/libp2p/rust-libp2p/pull/2145 [PR 2213]: https://github.com/libp2p/rust-libp2p/pull/2213 From 53ce603e9301947d0fb07eda323df472805992c6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 7 Sep 2021 11:48:54 +1000 Subject: [PATCH 242/242] Fix bad link --- core/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 0ec8117275d..1c4ace6835f 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -34,7 +34,7 @@ and `upgrade::read_one` (see [PR 2213]). - Add `SignedEnvelope` and `PeerRecord` according to [RFC0002] and [RFC0003] - (see [PR 2107). + (see [PR 2107]). [PR 2145]: https://github.com/libp2p/rust-libp2p/pull/2145 [PR 2213]: https://github.com/libp2p/rust-libp2p/pull/2213