use std::{
fmt::{self, Debug, Formatter},
net::SocketAddrV4,
sync::Arc,
time::{Duration, Instant},
};
use crate::common::Id;
pub const STALE_TIME: Duration = Duration::from_secs(15 * 60);
const MIN_PING_BACKOFF_INTERVAL: Duration = Duration::from_secs(10);
pub const TOKEN_ROTATE_INTERVAL: Duration = Duration::from_secs(60 * 5);
#[derive(PartialEq)]
pub(crate) struct NodeInner {
pub(crate) id: Id,
pub(crate) address: SocketAddrV4,
pub(crate) token: Option<Box<[u8]>>,
pub(crate) last_seen: Instant,
}
impl NodeInner {
pub fn random() -> Self {
Self {
id: Id::random(),
address: SocketAddrV4::new(0.into(), 0),
token: None,
last_seen: Instant::now(),
}
}
}
#[derive(Clone, PartialEq)]
pub struct Node(pub(crate) Arc<NodeInner>);
impl Debug for Node {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
fmt.debug_struct("Node")
.field("id", &self.0.id)
.field("address", &self.0.address)
.field("last_seen", &self.0.last_seen.elapsed().as_secs())
.finish()
}
}
impl Node {
pub fn new(id: Id, address: SocketAddrV4) -> Node {
Node(Arc::new(NodeInner {
id,
address,
token: None,
last_seen: Instant::now(),
}))
}
pub(crate) fn new_with_token(id: Id, address: SocketAddrV4, token: Box<[u8]>) -> Self {
Node(Arc::new(NodeInner {
id,
address,
token: Some(token),
last_seen: Instant::now(),
}))
}
pub fn random() -> Node {
Node(Arc::new(NodeInner::random()))
}
#[cfg(test)]
pub fn unique(i: usize) -> Node {
Node::new(Id::random(), SocketAddrV4::new((i as u32).into(), i as u16))
}
pub fn id(&self) -> &Id {
&self.0.id
}
pub fn address(&self) -> SocketAddrV4 {
self.0.address
}
pub fn token(&self) -> Option<Box<[u8]>> {
self.0.token.clone()
}
pub fn is_stale(&self) -> bool {
self.0.last_seen.elapsed() > STALE_TIME
}
pub fn valid_token(&self) -> bool {
self.0.last_seen.elapsed() <= TOKEN_ROTATE_INTERVAL
}
pub(crate) fn should_ping(&self) -> bool {
self.0.last_seen.elapsed() > MIN_PING_BACKOFF_INTERVAL
}
pub fn same_address(&self, other: &Self) -> bool {
self.0.address == other.0.address
}
pub fn same_ip(&self, other: &Self) -> bool {
self.0.address.ip() == other.0.address.ip()
}
pub fn is_secure(&self) -> bool {
self.0.id.is_valid_for_ip(*self.0.address.ip())
}
pub(crate) fn already_exists(&self, nodes: &[Self]) -> bool {
nodes.iter().any(|existing| {
self.same_ip(existing)
&& (!existing.is_secure()
|| self.id().first_21_bits() == existing.id().first_21_bits())
})
}
}