-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add base p2p node with relay client #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
913b56b
51a691f
e3ece2e
85a9b9c
65d5ae7
a312796
1cc2a29
c432d86
aeaa971
65d3a49
a82bbc7
a2fe791
30a550b
df9fdce
ddbb57b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| 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:?}"), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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(()) | ||
| } | ||
| 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 | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: In general we can write is as follows:
Suggested change
|
||||||
|
|
||||||
| /// Pluto behaviour. | ||||||
| pub mod pluto; | ||||||
|
|
||||||
| #[cfg(feature = "mdns")] | ||||||
| /// Pluto Mdns behaviour. | ||||||
| pub mod pluto_mdns; | ||||||
|
emlautarom1 marked this conversation as resolved.
|
||||||
| 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(), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)), | ||
| ), | ||
| } | ||
| } | ||
| } | ||
| 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"), | ||
| } | ||
| } | ||
| } |
| 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; |
There was a problem hiding this comment.
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.