Skip to content

Commit

Permalink
Create Cfg trait and its default implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mikelsr committed May 27, 2024
1 parent bf7956e commit 8fa7440
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 35 deletions.
66 changes: 66 additions & 0 deletions src/cfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::env;

use libp2p::{identity, kad};

// Configuration
pub trait Cfg {
// ID keys uniqely identifying the node.
fn id_keys(&self) -> identity::Keypair;
// Name of the protocol used to identify the node through libpb Identify.
fn identify_protocol(&self) -> String;
// Server or Client. Defaults to server.
fn kad_mode(&self) -> kad::Mode;
// Multiaddress the node listens on.
fn listen_addr(&self) -> String;
// Peer ID of the node. Derived from the public key in id_keys().
fn peer_id(&self) -> identity::PeerId;
}

// Default node configuration.
pub struct DefaultCfg {
id_keys: identity::Keypair,
identify_protocol: String,
listen_addr: String,
}

impl DefaultCfg {
// Default node configuration.
pub fn new() -> Self {
Self {
id_keys: identity::Keypair::generate_ed25519(),
identify_protocol: "/ww/identify/0.0.1".to_owned(),
listen_addr: "/ip4/0.0.0.0/tcp/0".to_owned(),
}
}

// Check if the node is a Kademlia client from the command-line arguments.
fn is_kad_client(&self) -> bool {
let args: Vec<String> = env::args().collect();
return args.iter().any(|arg| arg == "--kad-client");
}
}

impl Cfg for DefaultCfg {
fn identify_protocol(&self) -> String {
self.identify_protocol.to_owned()
}

fn listen_addr(&self) -> String {
self.listen_addr.to_owned()
}

fn kad_mode(&self) -> kad::Mode {
if self.is_kad_client() {
return kad::Mode::Client;
}
kad::Mode::Server
}

fn id_keys(&self) -> identity::Keypair {
self.id_keys.clone()
}

fn peer_id(&self) -> identity::PeerId {
identity::PeerId::from(self.id_keys().public())
}
}
46 changes: 19 additions & 27 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use std::{env, error::Error, time::Duration};
use std::{error::Error, time::Duration};

use anyhow::Result;
use libp2p::{identify, identity, kad, mdns, noise, ping, swarm, tcp, yamux, PeerId};
use libp2p::{identify, kad, mdns, noise, ping, swarm, tcp, yamux};
use tracing_subscriber::EnvFilter;

use ww_net;

fn is_kad_client() -> bool {
let args: Vec<String> = env::args().collect();
return args.iter().any(|arg| arg == "--kad-client");
}
pub mod cfg;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Use the default configuration.
let config: &dyn cfg::Cfg = &cfg::DefaultCfg::new();

// Start configuring a `fmt` subscriber
let subscriber = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
Expand All @@ -26,20 +26,19 @@ async fn main() -> Result<(), Box<dyn Error>> {
tracing::subscriber::set_global_default(subscriber).unwrap();

// Create a MDNS network behaviour.
let id_keys = identity::Keypair::generate_ed25519();
let peer_id = PeerId::from(id_keys.public());
let mdns_behaviour = mdns::tokio::Behaviour::new(mdns::Config::default(), peer_id)?;
let mdns_behaviour = mdns::tokio::Behaviour::new(mdns::Config::default(), config.peer_id())?;

// Create Stream behaviour.
let ping_behaviour = ping::Behaviour::default();

// Create Kademlia behaviour.
// Create Kademlia and Identify behaviours.
let kad_cfg = kad::Config::default();
let store = kad::store::MemoryStore::new(id_keys.public().to_peer_id());
let kad_behaviour = kad::Behaviour::with_config(id_keys.public().to_peer_id(), store, kad_cfg);
let kad_store = kad::store::MemoryStore::new(config.id_keys().public().to_peer_id());
let kad_behaviour =
kad::Behaviour::with_config(config.id_keys().public().to_peer_id(), kad_store, kad_cfg);
let identify_behaviour = identify::Behaviour::new(identify::Config::new(
"/test/1.0.0.".to_owned(),
id_keys.public(),
config.identify_protocol(),
config.id_keys().public(),
));

// Combine behaviours.
Expand All @@ -50,7 +49,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
identify: identify_behaviour,
};

