Skip to content

Commit

Permalink
protocols/relay: Implement circuit relay v2 protocol (#2059)
Browse files Browse the repository at this point in the history
This commit adds an implementation for the circuit relay v2 protocol to be used
as a relay server, i.e. it supports incoming HOP requests and outgoing STOP
requests and used as a relay clients, i.e. outgoing HOP requests and incoming
STOP requests.

The existing circuit relay v1 protocol implementation is moved to
protocols/relay/src/v1.

Co-authored-by: ronzigelman <ronzigelman@gmail.com>
Co-authored-by: Marco Munizaga <git@marcopolo.io>
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
Co-authored-by: Elena Frank <57632201+elenaf9@users.noreply.github.com>
  • Loading branch information
5 people authored Jan 14, 2022
1 parent 3e39cd7 commit 17ee504
Show file tree
Hide file tree
Showing 38 changed files with 5,919 additions and 270 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ noise = ["libp2p-noise"]
ping = ["libp2p-ping", "libp2p-metrics/ping"]
plaintext = ["libp2p-plaintext"]
pnet = ["libp2p-pnet"]
relay = ["libp2p-relay"]
relay = ["libp2p-relay", "libp2p-metrics/relay"]
request-response = ["libp2p-request-response"]
rendezvous = ["libp2p-rendezvous"]
tcp-async-io = ["libp2p-tcp", "libp2p-tcp/async-io"]
Expand Down
2 changes: 2 additions & 0 deletions misc/metrics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ gossipsub = ["libp2p-gossipsub"]
identify = ["libp2p-identify"]
kad = ["libp2p-kad"]
ping = ["libp2p-ping"]
relay = ["libp2p-relay"]

[dependencies]
libp2p-core = { version = "0.31.0", path = "../../core", default-features = false }
libp2p-gossipsub = { version = "0.35.0", path = "../../protocols/gossipsub", optional = true }
libp2p-identify = { version = "0.33.0", path = "../../protocols/identify", optional = true }
libp2p-kad = { version = "0.34.0", path = "../../protocols/kad", optional = true }
libp2p-ping = { version = "0.33.0", path = "../../protocols/ping", optional = true }
libp2p-relay = { version = "0.6.0", path = "../../protocols/relay", optional = true }
libp2p-swarm = { version = "0.33.0", path = "../../swarm" }
open-metrics-client = "0.14.0"

Expand Down
6 changes: 6 additions & 0 deletions misc/metrics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ mod identify;
mod kad;
#[cfg(feature = "ping")]
mod ping;
#[cfg(feature = "relay")]
mod relay;
mod swarm;

use open_metrics_client::registry::Registry;
Expand All @@ -47,6 +49,8 @@ pub struct Metrics {
kad: kad::Metrics,
#[cfg(feature = "ping")]
ping: ping::Metrics,
#[cfg(feature = "relay")]
relay: relay::Metrics,
swarm: swarm::Metrics,
}

Expand All @@ -70,6 +74,8 @@ impl Metrics {
kad: kad::Metrics::new(sub_registry),
#[cfg(feature = "ping")]
ping: ping::Metrics::new(sub_registry),
#[cfg(feature = "relay")]
relay: relay::Metrics::new(sub_registry),
swarm: swarm::Metrics::new(sub_registry),
}
}
Expand Down
114 changes: 114 additions & 0 deletions misc/metrics/src/relay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2021 Protocol Labs.
//
// 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 open_metrics_client::encoding::text::Encode;
use open_metrics_client::metrics::counter::Counter;
use open_metrics_client::metrics::family::Family;
use open_metrics_client::registry::Registry;

pub struct Metrics {
events: Family<EventLabels, Counter>,
}

impl Metrics {
pub fn new(registry: &mut Registry) -> Self {
let sub_registry = registry.sub_registry_with_prefix("relay");

let events = Family::default();
sub_registry.register(
"events",
"Events emitted by the relay NetworkBehaviour",
Box::new(events.clone()),
);

Self { events }
}
}

#[derive(Debug, Clone, Hash, PartialEq, Eq, Encode)]
struct EventLabels {
event: EventType,
}

#[derive(Debug, Clone, Hash, PartialEq, Eq, Encode)]
enum EventType {
ReservationReqAccepted,
ReservationReqAcceptFailed,
ReservationReqDenied,
ReservationReqDenyFailed,
ReservationTimedOut,
CircuitReqReceiveFailed,
CircuitReqDenied,
CircuitReqDenyFailed,
CircuitReqOutboundConnectFailed,
CircuitReqAccepted,
CircuitReqAcceptFailed,
CircuitClosed,
}

