use std::{
fmt::{self, Debug, Formatter},
net::SocketAddrV4,
sync::Arc,
time::{Duration, Instant},
};
use crate::common::Id;
pub(crate) const STALE_TIME: Duration = Duration::from_mins(15);
const MIN_PING_BACKOFF_INTERVAL: Duration = Duration::from_secs(10);
pub(crate) const TOKEN_ROTATE_INTERVAL: Duration = Duration::from_mins(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(crate) 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 {
#[must_use]
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(),
}))
}
#[must_use]
pub fn random() -> Node {
Node(Arc::new(NodeInner::random()))
}
#[cfg(test)]
#[must_use]
pub fn unique(i: usize) -> Node {
Node::new(
Id::random(),
SocketAddrV4::new(
u32::try_from(i).unwrap_or_default().into(),
u16::try_from(i).unwrap_or_default(),
),
)
}
#[must_use]
pub fn id(&self) -> &Id {
&self.0.id
}
#[must_use]
pub fn address(&self) -> SocketAddrV4 {
self.0.address
}
#[must_use]
pub fn token(&self) -> Option<Box<[u8]>> {
self.0.token.clone()
}
#[must_use]
pub fn is_stale(&self) -> bool {
self.0.last_seen.elapsed() > STALE_TIME
}
#[must_use]
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
}
#[must_use]
pub fn same_address(&self, other: &Self) -> bool {
self.0.address == other.0.address
}
#[must_use]
pub fn same_ip(&self, other: &Self) -> bool {
self.0.address.ip() == other.0.address.ip()
}
#[must_use]
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())
})
}
}