diff --git a/src/main.rs b/src/main.rs index 71b1440..92e9e18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod persistence; mod datastore; use crate::grpc::challenge::NodeUpdate; use tokio::sync::broadcast::Sender; diff --git a/src/persistence.rs b/src/persistence.rs new file mode 100644 index 0000000..870ba39 --- /dev/null +++ b/src/persistence.rs @@ -0,0 +1,108 @@ +#![allow(dead_code)] +use ed25519_dalek::SigningKey; +use ed25519_dalek::KEYPAIR_LENGTH; +use std::net::AddrParseError; +use std::net::Ipv4Addr; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use tokio::fs::File; +use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt, SeekFrom}; +use tokio::sync::Mutex; + +const DATA_SIZE: usize = 78; + +enum Error { + CorruptedIP, +} + +impl From for Error { + fn from(_: AddrParseError) -> Self { + Error::CorruptedIP + } +} + +struct Node { + ip: Ipv4Addr, + keypair: SigningKey, + joined_at: SystemTime, +} + +impl TryFrom<(&str, SigningKey, SystemTime)> for Node { + type Error = Error; + fn try_from(value: (&str, SigningKey, SystemTime)) -> Result { + Ok(Self { + ip: value.0.parse()?, + keypair: value.1, + joined_at: value.2, + }) + } +} + +impl Node { + fn ip_as_string(&self) -> String { + self.ip.to_string() + } + + fn signing_key(&self) -> SigningKey { + self.keypair.clone() + } + + fn to_bytes(self) -> [u8; DATA_SIZE] { + let mut result = [0; DATA_SIZE]; + result[0..4].copy_from_slice(&self.ip.octets()); + result[4..68].copy_from_slice(&self.keypair.to_keypair_bytes()); + result[69..DATA_SIZE].copy_from_slice( + &self + .joined_at + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + .to_le_bytes(), + ); + result + } + + fn from_bytes(bytes: [u8; DATA_SIZE]) -> Self { + let ip: [u8; 4] = bytes[0..4].try_into().unwrap(); + let ip: Ipv4Addr = ip.into(); + let keypair: [u8; KEYPAIR_LENGTH] = bytes[4..68].try_into().unwrap(); + let keypair: SigningKey = SigningKey::from_keypair_bytes(&keypair).unwrap(); + let joined_at: [u8; 8] = bytes[69..DATA_SIZE].try_into().unwrap(); + let joined_at: u64 = u64::from_le_bytes(joined_at); + let joined_at = SystemTime::UNIX_EPOCH + Duration::from_secs(joined_at); + Self { + ip, + keypair, + joined_at, + } + } +} + +struct FileManager { + file: Mutex, +} + +impl FileManager { + async fn init(path: &str) -> std::io::Result { + let file = File::open(path).await?; + Ok(Self { + file: Mutex::new(file), + }) + } + + async fn append_node(&self, node: Node) -> std::io::Result<()> { + let mut file = self.file.lock().await; + file.seek(SeekFrom::End(0)).await?; + file.write_all(&node.to_bytes()).await?; + file.flush().await?; + Ok(()) + } + + async fn get_node_by_id(&self, id: u64) -> std::io::Result { + let mut file = self.file.lock().await; + file.seek(SeekFrom::Start(id.wrapping_mul(DATA_SIZE.try_into().unwrap_or(0)))) + .await?; + let mut node_bytes = [0; DATA_SIZE]; + file.read_exact(&mut node_bytes).await?; + Ok(Node::from_bytes(node_bytes)) + } +}