Skip to content

Commit a2fe791

Browse files
committed
feat: add behaviours module, add relay client support
1 parent a82bbc7 commit a2fe791

10 files changed

Lines changed: 304 additions & 142 deletions

File tree

Cargo.lock

Lines changed: 84 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ publish = false
2626
anyhow = "*" # for testing purposes only
2727
axum = "0.8.6"
2828
chrono = { version = "0.4", features = ["serde"] }
29+
clap = { version = "4.5.53", features = ["derive"] }
2930
hex = { version = "^0.4.3" }
3031
serde = { version = "1.0", features = ["derive"] }
3132
serde_json = { version = "^1.0" }

crates/charon-p2p/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@ tokio.workspace = true
1919
charon-testutil.workspace = true
2020
vise-exporter.workspace = true
2121
anyhow.workspace = true
22+
clap.workspace = true
23+
charon-k1util.workspace = true
2224

2325
[lints]
2426
workspace = true
2527

2628
[features]
27-
mdns = ["libp2p/mdns"]
29+
mdns = ["libp2p/mdns"]
30+
31+
[[example]]
32+
name = "p2p"
33+
required-features = ["mdns"]

crates/charon-p2p/examples/p2p.rs

Lines changed: 83 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,124 @@
1-
#![allow(missing_docs)]
2-
3-
use std::net::Ipv4Addr;
1+
//! P2P example
2+
//!
3+
//! This example creates a Pluto P2P node and connects to a relay.
4+
//! Also, it discovers other Pluto nodes using mDNS (requires the `mdns`
5+
//! feature).
46
57
use anyhow::Result;
6-
use charon_eth2::enr::{Record, with_ip_impl, with_tcp_impl, with_udp_impl};
8+
use charon_eth2::enr::Record;
79
use charon_p2p::{
10+
behaviours::pluto::{PlutoBehaviourEvent},
11+
behaviours::pluto_mdns::{PlutoMdnsBehaviour, PlutoMdnsBehaviourEvent},
812
config::P2PConfig,
913
gater::ConnGater,
10-
peer::peer_id_from_key,
11-
p2p::{Node, NodeType, PlutoBehavior, PlutoBehaviorEvent},
14+
p2p::{Node, NodeType},
1215
};
16+
use clap::Parser;
1317
use k256::elliptic_curve::rand_core::OsRng;
14-
use libp2p::{Multiaddr, futures::StreamExt, identify, swarm::SwarmEvent};
18+
use libp2p::{Multiaddr, futures::StreamExt, identify, multiaddr::Protocol, swarm::SwarmEvent};
1519
use tokio::signal;
1620