impl From<&libp2p_relay::v2::relay::Event> for EventType {
fn from(event: &libp2p_relay::v2::relay::Event) -> Self {
match event {
libp2p_relay::v2::relay::Event::ReservationReqAccepted { .. } => {
EventType::ReservationReqAccepted
}
libp2p_relay::v2::relay::Event::ReservationReqAcceptFailed { .. } => {
EventType::ReservationReqAcceptFailed
}
libp2p_relay::v2::relay::Event::ReservationReqDenied { .. } => {
EventType::ReservationReqDenied
}
libp2p_relay::v2::relay::Event::ReservationReqDenyFailed { .. } => {
EventType::ReservationReqDenyFailed
}
libp2p_relay::v2::relay::Event::ReservationTimedOut { .. } => {
EventType::ReservationTimedOut
}
libp2p_relay::v2::relay::Event::CircuitReqReceiveFailed { .. } => {
EventType::CircuitReqReceiveFailed
}
libp2p_relay::v2::relay::Event::CircuitReqDenied { .. } => EventType::CircuitReqDenied,
libp2p_relay::v2::relay::Event::CircuitReqOutboundConnectFailed { .. } => {
EventType::CircuitReqOutboundConnectFailed
}
libp2p_relay::v2::relay::Event::CircuitReqDenyFailed { .. } => {
EventType::CircuitReqDenyFailed
}
libp2p_relay::v2::relay::Event::CircuitReqAccepted { .. } => {
EventType::CircuitReqAccepted
}
libp2p_relay::v2::relay::Event::CircuitReqAcceptFailed { .. } => {
EventType::CircuitReqAcceptFailed
}
libp2p_relay::v2::relay::Event::CircuitClosed { .. } => EventType::CircuitClosed,
}
}
}

impl super::Recorder<libp2p_relay::v2::relay::Event> for super::Metrics {
fn record(&self, event: &libp2p_relay::v2::relay::Event) {
self.relay
.events
.get_or_create(&EventLabels {
event: event.into(),
})
.inc();
}
}
12 changes: 8 additions & 4 deletions protocols/relay/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ edition = "2021"
rust-version = "1.56.1"
description = "Communications relaying for libp2p"
version = "0.6.0"
authors = ["Parity Technologies <admin@parity.io>"]
authors = ["Parity Technologies <admin@parity.io>", "Max Inden <mail@max-inden.de>"]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
keywords = ["peer-to-peer", "libp2p", "networking"]
Expand All @@ -13,6 +13,7 @@ categories = ["network-programming", "asynchronous"]
[dependencies]
asynchronous-codec = "0.6"
bytes = "1"
either = "1.6.0"
futures = "0.3.1"
futures-timer = "3"
instant = "0.1.11"
Expand All @@ -21,8 +22,10 @@ libp2p-swarm = { version = "0.33.0", path = "../../swarm" }
log = "0.4"
pin-project = "1"
prost = "0.9"
rand = "0.7"
rand = "0.8.4"
smallvec = "1.6.1"
static_assertions = "1"
thiserror = "1.0"
unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] }
void = "1"

Expand All @@ -31,10 +34,11 @@ prost-build = "0.9"

[dev-dependencies]
env_logger = "0.9.0"
structopt = "0.3.21"
libp2p = { path = "../.." }
libp2p-identify = { path = "../identify" }
libp2p-kad = { path = "../kad" }
libp2p-ping = { path = "../ping" }
libp2p-identify = { path = "../identify" }
libp2p-plaintext = { path = "../../transports/plaintext" }
libp2p-yamux = { path = "../../muxers/yamux" }
quickcheck = "1"
structopt = "0.3.21"
3 changes: 2 additions & 1 deletion protocols/relay/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@
// DEALINGS IN THE SOFTWARE.

