caretta-sync/lazy-supplements/src/config/node.rs

190 lines
5.6 KiB
Rust
Raw Normal View History

2025-06-02 12:02:04 +09:00
use std::{net::IpAddr, ops, path::{Path, PathBuf}};
2025-05-30 09:26:47 +09:00
2025-06-01 15:18:17 +09:00
use base64::{prelude::BASE64_STANDARD, Engine};
2025-06-01 16:29:35 +09:00
use clap::Args;
2025-06-02 12:02:04 +09:00
use libp2p::{identity::{self, DecodingError, Keypair}, noise, ping, tcp, yamux, Swarm};
2025-05-24 06:11:00 +09:00
use serde::{Deserialize, Serialize};
2025-06-01 16:29:35 +09:00
use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}};
2025-06-02 12:02:04 +09:00
use tracing_subscriber::EnvFilter;
2025-05-24 06:11:00 +09:00
2025-06-01 15:18:17 +09:00
2025-06-04 07:13:37 +09:00
use crate::{cli::ConfigArgs, error::Error, global::DEFAULT_DATABASE_FILE_PATH, p2p};
2025-06-01 15:18:17 +09:00
2025-06-01 16:29:35 +09:00
use super::{PartialConfig, DEFAULT_LISTEN_IPS, DEFAULT_PORT};
2025-06-01 15:18:17 +09:00
fn keypair_to_base64(keypair: &Keypair) -> Result<String, Error> {
let vec = keypair.to_protobuf_encoding()?;
let base64 = BASE64_STANDARD.encode(vec);
Ok(base64)
}
fn base64_to_keypair(base64: &str) -> Result<Keypair, Error> {
let vec = BASE64_STANDARD.decode(base64)?;
Ok(Keypair::from_protobuf_encoding(&vec)?)
}
2025-05-30 09:26:47 +09:00
2025-05-24 06:11:00 +09:00
#[derive(Debug, Deserialize, Serialize)]
pub struct NodeConfig {
2025-06-01 15:18:17 +09:00
#[serde(with = "keypair_parser")]
2025-06-02 12:02:04 +09:00
pub secret: Keypair,
pub database_path: PathBuf,
pub listen_ips: Vec<IpAddr>,
pub port: u16,
2025-05-30 09:26:47 +09:00
}
2025-06-02 12:02:04 +09:00
impl NodeConfig {
2025-06-04 07:13:37 +09:00
pub async fn try_into_swarm (self) -> Result<Swarm<p2p::Behaviour>, Error> {
2025-06-02 12:02:04 +09:00
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,
)?
2025-06-04 07:13:37 +09:00
.with_behaviour(|keypair| p2p::Behaviour::try_from(keypair).unwrap())?
2025-06-02 12:02:04 +09:00
.build();
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
Ok(swarm)
2025-05-30 09:26:47 +09:00
}
2025-06-01 15:18:17 +09:00
}
impl TryFrom<RawNodeConfig> for NodeConfig {
type Error = Error;
fn try_from(raw: RawNodeConfig) -> Result<NodeConfig, Self::Error> {
Ok(NodeConfig {
secret: base64_to_keypair(&raw.secret.ok_or(Error::MissingConfig("secret"))?)?,
database_path: raw.database_path.ok_or(Error::MissingConfig("database_path"))?,
listen_ips: raw.listen_ips.ok_or(Error::MissingConfig("listen_ips"))?,
port: raw.port.ok_or(Error::MissingConfig("port"))?
})
2025-05-30 09:26:47 +09:00
}
2025-05-24 06:11:00 +09:00
}
2025-06-01 15:18:17 +09:00
mod keypair_parser {
2025-05-24 06:11:00 +09:00
use libp2p::identity::Keypair;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(keypair: &Keypair, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
2025-06-01 15:18:17 +09:00
match super::keypair_to_base64(keypair) {
Ok(x) => serializer.serialize_str(&x),
Err(_) => Err(serde::ser::Error::custom("Decoding keypair error"))
}
2025-05-24 06:11:00 +09:00
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Keypair, D::Error>
where D: Deserializer<'de>
{
2025-06-01 15:18:17 +09:00
match super::base64_to_keypair(&String::deserialize(deserializer)?) {
Ok(x) => Ok(x),
Err(crate::error::Error::Base64Decode(_)) => Err(serde::de::Error::custom("Decoding base64 error")),
Err(_) => unreachable!()
}
2025-05-24 06:11:00 +09:00
}
}
2025-06-02 12:02:04 +09:00
#[derive(Args, Clone, Debug, Deserialize, Serialize)]
2025-06-01 15:18:17 +09:00
pub struct RawNodeConfig {
2025-06-01 16:29:35 +09:00
#[arg(skip)]
2025-06-02 12:02:04 +09:00
pub secret: Option<String>,
2025-06-01 16:29:35 +09:00
#[arg(long)]
2025-06-02 12:02:04 +09:00
pub database_path: Option<PathBuf>,
2025-06-01 16:29:35 +09:00
#[arg(long)]
2025-06-02 12:02:04 +09:00
pub listen_ips: Option<Vec<IpAddr>>,
2025-06-01 16:29:35 +09:00
#[arg(long)]
2025-06-02 12:02:04 +09:00
pub port: Option<u16>,
2025-06-01 15:18:17 +09:00
}
2025-06-01 16:29:35 +09:00
impl RawNodeConfig {
pub fn with_new_secret(mut self) -> Self {
self.secret = Some(keypair_to_base64(&Keypair::generate_ed25519()).unwrap());
self
}
pub fn new() -> Self {
RawNodeConfig {
secret: None,
database_path: None,
listen_ips: None,
port: None,
}
}
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: RawNodeConfig = 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() {
if let Some(x) = path.as_ref().parent() {
std::fs::create_dir_all(x)?;
};
let _ = File::create(&path).await?;
}
let mut file = File::create(&path).await?;
file.write_all(toml::to_string(self)?.as_bytes()).await?;
Ok(())
}
2025-06-02 12:02:04 +09:00
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
}
2025-06-01 16:29:35 +09:00
}
2025-06-01 15:18:17 +09:00
2025-06-02 12:02:04 +09:00
2025-05-24 06:11:00 +09:00
#[cfg(test)]
mod tests {
use libp2p::identity;
use super::*;
#[tokio::test]
2025-06-01 15:18:17 +09:00
async fn parse_keypair() {
2025-05-24 06:11:00 +09:00
let keypair = identity::Keypair::generate_ed25519();
2025-06-01 15:18:17 +09:00
let keypair2 = base64_to_keypair(&keypair_to_base64(&keypair).unwrap()).unwrap();
assert_eq!(keypair.public(), keypair2.public());
2025-05-24 06:11:00 +09:00
}
}