21+
/// Command line arguments
22+
#[derive(Debug, Parser)]
23+
pub struct Args {
24+
/// The port to listen on
25+
#[arg(short, long, default_value = "1050")]
26+
pub port: u16,
27+
/// The ENRs to listen on
28+
#[arg(short, long)]
29+
pub enrs: Vec<String>,
30+
/// The relay URL to dial
31+
#[arg(short, long)]
32+
pub relay_url: Option<Multiaddr>,
33+
}
34+
1735
#[tokio::main]
1836
async fn main() -> Result<()> {
1937
let key = k256::SecretKey::random(&mut OsRng);
20-
let mut p2p: Node<PlutoBehavior> = Node::new(
38+
let mut p2p: Node<_> = Node::new(
2139
P2PConfig::default(),
2240
key.clone(),
2341
ConnGater,
2442
false,
2543
NodeType::QUIC,
26-
PlutoBehavior::new,
27-
);
28-
29-
let swarm = &mut p2p.swarm;
30-
31-
// Get port from environment variable or default to 1050
32-
let port = std::env::var("PORT")
33-
.ok()
34-
.and_then(|p| p.parse::<u16>().ok())
35-
.unwrap_or(1050);
36-
37-
let enr = Record::new(
38-
key.clone(),
39-
vec![
40-
with_tcp_impl(port),
41-
with_udp_impl(port),
42-
with_ip_impl(Ipv4Addr::new(0, 0, 0, 0)),
43-
],
44-
)
45-
.unwrap();
46-
47-
println!("ENR: {}", enr);
48-
49-
swarm.listen_on(format!("/ip4/0.0.0.0/udp/{}/quic-v1", port).parse()?)?;
50-
swarm.listen_on(format!("/ip4/0.0.0.0/tcp/{}", port).parse()?)?;
51-
52-
// Fetch peers from CLI arguments (ENR strings)
53-
// Usage: cargo run --example p2p -- <enr1> <enr2> ...
54-
for enr_str in std::env::args().skip(1) {
55-
match Record::try_from(enr_str.as_str()) {
56-
Ok(enr) => {
57-
println!("Adding peer: {:?}", enr);
58-
// Extract public key and convert to PeerId
59-
let Some(public_key) = enr.public_key else {
60-
eprintln!("ENR missing public key");
61-
continue;
62-
};
63-
64-
let peer_id = match peer_id_from_key(public_key) {
65-
Ok(peer_id) => peer_id,
66-
Err(e) => {
67-
eprintln!("Failed to convert ENR public key to PeerId: {}", e);
68-
continue;
69-
}
70-
};
44+
PlutoMdnsBehaviour::new,
45+
)?;
7146

72-
// Extract IP and ports from ENR
73-
let ip = enr.ip().unwrap_or(Ipv4Addr::new(0, 0, 0, 0));
47+
let args = Args::parse();
7448

75-
// Try to add TCP address if available
76-
let tcp_port = enr.tcp().unwrap_or(3610);
77-
let udp_port = enr.udp().unwrap_or(3610);
49+
let swarm = &mut p2p.swarm;
7850

79-
if enr.tcp().is_none() && enr.udp().is_none() {
80-
eprintln!("ENR missing both TCP and UDP ports");
51+
let enr = Record::new(key.clone(), vec![])?;
52+
53+
if let Some(relay_url) = &args.relay_url {
54+
swarm.dial(relay_url.clone())?;
55+
println!("Dialed relay");
56+
let mut learned_observed_addr = false;
57+
let mut told_relay_observed_addr = false;
58+
59+
loop {
60+
match swarm
61+
.next()
62+
.await
63+
.ok_or(anyhow::anyhow!("Swarm event is None"))?
64+
{
65+
SwarmEvent::NewListenAddr { .. } => {}
66+
SwarmEvent::Dialing { .. } => {}
67+
SwarmEvent::ConnectionEstablished { .. } => {}
68+
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(
69+
PlutoBehaviourEvent::Ping(_),
70+
)) => {}
71+
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(
72+
PlutoBehaviourEvent::Identify(identify::Event::Sent { .. }),
73+
)) => {
74+
println!("Told relay its public address");
75+
told_relay_observed_addr = true;
8176
}
82-
83-
swarm.add_peer_address(peer_id, format!("/ip4/{}/udp/{}", ip, udp_port).parse().unwrap());
84-
swarm.add_peer_address(peer_id, format!("/ip4/{}/tcp/{}", ip, tcp_port).parse().unwrap());
77+
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(
78+
PlutoBehaviourEvent::Identify(identify::Event::Received {
79+
info: identify::Info { observed_addr, .. },
80+
..
81+
}),
82+
)) => {
83+
println!("Relay told us our observed address: {}", observed_addr);
84+
learned_observed_addr = true;
85+
}
86+
event => panic!("{event:?}"),
8587
}
86-
Err(e) => {
87-
eprintln!("Failed to parse ENR: {} (error: {})", enr_str, e);
88+
if learned_observed_addr && told_relay_observed_addr {
89+
break;
8890
}
8991
}
9092
}
9193

94+
println!("ENR: {}", enr);
95+
96+
swarm.listen_on(format!("/ip4/0.0.0.0/udp/{}/quic-v1", args.port).parse()?)?;
97+
swarm.listen_on(format!("/ip4/0.0.0.0/tcp/{}", args.port).parse()?)?;
98+
if let Some(relay_url) = args.relay_url {
99+
swarm.listen_on(relay_url.with(Protocol::P2pCircuit))?;
100+
}
101+
92102
loop {
93103
tokio::select! {
94104
event = swarm.select_next_some() => match event {
95-
SwarmEvent::Behaviour(PlutoBehaviorEvent::Relay(event)) => {
96-
println!("Got relay event: {:?}", event);
97-
},
98-
SwarmEvent::Behaviour(PlutoBehaviorEvent::Identify(identify::Event::Received {
99-
info: identify::Info { observed_addr, ..}, ..
100-
})) => {
105+
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(PlutoBehaviourEvent::Identify(identify::Event::Received { info: identify::Info { observed_addr, .. }, .. }))) => {
106+
swarm.add_external_address(observed_addr.clone());
101107
println!("Address observed {}", observed_addr);
102108
}
103-
SwarmEvent::Behaviour(PlutoBehaviorEvent::Mdns(libp2p::mdns::Event::Discovered(nodes))) => {
109+
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(PlutoBehaviourEvent::Relay(event))) => {
110+
println!("Got relay event: {:?}", event);
111+
},
112+
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Mdns(libp2p::mdns::Event::Discovered(nodes))) => {
104113
for node in nodes {
105114
println!("Discovered node: {:?}", node);
106-
swarm.dial(node.1).unwrap();
115+
swarm.dial(node.1)?;
107116
}
108117
}
109118
SwarmEvent::NewListenAddr { address, .. } => {
110119
println!("Local node is listening on {address}");
111120
}
112-
SwarmEvent::Behaviour(PlutoBehaviorEvent::Ping(ping_event)) => {
121+
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(PlutoBehaviourEvent::Ping(ping_event))) => {
113122
println!("Got ping event: {:?}", ping_event);
114123
}
115124
SwarmEvent::IncomingConnection { connection_id, local_addr, send_back_addr } => {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! Behaviours.
2+
#![allow(missing_docs)] // we need to allow missing docs for the derive macro
3+
4+
/// Pluto behaviour.
5+
pub mod pluto;
6+
7+
#[cfg(feature = "mdns")]
8+
/// Pluto Mdns behaviour.
9+
pub mod pluto_mdns;

0 commit comments

Comments
 (0)