Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 131 additions & 47 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ license = "Apache-2.0" # TODO(template) updat
publish = false

[workspace.dependencies]
anyhow = "1"
alloy = { version = "1.1.3", features = ["essentials"] }
axum = "0.8.6"
blst = "0.3.13"
cancellation = "0.1.0"
chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.5.53", features = ["derive"] }
crossbeam = "0.8.4"
hex = { version = "^0.4.3" }
prost = "0.14"
Expand Down
11 changes: 10 additions & 1 deletion crates/charon-p2p/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,20 @@ charon-k1util.workspace = true
vise.workspace = true
tokio.workspace = true
rand.workspace = true
tempfile.workspace = true
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can move this to a dev-dependency since it's only used in tests.


[dev-dependencies]
charon-testutil.workspace = true
tempfile.workspace = true
vise-exporter.workspace = true
anyhow.workspace = true
clap.workspace = true

[lints]
workspace = true

[features]
mdns = ["libp2p/mdns"]

[[example]]
name = "p2p"
required-features = ["mdns"]
150 changes: 150 additions & 0 deletions crates/charon-p2p/examples/p2p.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
//! P2P example
//!
//! This example creates a Pluto P2P node and connects to a relay.
//! Also, it discovers other Pluto nodes using mDNS (requires the `mdns`
//! feature).

use anyhow::Result;
use charon_eth2::enr::Record;
use charon_p2p::{
behaviours::{
pluto::PlutoBehaviourEvent,
pluto_mdns::{PlutoMdnsBehaviour, PlutoMdnsBehaviourEvent},
},
config::P2PConfig,
gater::ConnGater,
p2p::{Node, NodeType},
};
use clap::Parser;
use k256::elliptic_curve::rand_core::OsRng;
use libp2p::{Multiaddr, futures::StreamExt, identify, multiaddr::Protocol, swarm::SwarmEvent};
use tokio::signal;

/// Command line arguments
#[derive(Debug, Parser)]
pub struct Args {
/// The port to listen on
#[arg(short, long, default_value = "1050")]
pub port: u16,
/// The ENRs to listen on
#[arg(short, long)]
pub enrs: Vec<String>,
/// The relay URL to dial
#[arg(short, long)]
pub relay_url: Option<Multiaddr>,
}

#[tokio::main]
async fn main() -> Result<()> {
let key = k256::SecretKey::random(&mut OsRng);
let mut p2p: Node<_> = Node::new(
P2PConfig::default(),
key.clone(),
ConnGater,
false,
NodeType::QUIC,
PlutoMdnsBehaviour::new,
)?;

let args = Args::parse();

let swarm = &mut p2p.swarm;

let enr = Record::new(key.clone(), vec![])?;

if let Some(relay_url) = &args.relay_url {
swarm.dial(relay_url.clone())?;
println!("Dialed relay");
let mut learned_observed_addr = false;
let mut told_relay_observed_addr = false;

loop {
match swarm
.next()
.await
.ok_or(anyhow::anyhow!("Swarm event is None"))?
{
SwarmEvent::NewListenAddr { .. } => {}
SwarmEvent::Dialing { .. } => {}
SwarmEvent::ConnectionEstablished { .. } => {}
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(
PlutoBehaviourEvent::Ping(_),
)) => {}
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(
PlutoBehaviourEvent::Identify(identify::Event::Sent { .. }),
)) => {
println!("Told relay its public address");
told_relay_observed_addr = true;
}
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(
PlutoBehaviourEvent::Identify(identify::Event::Received {
info: identify::Info { observed_addr, .. },
..
}),
)) => {
println!("Relay told us our observed address: {}", observed_addr);
learned_observed_addr = true;
}
event => panic!("{event:?}"),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During testing, if you run this client, shut it down, and then start again with the same relay we can end up in a panic due to recieving an NewExternalAddrOfPeer message.

Not a blocker since it's testing code but it's worth keeping track of this.

}
if learned_observed_addr && told_relay_observed_addr {
break;
}
}
}

println!("ENR: {}", enr);

swarm.listen_on(format!("/ip4/0.0.0.0/udp/{}/quic-v1", args.port).parse()?)?;
swarm.listen_on(format!("/ip4/0.0.0.0/tcp/{}", args.port).parse()?)?;
if let Some(relay_url) = args.relay_url {
swarm.listen_on(relay_url.with(Protocol::P2pCircuit))?;
}

