use futures::StreamExt;
use libp2p::{
core::upgrade,
floodsub::{self, Floodsub, FloodsubEvent},
identity, mdns, mplex, noise,
swarm::{NetworkBehaviour, SwarmEvent},
tcp, Multiaddr, PeerId, Transport,
};
use std::error::Error;
use tokio::io::{self, AsyncBufReadExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();
let id_keys = identity::Keypair::generate_ed25519();
let peer_id = PeerId::from(id_keys.public());
println!("Local peer id: {peer_id:?}");
let transport = tcp::tokio::Transport::new(tcp::Config::default().nodelay(true))
.upgrade(upgrade::Version::V1)
.authenticate(
noise::NoiseAuthenticated::xx(&id_keys)
.expect("Signing libp2p-noise static DH keypair failed."),
)
.multiplex(mplex::MplexConfig::new())
.boxed();
let floodsub_topic = floodsub::Topic::new("chat");
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "MyBehaviourEvent")]
struct MyBehaviour {
floodsub: Floodsub,
mdns: mdns::tokio::Behaviour,
}
#[allow(clippy::large_enum_variant)]
enum MyBehaviourEvent {
Floodsub(FloodsubEvent),
Mdns(mdns::Event),
}
impl From<FloodsubEvent> for MyBehaviourEvent {
fn from(event: FloodsubEvent) -> Self {
MyBehaviourEvent::Floodsub(event)
}
}
impl From<mdns::Event> for MyBehaviourEvent {
fn from(event: mdns::Event) -> Self {
MyBehaviourEvent::Mdns(event)
}
}
let mdns_behaviour = mdns::Behaviour::new(Default::default())?;
let behaviour = MyBehaviour {
floodsub: Floodsub::new(peer_id),
mdns: mdns_behaviour,
};
let mut swarm = libp2p_swarm::Swarm::with_tokio_executor(transport, behaviour, peer_id);
if let Some(to_dial) = std::env::args().nth(1) {
let addr: Multiaddr = to_dial.parse()?;
swarm.dial(addr)?;
println!("Dialed {to_dial:?}");
}
let mut stdin = io::BufReader::new(io::stdin()).lines();
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
loop {
tokio::select! {
line = stdin.next_line() => {
let line = line?.expect("stdin closed");
swarm.behaviour_mut().floodsub.publish(floodsub_topic.clone(), line.as_bytes());
}
event = swarm.select_next_some() => {
match event {
SwarmEvent::NewListenAddr { address, .. } => {
println!("Listening on {address:?}");
}
SwarmEvent::Behaviour(MyBehaviourEvent::Floodsub(FloodsubEvent::Message(message))) => {
println!(
"Received: '{:?}' from {:?}",
String::from_utf8_lossy(&message.data),
message.source
);
}
SwarmEvent::Behaviour(MyBehaviourEvent::Mdns(event)) => {
match event {
mdns::Event::Discovered(list) => {
for (peer, _) in list {
swarm.behaviour_mut().floodsub.add_node_to_partial_view(peer);
}
}
mdns::Event::Expired(list) => {
for (peer, _) in list {
if !swarm.behaviour().mdns.has_node(&peer) {
swarm.behaviour_mut().floodsub.remove_node_from_partial_view(&peer);
}
}
}
}
}
_ => {}
}
}
}
}
}