Skip to main content

mainline/common/
node.rs

1//! Struct and implementation of the Node entry in the Kademlia routing table
2use std::{
3    fmt::{self, Debug, Formatter},
4    net::SocketAddrV4,
5    sync::Arc,
6    time::{Duration, Instant},
7};
8
9use crate::common::Id;
10
11/// The age of a node's last_seen time before it is considered stale and removed from a full bucket
12/// on inserting a new node.
13pub const STALE_TIME: Duration = Duration::from_secs(15 * 60);
14const MIN_PING_BACKOFF_INTERVAL: Duration = Duration::from_secs(10);
15pub const TOKEN_ROTATE_INTERVAL: Duration = Duration::from_secs(60 * 5);
16
17#[derive(PartialEq)]
18pub(crate) struct NodeInner {
19    pub(crate) id: Id,
20    pub(crate) address: SocketAddrV4,
21    pub(crate) token: Option<Box<[u8]>>,
22    pub(crate) last_seen: Instant,
23}
24
25impl NodeInner {
26    pub fn random() -> Self {
27        Self {
28            id: Id::random(),
29            address: SocketAddrV4::new(0.into(), 0),
30            token: None,
31            last_seen: Instant::now(),
32        }
33    }
34}
35
36#[derive(Clone, PartialEq)]
37/// Node entry in Kademlia routing table
38pub struct Node(pub(crate) Arc<NodeInner>);
39
40impl Debug for Node {
41    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
42        fmt.debug_struct("Node")
43            .field("id", &self.0.id)
44            .field("address", &self.0.address)
45            .field("last_seen", &self.0.last_seen.elapsed().as_secs())
46            .finish()
47    }
48}
49
50impl Node {
51    /// Creates a new Node from an id and socket address.
52    pub fn new(id: Id, address: SocketAddrV4) -> Node {
53        Node(Arc::new(NodeInner {
54            id,
55            address,
56            token: None,
57            last_seen: Instant::now(),
58        }))
59    }
60
61    pub(crate) fn new_with_token(id: Id, address: SocketAddrV4, token: Box<[u8]>) -> Self {
62        Node(Arc::new(NodeInner {
63            id,
64            address,
65            token: Some(token),
66            last_seen: Instant::now(),
67        }))
68    }
69
70    /// Creates a node with random Id for testing purposes.
71    pub fn random() -> Node {
72        Node(Arc::new(NodeInner::random()))
73    }
74
75    /// Create a node that is unique per `i` as it has a random Id and sets IP and port to `i`
76    #[cfg(test)]
77    pub fn unique(i: usize) -> Node {
78        Node::new(Id::random(), SocketAddrV4::new((i as u32).into(), i as u16))
79    }
80
81    // === Getters ===
82
83    /// Returns the id of this node
84    pub fn id(&self) -> &Id {
85        &self.0.id
86    }
87
88    /// Returns the address of this node
89    pub fn address(&self) -> SocketAddrV4 {
90        self.0.address
91    }
92
93    /// Returns the token we received from this node if any.
94    pub fn token(&self) -> Option<Box<[u8]>> {
95        self.0.token.clone()
96    }
97
98    /// Node is last seen more than a threshold ago.
99    pub fn is_stale(&self) -> bool {
100        self.0.last_seen.elapsed() > STALE_TIME
101    }
102
103    /// Node's token was received 5 minutes ago or less
104    pub fn valid_token(&self) -> bool {
105        self.0.last_seen.elapsed() <= TOKEN_ROTATE_INTERVAL
106    }
107
108    pub(crate) fn should_ping(&self) -> bool {
109        self.0.last_seen.elapsed() > MIN_PING_BACKOFF_INTERVAL
110    }
111
112    /// Returns true if both nodes have the same ip and port
113    pub fn same_address(&self, other: &Self) -> bool {
114        self.0.address == other.0.address
115    }
116
117    /// Returns true if both nodes have the same ip
118    pub fn same_ip(&self, other: &Self) -> bool {
119        self.0.address.ip() == other.0.address.ip()
120    }
121
122    /// Node [Id] is valid for its IP address.
123    ///
124    /// Check [BEP_0042](https://www.bittorrent.org/beps/bep_0042.html).
125    pub fn is_secure(&self) -> bool {
126        self.0.id.is_valid_for_ip(*self.0.address.ip())
127    }
128
129    /// Returns true if Any of the existing nodes:
130    ///  - Have the same IP as this node, And:
131    ///    = The existing nodes is Not secure.
132    ///    = The existing nodes is secure And shares the same first 21 bits.
133    ///
134    /// Effectively, allows only One non-secure node or Eight secure nodes from the same IP, in the routing table or ClosestNodes.
135    pub(crate) fn already_exists(&self, nodes: &[Self]) -> bool {
136        nodes.iter().any(|existing| {
137            self.same_ip(existing)
138                && (!existing.is_secure()
139                    || self.id().first_21_bits() == existing.id().first_21_bits())
140        })
141    }
142}