loop {
tokio::select! {
event = swarm.select_next_some() => match event {
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(PlutoBehaviourEvent::Identify(identify::Event::Received { info: identify::Info { observed_addr, .. }, .. }))) => {
swarm.add_external_address(observed_addr.clone());
println!("Address observed {}", observed_addr);
}
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(PlutoBehaviourEvent::Relay(event))) => {
println!("Got relay event: {:?}", event);
},
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Mdns(libp2p::mdns::Event::Discovered(nodes))) => {
for node in nodes {
println!("Discovered node: {:?}", node);
swarm.dial(node.1)?;
}
}
SwarmEvent::NewListenAddr { address, .. } => {
println!("Local node is listening on {address}");
}
SwarmEvent::Behaviour(PlutoMdnsBehaviourEvent::Pluto(PlutoBehaviourEvent::Ping(ping_event))) => {
println!("Got ping event: {:?}", ping_event);
}
SwarmEvent::IncomingConnection { connection_id, local_addr, send_back_addr } => {
println!("Incoming connection (id={connection_id}) from {:?} (send on {:?})", local_addr, send_back_addr);
}
SwarmEvent::IncomingConnectionError {peer_id,connection_id,error, local_addr, send_back_addr } => {
println!("Incoming connection (id={connection_id}) error from {:?} (send on {:?} to {:?}): {:?}", peer_id, local_addr, send_back_addr, error);
}
event => {
println!("{:?}", event);
}
},
_ = signal::ctrl_c() => {
println!("\nReceived Ctrl+C, shutting down gracefully...");

// Perform cleanup
let _ = swarm;
drop(p2p);

println!("Shutdown complete");
break;
}
}
}

Ok(())
}
9 changes: 9 additions & 0 deletions crates/charon-p2p/src/behaviours/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//! Behaviours.
#![allow(missing_docs)] // we need to allow missing docs for the derive macro
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: In general we can write is as follows:

Suggested change
#![allow(missing_docs)] // we need to allow missing docs for the derive macro
#![allow(missing_docs, reason = "required for the derive macro")]


/// Pluto behaviour.
pub mod pluto;

#[cfg(feature = "mdns")]
/// Pluto Mdns behaviour.
pub mod pluto_mdns;
Comment thread
emlautarom1 marked this conversation as resolved.
33 changes: 33 additions & 0 deletions crates/charon-p2p/src/behaviours/pluto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! Pluto behaviour.

use std::time::Duration;

use libp2p::{identify, identity::Keypair, ping, relay, swarm::NetworkBehaviour};

#[derive(NetworkBehaviour)]
pub struct PlutoBehaviour {
/// Relay client behaviour.
pub relay: relay::client::Behaviour,
/// Identify behaviour.
pub identify: identify::Behaviour,
/// Ping behaviour.
pub ping: ping::Behaviour,
}

impl PlutoBehaviour {
/// Creates a new Pluto behaviour.
pub fn new(key: &Keypair, relay_client: relay::client::Behaviour) -> Self {
Self {
relay: relay_client,
identify: identify::Behaviour::new(identify::Config::new(
"/pluto/1.0.0-alpha".into(),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original Go implementation uses "" as the protocol version (ends up being the default).

What actually gets configured is the UserAgent which is set to obolnetwork-charon/<current version>, ex. obolnetwork-charon/v1.7.0-rc. We should set the User agent to something like pluto/v1.0.0-alpha.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key.public(),
)),
ping: ping::Behaviour::new(
ping::Config::new()
.with_interval(Duration::from_secs(1))
.with_timeout(Duration::from_secs(2)),
),
}
}
}
24 changes: 24 additions & 0 deletions crates/charon-p2p/src/behaviours/pluto_mdns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Pluto Mdns behaviour.
use libp2p::{identity::Keypair, mdns, relay, swarm::NetworkBehaviour};

use crate::behaviours::pluto::PlutoBehaviour;

/// Pluto network behaviour.
#[derive(NetworkBehaviour)]
pub struct PlutoMdnsBehaviour {
/// Pluto behaviour.
pub pluto: PlutoBehaviour,
/// Mdns behaviour.
pub mdns: mdns::tokio::Behaviour,
}

impl PlutoMdnsBehaviour {
/// Creates a new Pluto Mdns behaviour.
pub fn new(key: &Keypair, relay_client: relay::client::Behaviour) -> Self {
Self {
pluto: PlutoBehaviour::new(key, relay_client),
mdns: mdns::tokio::Behaviour::new(mdns::Config::default(), key.public().to_peer_id())
.expect("Failed to create mDNS behaviour"),
}
}
}
2 changes: 1 addition & 1 deletion crates/charon-p2p/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type Result<T> = std::result::Result<T, P2PConfigError>;
#[derive(Debug, Clone, Default)]
pub struct P2PConfig {
/// Defines the libp2p relay multiaddrs or URLs.
pub relays: Vec<String>,
pub relays: Vec<Multiaddr>,

/// The external IP address of the node.
pub external_ip: String,
Expand Down
4 changes: 4 additions & 0 deletions crates/charon-p2p/src/gater.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! Gater is responsible for whitelisting / blacklisting peers

/// todo: Temporary empty
pub struct ConnGater;
9 changes: 9 additions & 0 deletions crates/charon-p2p/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,14 @@ pub mod config;
/// Metrics.
pub mod metrics;

/// P2P.
pub mod p2p;

/// Gater
pub mod gater;

/// Behaviours.
pub mod behaviours;

/// K1 utilities.
pub mod k1;
Loading