let raw_swarm = libp2p::SwarmBuilder::with_existing_identity(id_keys)
let raw_swarm = libp2p::SwarmBuilder::with_existing_identity(config.id_keys())
.with_tokio()
.with_tcp(
tcp::Config::default(),
Expand All @@ -61,19 +60,14 @@ async fn main() -> Result<(), Box<dyn Error>> {
.with_swarm_config(|cfg| cfg.with_idle_connection_timeout(Duration::from_secs(u64::MAX)))
.build();

// Wrap the swarm in our custom type to overwrite its behaviour and event management.
let mut swarm = ww_net::DefaultSwarm(raw_swarm);

// Configure Kademlia mode, defaults to server.
let kad_mode = if is_kad_client() {
kad::Mode::Client
} else {
kad::Mode::Server
};
swarm.behaviour_mut().kad.set_mode(Some(kad_mode));
// Set the Kademlia mode.
swarm.behaviour_mut().kad.set_mode(Some(config.kad_mode()));

// Tell the swarm to listen on all interfaces and a random, OS-assigned
// port.
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
// Tell the swarm to listen on all interfaces and a random, OS-assigned port.
swarm.listen_on(config.listen_addr().parse()?)?;

loop {
match swarm.select_next_some().await {
Expand All @@ -82,11 +76,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
// using our PeerID as the key.
tracing::info!("listening on {address:?}")
}

swarm::SwarmEvent::Behaviour(ww_net::DefaultBehaviourEvent::Mdns(event)) => {
ww_net::net::default_mdns_handler(&mut swarm, event);
}

swarm::SwarmEvent::Behaviour(ww_net::DefaultBehaviourEvent::Ping(event)) => {
tracing::info!("got PING event: {event:?}");
}
Expand Down
19 changes: 11 additions & 8 deletions ww_net/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ use libp2p::{swarm, Swarm};

pub struct DefaultSwarm(pub swarm::Swarm<DefaultBehaviour>);

impl DefaultSwarm {
// Forward tge call to the inner Swarm.
pub fn select_next_some(&mut self) -> SelectNextSome<'_, Swarm<DefaultBehaviour>> {
self.0.select_next_some()
}
}

// Required to use DefaultSwarm as Swarm in our modules.
impl Deref for DefaultSwarm {
type Target = swarm::Swarm<DefaultBehaviour>;

Expand All @@ -16,24 +24,20 @@ impl Deref for DefaultSwarm {
}
}

// Required to use DefaultSwarm as Swarm in our modules.
impl DerefMut for DefaultSwarm {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl net::Dialer for DefaultSwarm {
// Forward the call to the inner Swarm.
fn dial(&mut self, opts: swarm::dial_opts::DialOpts) -> Result<(), swarm::DialError> {
self.0.dial(opts)
}
}

impl DefaultSwarm {
pub fn select_next_some(&mut self) -> SelectNextSome<'_, Swarm<DefaultBehaviour>> {
self.0.select_next_some()
}
}

#[derive(swarm::NetworkBehaviour)]
#[behaviour(to_swarm = "DefaultBehaviourEvent")]
pub struct DefaultBehaviour {
Expand All @@ -43,11 +47,10 @@ pub struct DefaultBehaviour {
pub identify: libp2p::identify::Behaviour,
}

// Events explicitly managed or intercepted by the DefaultBehaviour.
#[derive(Debug)]
pub enum DefaultBehaviourEvent {
// Events emitted by the MDNS behaviour.
Mdns(libp2p::mdns::Event),
// Events emitted by the Ping behaviour.
Ping(libp2p::ping::Event),
Kad(libp2p::kad::Event),
Identify(libp2p::identify::Event),
Expand Down
2 changes: 2 additions & 0 deletions ww_net/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ use libp2p::{
Multiaddr, PeerId,
};

// Defines the hability to dial another peer.
pub trait Dialer {
fn dial(&mut self, opts: DialOpts) -> Result<(), swarm::DialError>;
}

// Dials each newly discovered peer.
pub fn default_mdns_handler(d: &mut dyn Dialer, event: mdns::Event) {
match event {
mdns::Event::Discovered(peers) => {
Expand Down

0 comments on commit 8fa7440

Please sign in to comment.