use clap::Parser;
use hex::FromHex;
use libp2p::futures::StreamExt;
use libp2p::swarm::SwarmEvent;
use libp2p::SwarmBuilder;
use libp2p::{identity::Keypair, Multiaddr, PeerId};
use std::error::Error;
use strum::EnumString;
#[derive(clap::Parser)]
#[command(version)]
pub struct Config {
#[clap(long, default_value = "/p2ping/0.0.0")]
protocol_version: String,
#[clap(long, default_value = "p2ping/dev")]
agent_version: String,
#[clap(long, short = 'k')]
node_key: Option<String>,
#[clap(long, short = 't', default_value = "ed25519")]
key_type: KeyType,
#[clap(long, short = 'l')]
listen_addr: Vec<String>,
peer_addr: Option<String>,
}
#[derive(Debug, Default, Clone, EnumString)]
pub enum KeyType {
#[strum(serialize = "ed25519")]
#[default]
Ed25519,
#[strum(serialize = "secp256k1")]
Secp256k1,
#[strum(serialize = "ecdsa")]
Ecdsa,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let config = Config::parse();
if config.peer_addr.is_none() && config.listen_addr.is_empty() {
println!("[WARN]: no listen-addr or peer-addr specified, please refer to `p2ping --help` for help")
}
let local_key_pair = match config.node_key {
Some(key) => match config.key_type {
KeyType::Ed25519 | KeyType::Secp256k1 | KeyType::Ecdsa => {
let key_bytes = <[u8; 32]>::from_hex(key.trim_start_matches("0x"))?;
Keypair::ed25519_from_bytes(key_bytes)?
}
},
None => match config.key_type {
KeyType::Ed25519 => Keypair::generate_ed25519(),
KeyType::Secp256k1 => Keypair::generate_secp256k1(),
KeyType::Ecdsa => Keypair::generate_ecdsa(),
},
};
let local_peer_id = PeerId::from(local_key_pair.public());
println!("Local peer id: {local_peer_id:?}");
let behaviour = p2ping::Behaviour::new(
&config.protocol_version,
&config.agent_version,
local_key_pair.public(),
);
let mut swarm = SwarmBuilder::with_existing_identity(local_key_pair)
.with_tokio()
.with_tcp(
Default::default(),
(libp2p_tls::Config::new, libp2p_noise::Config::new),
libp2p_yamux::Config::default,
)?
.with_quic()
.with_dns()?
.with_websocket(
(libp2p_tls::Config::new, libp2p_noise::Config::new),
libp2p_yamux::Config::default,
)
.await?
.with_relay_client(
(libp2p_tls::Config::new, libp2p_noise::Config::new),
libp2p_yamux::Config::default,
)?
.with_behaviour(|_key, _relay| behaviour)?
.with_swarm_config(|cfg| {
cfg.with_idle_connection_timeout(std::time::Duration::from_secs(60))
})
.build();
for addr in &config.listen_addr {
swarm.listen_on(addr.parse()?)?;
}
if let Some(peer_addr) = config.peer_addr {
let remote: Multiaddr = peer_addr.parse()?;
swarm.dial(remote)?;
println!("Dialed {peer_addr}")
}
loop {
match swarm.select_next_some().await {
SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"),
SwarmEvent::Behaviour(event) => println!("{event:?}"),
x => println!("Unhandled: {x:?}"),
}
}
}