Skip to content

Commit

Permalink
protocols/relay: Simplify running the relay example (#2080)
Browse files Browse the repository at this point in the history
Simplify running the relay example with all peers run via docker compose.

Co-authored-by: Max Inden <mail@max-inden.de>
  • Loading branch information
r-zig and mxinden authored Jun 30, 2021
1 parent e038f87 commit f9491e7
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 51 deletions.
1 change: 1 addition & 0 deletions protocols/relay/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ prost-build = "0.7"

[dev-dependencies]
env_logger = "0.8.3"
structopt = "0.3.21"
libp2p = { path = "../.." }
libp2p-kad = { path = "../kad" }
libp2p-ping = { path = "../ping" }
Expand Down
19 changes: 19 additions & 0 deletions protocols/relay/examples/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 1: Build the exe
FROM rust:1 as builder
LABEL Name=libp2p-relay Version=0.0.1
# 1a: Prepare for static linking
RUN apt-get update && \
apt-get dist-upgrade -y && \
apt-get install -y musl-tools && \
rustup target add x86_64-unknown-linux-musl
# 1b: Download and compile Rust dependencies (and store as a separate Docker layer)
WORKDIR /usr/src/libp2p

COPY . .
RUN cargo build --target x86_64-unknown-linux-musl --example=relay --package=libp2p-relay

# 2: Copy the exe and extra files ("static") to an empty Docker image
FROM debian:buster-slim

COPY --from=builder /usr/src/libp2p/target/x86_64-unknown-linux-musl/debug/examples/relay .
USER 1000
63 changes: 63 additions & 0 deletions protocols/relay/examples/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Run `docker-compose up` to start the setup.

version: '2.1'
services:
relay:
image: libp2p-relay
command:
- "./relay"
- "--mode=relay"
- "--secret-key-seed=1"
- "--address=/ip6/::/tcp/4444"
build:
context: ../../../.
dockerfile: ./protocols/relay/examples/Dockerfile
networks:
- network-a
- network-b

client-listen:
image: libp2p-relay
command:
- "./relay"
- "--mode=client-listen"
- "--secret-key-seed=2"
- "--address=/dns6/relay/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit"
build:
context: ../../../.
dockerfile: ./protocols/relay/examples/Dockerfile
depends_on:
- "relay"
networks:
- network-a

client-dial:
image: libp2p-relay
command:
- "./relay"
- "--mode=client-dial"
- "--secret-key-seed=3"
- "--address=/dns6/relay/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3"
build:
context: ../../../.
dockerfile: ./protocols/relay/examples/Dockerfile
depends_on:
- "client-listen"
networks:
- network-b

networks:
network-a:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: 2001:3984:3989::/64
network-b:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: 2001:3984:3988::/64
210 changes: 159 additions & 51 deletions protocols/relay/examples/relay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,63 +24,78 @@
//! relay client listening via the relay server and (3) a dialing relay client
//! dialing the listening relay client via the relay server.
//!
//! 1. To start the relay server, run `cargo run --example relay -- relay` which will print
//! something along the lines of:
//! 1. To start the relay server, run `cargo run --example=relay --package=libp2p-relay --mode relay --secret-key-seed 1 --address /ip4/<ip address>/tcp/<port>`.
//! The `-secret-key-seed` helps create a static peer id using the given number argument as a seed.
//! The mode specifies whether the node should run as a relay server, a listening client or a dialing client.
//! The address specifies a static address. Usually it will be some loop back address such as `/ip4/0.0.0.0/tcp/4444`.
//! Example:
//! `cargo run --example=relay --package=libp2p-relay -- --mode relay --secret-key-seed 1 --address /ip4/0.0.0.0/tcp/4444`
//! `cargo run --example=relay --package=libp2p-relay -- --mode relay --secret-key-seed 1 --address /ip6/::/tcp/4444`
//!
//! ```
//! Local peer id: PeerId("12D3KooWAP5X5k9DS94n7AsiUAsaiso59Kioh14j2c13fCiudjdZ")
//! # ^-- <peer-id-relay-server>
//! Listening on "/ip6/::1/tcp/36537"
//! # ^-- <addr-relay-server>
//! ```
//!
//! 2. To start the listening relay client run `cargo run --example relay -- client-listen
//! 2. To start the listening relay client run `cargo run --example=relay --package=libp2p-relay -- --mode client-listen --secret-key-seed 2 --address
//! <addr-relay-server>/p2p/<peer-id-relay-server>/p2p-circuit` in a second terminal where:
//!
//! - `<addr-relay-server>`: one of the listening addresses of the relay server
//! - `<peer-id-relay-server>`: the peer id of the relay server
//! - `<addr-relay-server>` is replaced by one of the listening addresses of the relay server.
//! - `<peer-id-relay-server>` is replaced by the peer id of the relay server.
//!
//! Example:
//! `cargo run --example=relay --package=libp2p-relay -- --mode client-listen --secret-key-seed 2 --address /ip4/127.0.0.1/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit`
//! `cargo run --example=relay --package=libp2p-relay -- --mode client-listen --secret-key-seed 2 --address /ip6/::1/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit`
//!
//! 3. To start the dialing relay client run `cargo run --example relay -- client-dial
//! <addr-relay-server>/p2p/<peer-id-relay-server>/p2p-circuit/p2p/<peer-id-listening-relay-client>`
//! in a third terminal where:
//! 3. To start the dialing relay client run `cargo run --example=relay --package=libp2p-relay -- --mode client-dial --secret-key-seed 3 --address
//! <addr-relay-server>/p2p/<peer-id-relay-server>/p2p-circuit/p2p/<peer-id-listening-relay-client>` in
//! a third terminal where:
//!
//! - `<addr-relay-server>`: one of the listening addresses of the relay server
//! - `<peer-id-relay-server>`: the peer id of the relay server
//! - `<peer-id-listening-relay-client>`: the peer id of the listening relay client
//! - `<addr-relay-server>` is replaced by one of the listening addresses of the relay server.
//! - `<peer-id-relay-server>` is replaced by the peer id of the relay server.
//! - `<peer-id-listening-relay-client>` is replaced by the peer id of the listening relay client.
//! Example:
//! `cargo run --example=relay --package=libp2p-relay -- --mode client-dial --secret-key-seed 3 --address /ip4/127.0.0.1/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3`
//! `cargo run --example=relay --package=libp2p-relay -- --mode client-dial --secret-key-seed 3 --address /ip6/::1/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3`
//!
//! In the third terminal you will see the dialing relay client to receive pings from both the relay
//! server AND from the listening relay client relayed via the relay server.
//! In the third terminal you will see the dialing relay client to receive pings
//! from both the relay server AND from the listening relay client relayed via
//! the relay server.
use futures::executor::block_on;
use futures::stream::StreamExt;
use libp2p::core::upgrade;
use libp2p::dns::DnsConfig;
use libp2p::ping::{Ping, PingConfig, PingEvent};
use libp2p::plaintext;
use libp2p::relay::{Relay, RelayConfig};
use libp2p::swarm::SwarmEvent;
use libp2p::tcp::TcpConfig;
use libp2p::Transport;
use libp2p::{identity, Multiaddr, NetworkBehaviour, PeerId, Swarm};
use libp2p::{core::upgrade, identity::ed25519};
use libp2p::{identity, NetworkBehaviour, PeerId, Swarm};
use std::error::Error;
use std::task::{Context, Poll};
use std::time::Duration;
use std::{fmt, str::FromStr};
use structopt::StructOpt;

// Listen on all interfaces and whatever port the OS assigns
const DEFAULT_RELAY_ADDRESS: &str = "/ip4/0.0.0.0/tcp/0";

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

// Create a random PeerId
let local_key = identity::Keypair::generate_ed25519();
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 transport = block_on(DnsConfig::system(TcpConfig::new()))?;

let relay_config = RelayConfig {
connection_idle_timeout: Duration::from_secs(10 * 60),
..Default::default()
};
let (relay_wrapped_transport, relay_behaviour) =
libp2p_relay::new_transport_and_behaviour(relay_config, tcp_transport);
libp2p_relay::new_transport_and_behaviour(relay_config, transport);

let behaviour = Behaviour {
relay: relay_behaviour,
Expand All @@ -103,40 +118,33 @@ fn main() -> Result<(), Box<dyn Error>> {

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

match std::env::args()
.nth(1)
.expect("Please provide either of relay, client-listen or client-dial.")
.as_str()
{
"relay" => {
// Listen on all interfaces and whatever port the OS assigns
swarm.listen_on("/ip6/::/tcp/0".parse()?)?;
match opt.mode {
Mode::Relay => {
let address = get_relay_address(&opt);
swarm.listen_on(address.parse()?)?;
println!("starting listening as relay on {}", &address);
}
"client-listen" => {
let addr: Multiaddr = std::env::args()
.nth(2)
.expect("Please provide relayed listen address.")
.parse()?;
swarm.listen_on(addr)?;
Mode::ClientListen => {
let relay_address = get_relay_peer_address(&opt);
swarm.listen_on(relay_address.parse()?)?;
println!("starting client listener via relay on {}", &relay_address);
}
"client-dial" => {
let addr: Multiaddr = std::env::args()
.nth(2)
.expect("Please provide relayed dial address.")
.parse()?;
swarm.dial_addr(addr)?;
Mode::ClientDial => {
let client_listen_address = get_client_listen_address(&opt);
swarm.dial_addr(client_listen_address.parse()?)?;
println!("starting as client dialer on {}", client_listen_address);
}
s => panic!("Unexpected argument {:?}", s),
}

block_on(futures::future::poll_fn(move |cx: &mut Context<'_>| {
loop {
match swarm.poll_next_unpin(cx) {
Poll::Ready(Some(event)) => {
if let SwarmEvent::NewListenAddr(addr) = event {
println!("Listening on {:?}", addr);
Poll::Ready(Some(event)) => match event {
SwarmEvent::NewListenAddr(addr) => {
print_listener_peer(&addr, &opt.mode, local_peer_id)
}
}
_ => println!("{:?}", event),
},
Poll::Ready(None) => return Poll::Ready(Ok(())),
Poll::Pending => break,
}
Expand All @@ -145,6 +153,62 @@ fn main() -> Result<(), Box<dyn Error>> {
}))
}

fn print_listener_peer(addr: &libp2p::Multiaddr, mode: &Mode, local_peer_id: PeerId) -> () {
match mode {
Mode::Relay => {
println!(
"Peer that act as Relay can access on: `{}/p2p/{}/p2p-circuit`",
addr, local_peer_id
);
}
Mode::ClientListen => {
println!(
"Peer that act as Client Listen can access on: `/p2p/{}/{}`",
addr, local_peer_id
);
}
Mode::ClientDial => {
println!("Peer that act as Client Dial Listening on {:?}", addr);
}
}
}

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

let secret_key = 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())
}

/// Get the address for relay mode
fn get_relay_address(opt: &Opt) -> String {
match &opt.address {
Some(address) => address.clone(),
None => {
println!("--address argument was not provided, will use the default listening relay address: {}",DEFAULT_RELAY_ADDRESS);
DEFAULT_RELAY_ADDRESS.to_string()
}
}
}

/// Get the address for client_listen mode
fn get_relay_peer_address(opt: &Opt) -> String {
match &opt.address {
Some(address) => address.clone(),
None => panic!("Please provide relayed listen address such as: <addr-relay-server>/p2p/<peer-id-relay-server>/p2p-circuit"),
}
}

/// Get the address for client-dial mode
fn get_client_listen_address(opt: &Opt) -> String {
match &opt.address {
Some(address) => address.clone(),
None => panic!("Please provide client listen address such as: <addr-relay-server>/p2p/<peer-id-relay-server>/p2p-circuit/p2p/<peer-id-listening-relay-client>")
}
}

#[derive(NetworkBehaviour)]
#[behaviour(out_event = "Event", event_process = false)]
struct Behaviour {
Expand All @@ -169,3 +233,47 @@ impl From<()> for Event {
Event::Relay(())
}
}

#[derive(Debug, StructOpt)]
enum Mode {
Relay,
ClientListen,
ClientDial,
}

impl FromStr for Mode {
type Err = ModeError;
fn from_str(mode: &str) -> Result<Self, Self::Err> {
match mode {
"relay" => Ok(Mode::Relay),
"client-listen" => Ok(Mode::ClientListen),
"client-dial" => Ok(Mode::ClientDial),
_ => Err(ModeError {}),
}
}
}

#[derive(Debug)]
struct ModeError {}
impl Error for ModeError {}
impl fmt::Display for ModeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Could not parse a mode")
}
}

#[derive(Debug, StructOpt)]
#[structopt(name = "libp2p relay")]
struct Opt {
/// The mode (relay, client-listen, client-dial)
#[structopt(long)]
mode: Mode,

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

/// The listening address
#[structopt(long)]
address: Option<String>,
}

0 comments on commit f9491e7

Please sign in to comment.