use std::{io, time::Duration}; use clap::Parser; use futures::StreamExt; use libp2p::{ autonat::Behaviour as Autonat, core::{ either::EitherOutput, muxing::StreamMuxerBox, transport::{timeout::TransportTimeout, Boxed, OrTransport}, upgrade::{SelectUpgrade, Version}, }, dns::{DnsConfig, ResolverConfig}, identify::{self, Behaviour as Identify, Info}, identity::{self, Keypair}, mplex::MplexConfig, noise::{self, NoiseConfig}, ping::Behaviour as Ping, quic::async_std::Transport as AsyncQuicTransport, quic::Config as QuicConfig, relay::v2::relay::Relay, swarm::{NetworkBehaviour, SwarmBuilder, SwarmEvent}, tcp::{async_io::Transport as AsyncTcpTransport, Config as GenTcpConfig}, yamux::YamuxConfig, PeerId, Transport, }; #[derive(NetworkBehaviour)] pub struct Behaviour { relay: Relay, autonat: Autonat, identify: Identify, ping: Ping, } #[derive(Debug, Parser)] #[clap(name = "libp2p server")] struct Opts { /// Fixed value to generate deterministic peer id. #[clap(long)] secret_key_seed: Option, } #[async_std::main] async fn main() -> anyhow::Result<()> { env_logger::init(); let opts = Opts::parse(); let local_keypair = match opts.secret_key_seed { Some(seed) => generate_ed25519(seed), None => Keypair::generate_ed25519(), }; let local_peer_id = PeerId::from(local_keypair.public()); println!("Local Node: {local_peer_id}"); let transport = build_transport(local_keypair.clone())?; let behaviour = Behaviour { autonat: Autonat::new(local_peer_id, Default::default()), ping: Ping::new(Default::default()), identify: Identify::new({ let mut config = identify::Config::new("/autorelay/0.1.0".to_string(), local_keypair.public()); config.push_listen_addr_updates = true; config }), relay: Relay::new(local_peer_id, Default::default()), }; let mut swarm = SwarmBuilder::with_async_std_executor(transport, behaviour, local_peer_id) .dial_concurrency_factor(10_u8.try_into().expect("Always greater than 0")) .build(); for addr in [ "/ip4/0.0.0.0/tcp/0".parse().unwrap(), "/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap(), ] { swarm.listen_on(addr)?; } loop { futures::select! { event = swarm.select_next_some() => { match event { SwarmEvent::NewListenAddr { address, .. } => { println!("Listening on {address}"); } SwarmEvent::Behaviour(event) => match event { BehaviourEvent::Identify(identify::Event::Received { peer_id, info: Info { protocols, listen_addrs, .. }, }) => { if protocols .iter() .any(|p| p.as_bytes() == libp2p::autonat::DEFAULT_PROTOCOL_NAME) { for addr in listen_addrs.clone() { swarm .behaviour_mut() .autonat .add_server(peer_id, Some(addr)); } } } BehaviourEvent::Relay(event) => { println!("Relay Event: {event:?}"); } _ => {} }, _ => {} } } } } } pub fn build_transport(keypair: Keypair) -> io::Result> { let xx_keypair = noise::Keypair::::new() .into_authentic(&keypair) .unwrap(); let noise_config = NoiseConfig::xx(xx_keypair).into_authenticated(); let multiplex_upgrade = SelectUpgrade::new(YamuxConfig::default(), MplexConfig::new()); let quic_transport = AsyncQuicTransport::new(QuicConfig::new(&keypair)); let transport = AsyncTcpTransport::new(GenTcpConfig::default().nodelay(true).port_reuse(true)); let transport_timeout = TransportTimeout::new(transport, Duration::from_secs(30)); let transport = futures::executor::block_on(DnsConfig::custom( transport_timeout, ResolverConfig::cloudflare(), Default::default(), ))?; let transport = transport .upgrade(Version::V1) .authenticate(noise_config) .multiplex(multiplex_upgrade) .timeout(Duration::from_secs(30)) .map(|(peer_id, muxer), _| (peer_id, StreamMuxerBox::new(muxer))) .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err)) .boxed(); let transport = OrTransport::new(quic_transport, transport) .map(|either_output, _| match either_output { EitherOutput::First((peer_id, muxer)) => (peer_id, StreamMuxerBox::new(muxer)), EitherOutput::Second((peer_id, muxer)) => (peer_id, StreamMuxerBox::new(muxer)), }) .boxed(); Ok(transport) } fn generate_ed25519(secret_key_seed: u8) -> identity::Keypair { let mut bytes = [0u8; 32]; bytes[0] = secret_key_seed; let secret_key = identity::ed25519::SecretKey::from_bytes(&mut bytes) .expect("this returns `Err` only if the length is wrong; the length is correct; qed"); identity::Keypair::Ed25519(secret_key.into()) }