use std::{
ops::{Deref, Sub},
time::{Duration, SystemTime},
};
use iroh_bytes::HashAndFormat;
use iroh_net::NodeId;
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
pub const ALPN: &[u8] = b"n0/tracker/1";
pub const REQUEST_SIZE_LIMIT: usize = 1024 * 16;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum AnnounceKind {
Partial = 0,
Complete,
}
impl AnnounceKind {
pub fn from_complete(complete: bool) -> Self {
if complete {
Self::Complete
} else {
Self::Partial
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct AbsoluteTime(u64);
impl AbsoluteTime {
pub fn now() -> Self {
Self::try_from(SystemTime::now()).unwrap()
}
pub fn from_micros(micros: u64) -> Self {
Self(micros)
}
pub fn as_micros(&self) -> u64 {
self.0
}
}
impl Sub for AbsoluteTime {
type Output = Duration;
fn sub(self, rhs: Self) -> Self::Output {
Duration::from_micros(self.0 - rhs.0)
}
}
impl TryFrom<SystemTime> for AbsoluteTime {
type Error = anyhow::Error;
fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
Ok(Self(
value
.duration_since(std::time::UNIX_EPOCH)
.expect("Time went backwards")
.as_micros()
.try_into()
.expect("time too large"),
))
}
}
impl From<AbsoluteTime> for SystemTime {
fn from(value: AbsoluteTime) -> Self {
std::time::UNIX_EPOCH + Duration::from_micros(value.0)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Announce {
pub host: NodeId,
pub content: HashAndFormat,
pub kind: AnnounceKind,
pub timestamp: AbsoluteTime,
}
#[derive(derive_more::Debug, Clone, Copy, Serialize, Deserialize)]
pub struct SignedAnnounce {
pub announce: Announce,
#[serde(with = "BigArray")]
#[debug("{}", hex::encode(self.signature))]
pub signature: [u8; 64],
}
impl Deref for SignedAnnounce {
type Target = Announce;
fn deref(&self) -> &Self::Target {
&self.announce
}
}
impl SignedAnnounce {
pub fn new(announce: Announce, secret_key: &iroh_net::key::SecretKey) -> anyhow::Result<Self> {
let announce_bytes = postcard::to_allocvec(&announce)?;
let signature = secret_key.sign(&announce_bytes).to_bytes();
Ok(Self {
announce,
signature,
})
}
pub fn verify(&self) -> anyhow::Result<()> {
let announce_bytes = postcard::to_allocvec(&self.announce)?;
let signature = iroh_net::key::Signature::from_bytes(&self.signature);
self.announce.host.verify(&announce_bytes, &signature)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct QueryFlags {
pub complete: bool,
pub verified: bool,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct Query {
pub content: HashAndFormat,
pub flags: QueryFlags,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryResponse {
pub hosts: Vec<SignedAnnounce>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Request {
Announce(SignedAnnounce),
Query(Query),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Response {
QueryResponse(QueryResponse),
}
#[cfg(test)]
mod tests {}