fn main() {
prost_build::compile_protos(&["src/message.proto"], &["src"]).unwrap();
prost_build::compile_protos(&["src/v1/message.proto"], &["src/v1"]).unwrap();
prost_build::compile_protos(&["src/v2/message.proto"], &["src/v2"]).unwrap();
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ use futures::executor::block_on;
use futures::stream::StreamExt;
use libp2p::dns::DnsConfig;
use libp2p::plaintext;
use libp2p::relay::{Relay, RelayConfig};
use libp2p::relay::v1::{Relay, RelayConfig};
use libp2p::swarm::SwarmEvent;
use libp2p::tcp::TcpConfig;
use libp2p::Transport;
Expand Down Expand Up @@ -94,7 +94,7 @@ fn main() -> Result<(), Box<dyn Error>> {
..Default::default()
};
let (relay_wrapped_transport, relay_behaviour) =
libp2p_relay::new_transport_and_behaviour(relay_config, transport);
libp2p_relay::v1::new_transport_and_behaviour(relay_config, transport);

let behaviour = Behaviour {
relay: relay_behaviour,
Expand Down
152 changes: 152 additions & 0 deletions protocols/relay/examples/relay_v2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// Copyright 2021 Protocol Labs.
//
// 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::executor::block_on;
use futures::stream::StreamExt;
use libp2p::core::upgrade;
use libp2p::identify::{Identify, IdentifyConfig, IdentifyEvent};
use libp2p::multiaddr::Protocol;
use libp2p::ping::{Ping, PingConfig, PingEvent};
use libp2p::relay::v2::relay::{self, Relay};
use libp2p::swarm::{Swarm, SwarmEvent};
use libp2p::tcp::TcpConfig;
use libp2p::Transport;
use libp2p::{identity, NetworkBehaviour, PeerId};
use libp2p::{noise, Multiaddr};
use std::error::Error;
use std::net::{Ipv4Addr, Ipv6Addr};
use structopt::StructOpt;

fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();

let opt = Opt::from_args();
println!("opt: {:?}", opt);

// Create a static known PeerId based on given secret
let local_key: identity::Keypair = generate_ed25519(opt.secret_key_seed);
let local_peer_id = PeerId::from(local_key.public());
println!("Local peer id: {:?}", local_peer_id);

let tcp_transport = TcpConfig::new();

let noise_keys = noise::Keypair::<noise::X25519Spec>::new()
.into_authentic(&local_key)
.expect("Signing libp2p-noise static DH keypair failed.");

let transport = tcp_transport
.upgrade(upgrade::Version::V1)
.authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated())
.multiplex(libp2p_yamux::YamuxConfig::default())
.boxed();

let behaviour = Behaviour {
relay: Relay::new(local_peer_id, Default::default()),
ping: Ping::new(PingConfig::new()),
identify: Identify::new(IdentifyConfig::new(
"/TODO/0.0.1".to_string(),
local_key.public(),
)),
};

let mut swarm = Swarm::new(transport, behaviour, local_peer_id);

// Listen on all interfaces
let listen_addr = Multiaddr::empty()
.with(match opt.use_ipv6 {
Some(true) => Protocol::from(Ipv6Addr::UNSPECIFIED),
_ => Protocol::from(Ipv4Addr::UNSPECIFIED),
})
.with(Protocol::Tcp(opt.port));
swarm.listen_on(listen_addr)?;

block_on(async {
loop {
match swarm.next().await.expect("Infinite Stream.") {
SwarmEvent::Behaviour(Event::Relay(event)) => {
println!("{:?}", event)
}
SwarmEvent::NewListenAddr { address, .. } => {
println!("Listening on {:?}", address);
}
_ => {}
}
}
})
}

#[derive(NetworkBehaviour)]
#[behaviour(out_event = "Event", event_process = false)]
struct Behaviour {
relay: Relay,
ping: Ping,
identify: Identify,
}

#[derive(Debug)]
enum Event {
Ping(PingEvent),
Identify(IdentifyEvent),
Relay(relay::Event),
}

impl From<PingEvent> for Event {
fn from(e: PingEvent) -> Self {
Event::Ping(e)
}
}

impl From<IdentifyEvent> for Event {
fn from(e: IdentifyEvent) -> Self {
Event::Identify(e)
}
}

impl From<relay::Event> for Event {
fn from(e: relay::Event) -> Self {
Event::Relay(e)
}
}

fn generate_ed25519(secret_key_seed: u8) -> identity::Keypair {
let mut bytes = [0u8; 32];
bytes[0] = secret_key_seed;

let secret_key = identity::ed25519::SecretKey::from_bytes(&mut bytes)
.expect("this returns `Err` only if the length is wrong; the length is correct; qed");
identity::Keypair::Ed25519(secret_key.into())
}

#[derive(Debug, StructOpt)]
#[structopt(name = "libp2p relay")]
struct Opt {
/// Determine if the relay listen on ipv6 or ipv4 loopback address. the default is ipv4
#[structopt(long)]
use_ipv6: Option<bool>,

/// Fixed value to generate deterministic peer id
#[structopt(long)]
secret_key_seed: u8,

/// The port used to listen on all interfaces
#[structopt(long)]
port: u16,
}
Loading

0 comments on commit 17ee504

Please sign in to comment.