Implement server and node ping command

This commit is contained in:
fluo10 2025-06-02 12:02:04 +09:00
parent 1a99cfd1a2
commit 886ad9c1a6
8 changed files with 140 additions and 87 deletions

View file

@ -0,0 +1,30 @@
use std::path::PathBuf;
use clap::Args;
use libp2p::identity;
use crate::{config::{NodeConfig, RawNodeConfig}, error::Error, global::{DEFAULT_CONFIG_FILE_PATH, DEFAULT_RAW_NODE_CONFIG}};
#[derive(Args, Clone, Debug)]
pub struct ConfigArgs {
#[arg(long)]
pub config: Option<PathBuf>,
#[command(flatten)]
pub node_config: RawNodeConfig,
}
impl ConfigArgs {
pub fn get_config_path_or_default(&self) -> PathBuf {
if let Some(x) = self.config.as_ref() {
x.clone()
} else {
DEFAULT_CONFIG_FILE_PATH.to_path_buf()
}
}
pub async fn try_into_raw_node_config(self) -> Result<RawNodeConfig, Error> {
Ok(RawNodeConfig::read_from(self.get_config_path_or_default()).await? + self.node_config)
}
pub async fn try_into_node_config(self) -> Result<NodeConfig, Error> {
Ok((DEFAULT_RAW_NODE_CONFIG.clone() + self.try_into_raw_node_config().await?).try_into()?)
}
}

View file

@ -1,7 +1,11 @@
use std::path::PathBuf;
mod config;
mod init;
mod node;
mod server;
pub use config::ConfigArgs;
pub use init::InitArgs;
pub use node::{ NodeArgs, NodeCommand, JoinNodeArgs };
pub use server::ServerArgs;

View file

@ -7,7 +7,9 @@ use libp2p::{
};
use tracing_subscriber::EnvFilter;
use crate::error::Error;
use crate::{cli::ServerArgs, error::Error};
use super::ConfigArgs;
#[derive(Args, Debug)]
pub struct NodeArgs {
@ -18,13 +20,13 @@ pub struct NodeArgs {
#[derive(Args, Debug)]
pub struct JoinNodeArgs {
#[arg(long)]
pub endpoint: IpAddr,
pub peer_ip: IpAddr,
#[arg(long)]
pub port: u16,
#[arg(long)]
pub peer_id: String,
#[arg(long)]
pub config: Option<PathBuf>,
pub peer_port: u16,
//#[arg(long)]
//pub peer_id: String,
#[command(flatten)]
pub config: ConfigArgs,
}
#[derive(Debug, Subcommand)]
@ -36,28 +38,14 @@ pub enum NodeCommand {
impl JoinNodeArgs {
pub async fn ping(self) -> Result<(), Error> {
let _ = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.try_init();
let mut swarm = libp2p::SwarmBuilder::with_new_identity()
.with_tokio()
.with_tcp(
tcp::Config::default(),
noise::Config::new,
yamux::Config::default,
)?
.with_behaviour(|_| ping::Behaviour::default())?
.build();
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
let mut swarm = self.config.try_into_node_config().await?.try_into_swarm().await?;
let mut remote: Multiaddr = Multiaddr::empty();
remote.push(match self.endpoint {
remote.push(match self.peer_ip {
IpAddr::V4(x) => Protocol::Ip4(x),
IpAddr::V6(x) => Protocol::Ip6(x),
});
remote.push(Protocol::Tcp(self.port));
remote.push(Protocol::Tcp(self.peer_port));
let addr = remote.to_string();
swarm.dial(remote)?;
println!("Dialed {addr}");

View file

@ -0,0 +1,26 @@
use clap::Args;
use futures::StreamExt;
use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm};
use tracing_subscriber::EnvFilter;
use crate::error::Error;
use super::ConfigArgs;
#[derive(Args, Debug)]
pub struct ServerArgs {
#[command(flatten)]
config: ConfigArgs,
}
impl ServerArgs {
pub async fn start_server(self) -> Result<(), Error>{
let mut swarm = self.config.try_into_node_config().await?.try_into_swarm().await?;
loop{
match swarm.select_next_some().await {
SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"),
SwarmEvent::Behaviour(event) => println!("{event:?}"),
_ => {}
}
}
}
}

View file

@ -14,43 +14,4 @@ pub struct PartialConfig {
node: Option<NodeConfig>,
}
impl PartialConfig {
pub fn new() -> Self {
PartialConfig {
node: Some(NodeConfig::default()),
}
}
pub async fn read_or_create<T>(path: T) -> Result<Self, Error>
where
T: AsRef<Path>
{
if !path.as_ref().exists() {
Self::new().write_to(&path).await?;
}
Self::read_from(&path).await
}
pub async fn read_from<T>(path:T) -> Result<Self, Error>
where
T: AsRef<Path>
{
let mut file = File::open(path.as_ref()).await?;
let mut content = String::new();
file.read_to_string(&mut content).await?;
let config: PartialConfig = toml::from_str(&content)?;
Ok(config)
}
pub async fn write_to<T>(&self, path:T) -> Result<(), Error>
where
T: AsRef<Path>
{
if !path.as_ref().exists() {
let _ = File::create(&path).await?;
}
let mut file = File::open(&path).await?;
file.write_all(toml::to_string(self)?.as_bytes()).await?;
Ok(())
}
}

View file

@ -1,13 +1,14 @@
use std::{net::IpAddr, path::{Path, PathBuf}};
use std::{net::IpAddr, ops, path::{Path, PathBuf}};
use base64::{prelude::BASE64_STANDARD, Engine};
use clap::Args;
use libp2p::identity::{self, DecodingError, Keypair};
use libp2p::{identity::{self, DecodingError, Keypair}, noise, ping, tcp, yamux, Swarm};
use serde::{Deserialize, Serialize};
use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}};
use tracing_subscriber::EnvFilter;
use crate::{error::Error, global::DEFAULT_DATABASE_FILE_PATH};
use crate::{cli::ConfigArgs, error::Error, global::DEFAULT_DATABASE_FILE_PATH};
use super::{PartialConfig, DEFAULT_LISTEN_IPS, DEFAULT_PORT};
@ -20,26 +21,33 @@ fn keypair_to_base64(keypair: &Keypair) -> Result<String, Error> {
fn base64_to_keypair(base64: &str) -> Result<Keypair, Error> {
let vec = BASE64_STANDARD.decode(base64)?;
Ok(Keypair::from_protobuf_encoding(&vec)?)
}
#[derive(Debug, Deserialize, Serialize)]
pub struct NodeConfig {
#[serde(with = "keypair_parser")]
secret: Keypair,
database_path: PathBuf,
listen_ips: Vec<IpAddr>,
port: u16,
pub secret: Keypair,
pub database_path: PathBuf,
pub listen_ips: Vec<IpAddr>,
pub port: u16,
}
impl Default for NodeConfig {
fn default() -> NodeConfig{
NodeConfig {
secret: identity::Keypair::generate_ed25519(),
database_path: DEFAULT_DATABASE_FILE_PATH.to_path_buf(),
listen_ips: DEFAULT_LISTEN_IPS.to_vec(),
port: DEFAULT_PORT,
}
impl NodeConfig {
pub async fn try_into_swarm (self) -> Result<Swarm<ping::Behaviour>, Error> {
let _ = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.try_init();
let mut swarm = libp2p::SwarmBuilder::with_existing_identity(self.secret)
.with_tokio()
.with_tcp(
tcp::Config::default(),
noise::Config::new,
yamux::Config::default,
)?
.with_behaviour(|_| ping::Behaviour::default())?
.build();
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
Ok(swarm)
}
}
@ -78,16 +86,16 @@ mod keypair_parser {
}
}
#[derive(Args, Debug, Deserialize, Serialize)]
#[derive(Args, Clone, Debug, Deserialize, Serialize)]
pub struct RawNodeConfig {
#[arg(skip)]
secret: Option<String>,
pub secret: Option<String>,
#[arg(long)]
database_path: Option<PathBuf>,
pub database_path: Option<PathBuf>,
#[arg(long)]
listen_ips: Option<Vec<IpAddr>>,
pub listen_ips: Option<Vec<IpAddr>>,
#[arg(long)]
port: Option<u16>,
pub port: Option<u16>,
}
impl RawNodeConfig {
@ -138,8 +146,33 @@ impl RawNodeConfig {
file.write_all(toml::to_string(self)?.as_bytes()).await?;
Ok(())
}
pub fn merge(&mut self, another: RawNodeConfig) {
if let Some(x) = another.secret {
self.secret = Some(x);
};
if let Some(x) = another.database_path {
self.database_path = Some(x);
};
if let Some(x) = another.listen_ips {
self.listen_ips = Some(x);
};
if let Some(x) = another.port {
self.port = Some(x);
};
}
}
impl ops::Add<RawNodeConfig> for RawNodeConfig {
type Output = RawNodeConfig;
fn add(mut self, another: RawNodeConfig) -> RawNodeConfig {
self.merge(another);
self
}
}
#[cfg(test)]
mod tests {
use libp2p::identity;

View file

@ -1,6 +1,6 @@
use std::{net::{IpAddr, Ipv4Addr}, path::PathBuf, sync::LazyLock};
use crate::config::NodeConfig;
use crate::config::{NodeConfig, RawNodeConfig};
use sea_orm::DatabaseConnection;
use tokio::sync::OnceCell;
@ -69,4 +69,13 @@ impl Global {
pub async fn get_or_try_init_node_config(&self, config: NodeConfig) -> &NodeConfig {
self.node_config.get_or_init(|| async {config}).await
}
}
}
pub static DEFAULT_RAW_NODE_CONFIG: LazyLock<RawNodeConfig> = LazyLock::new(|| {
RawNodeConfig {
secret: None,
database_path: Some(DEFAULT_DATABASE_FILE_PATH.to_path_buf()),
listen_ips: Some(DEFAULT_LISTEN_IPS.to_vec()),
port: Some(DEFAULT_PORT),
}
});

View file

@ -1,5 +1,5 @@
use clap::{Parser, Subcommand};
use lazy_supplements::{cli::{InitArgs, NodeArgs, NodeCommand}, *};
use lazy_supplements::{cli::{InitArgs, NodeArgs, NodeCommand, ServerArgs}, *};
#[derive(Debug, Parser)]
struct Cli {
@ -11,6 +11,7 @@ struct Cli {
enum Command {
Node(NodeArgs),
Init(InitArgs),
Server(ServerArgs),
}
@ -22,5 +23,6 @@ async fn main() {
NodeCommand::Join(y) => println!("{y:?}"),
},
Command::Init(x) => x.init_config().await,
Command::Server(x) => x.start_server().await.unwrap(),
}
}