1use std::{
4 collections::HashSet,
5 net::SocketAddr,
6 time::{Duration, Instant},
7};
8
9use dht_rpc::IdBytes;
10
11const MIN_CONNECTION_TIME: Duration = Duration::from_secs(15);
13
14#[derive(Debug, Clone, Default)]
16pub enum ConnectionState {
17 #[default]
19 Idle,
20 Queued,
22 Waiting,
24 Connecting,
26 Connected { since: Instant },
28}
29
30impl ConnectionState {
31 pub fn is_idle(&self) -> bool {
32 matches!(self, Self::Idle)
33 }
34
35 pub fn is_queued(&self) -> bool {
36 matches!(self, Self::Queued)
37 }
38
39 pub fn is_waiting(&self) -> bool {
40 matches!(self, Self::Waiting)
41 }
42
43 pub fn is_connecting(&self) -> bool {
44 matches!(self, Self::Connecting)
45 }
46
47 pub fn is_connected(&self) -> bool {
48 matches!(self, Self::Connected { .. })
49 }
50
51 pub fn is_busy(&self) -> bool {
53 matches!(self, Self::Queued | Self::Waiting | Self::Connecting)
54 }
55}
56
57#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
59pub enum PeerOrigin {
60 #[default]
62 Discovered,
63 Explicit,
65 Incoming,
67}
68
69impl PeerOrigin {
70 pub fn is_client(&self) -> bool {
72 matches!(self, Self::Discovered | Self::Explicit)
73 }
74
75 pub fn is_explicit(&self) -> bool {
77 matches!(self, Self::Explicit)
78 }
79}
80
81#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
83pub enum TrustLevel {
84 #[default]
86 Unknown,
87 Proven,
89 Banned,
91}
92
93impl TrustLevel {
94 pub fn is_proven(&self) -> bool {
95 matches!(self, Self::Proven)
96 }
97
98 pub fn is_banned(&self) -> bool {
99 matches!(self, Self::Banned)
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
105#[repr(u8)]
106pub enum Priority {
107 VeryLow = 0,
109 Low = 1,
111 #[default]
113 Normal = 2,
114 High = 3,
116 VeryHigh = 4,
118}
119
120#[derive(Debug)]
122pub struct PeerInfo {
123 pub public_key: IdBytes,
125
126 pub relay_addresses: Vec<SocketAddr>,
128
129 pub reconnecting: bool,
131
132 pub trust: TrustLevel,
134
135 pub attempts: u32,
137
138 pub priority: Priority,
140
141 pub state: ConnectionState,
143
144 pub origin: PeerOrigin,
146
147 pub topics: HashSet<IdBytes>,
149}
150
151impl PeerInfo {
152 pub fn new(public_key: IdBytes) -> Self {
154 Self {
155 public_key,
156 relay_addresses: Vec::new(),
157 reconnecting: true,
158 trust: TrustLevel::Unknown,
159 attempts: 0,
160 priority: Priority::Normal,
161 state: ConnectionState::Idle,
162 origin: PeerOrigin::Discovered,
163 topics: HashSet::new(),
164 }
165 }
166
167 pub fn connected(&mut self) {
169 self.state = ConnectionState::Connected {
170 since: Instant::now(),
171 };
172 self.trust = TrustLevel::Proven;
173 self.update_priority();
174 }
175
176 pub fn disconnected(&mut self) {
178 if let ConnectionState::Connected { since } = self.state {
180 if since.elapsed() < MIN_CONNECTION_TIME {
181 self.attempts = self.attempts.saturating_add(1);
182 } else {
183 self.attempts = 0;
185 }
186 } else {
187 self.attempts = self.attempts.saturating_add(1);
189 }
190 self.state = ConnectionState::Idle;
191 self.update_priority();
192 }
193
194 pub fn add_relay_addresses(&mut self, relay_addresses: Vec<SocketAddr>) -> &mut Self {
196 for addr in relay_addresses {
197 if !self.relay_addresses.contains(&addr) {
198 self.relay_addresses.push(addr);
199 }
200 }
201 self
202 }
203
204 pub fn add_topic(&mut self, topic: IdBytes) -> &mut Self {
206 self.topics.insert(topic);
207 self
208 }
209
210 pub fn remove_topic(&mut self, topic: &IdBytes) {
212 self.topics.remove(topic);
213 }
214
215 pub fn ban(&mut self) {
217 self.trust = TrustLevel::Banned;
218 self.priority = Priority::VeryLow;
219 }
220
221 pub fn update_priority(&mut self) -> bool {
223 if self.trust.is_banned() {
224 self.priority = Priority::VeryLow;
225 return false;
226 }
227
228 let old_priority = self.priority;
229 self.priority = self.calculate_priority();
230
231 self.priority != Priority::VeryLow
233 && (self.priority != old_priority || !self.state.is_queued())
234 }
235
236 fn calculate_priority(&self) -> Priority {
238 match (self.trust.is_proven(), self.attempts) {
239 (true, 0) => Priority::VeryHigh,
241 (true, 1) => Priority::VeryHigh,
242 (true, 2) => Priority::High,
243 (true, 3) => Priority::Normal,
244 (true, _) => Priority::Low,
245
246 (false, 0) => Priority::Normal,
248 (false, 1) => Priority::High, (false, 2) => Priority::Normal,
250 (false, 3) => Priority::Low,
251 (false, _) => Priority::VeryLow,
252 }
253 }
254
255 pub fn reset(&mut self) {
257 if !self.trust.is_proven() {
258 self.attempts = 0;
259 }
260 self.update_priority();
261 }
262
263 pub fn should_gc(&self) -> bool {
265 if self.state.is_busy() || self.origin.is_explicit() || !self.topics.is_empty() {
267 return false;
268 }
269
270 self.trust.is_banned() || self.attempts > 10
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn test_priority_ordering() {
281 assert!(Priority::VeryLow < Priority::Low);
282 assert!(Priority::Low < Priority::Normal);
283 assert!(Priority::Normal < Priority::High);
284 assert!(Priority::High < Priority::VeryHigh);
285 }
286
287 #[test]
288 fn test_new_peer_priority() {
289 let info = PeerInfo::new(IdBytes::random());
290 assert_eq!(info.priority, Priority::Normal);
291 assert_eq!(info.trust, TrustLevel::Unknown);
292 assert_eq!(info.attempts, 0);
293 }
294
295 #[test]
296 fn test_connected_sets_proven() {
297 let mut info = PeerInfo::new(IdBytes::random());
298 assert!(!info.trust.is_proven());
299
300 info.connected();
301 assert!(info.trust.is_proven());
302 assert_eq!(info.priority, Priority::VeryHigh);
303 }
304
305 #[test]
306 fn test_ban_sets_very_low() {
307 let mut info = PeerInfo::new(IdBytes::random());
308 info.ban();
309
310 assert!(info.trust.is_banned());
311 assert_eq!(info.priority, Priority::VeryLow);
312 }
313
314 #[test]
315 fn test_attempts_decrease_priority() {
316 let mut info = PeerInfo::new(IdBytes::random());
317
318 assert_eq!(info.calculate_priority(), Priority::Normal);
320
321 info.attempts = 1;
323 assert_eq!(info.calculate_priority(), Priority::High);
324
325 info.attempts = 2;
327 assert_eq!(info.calculate_priority(), Priority::Normal);
328
329 info.attempts = 3;
331 assert_eq!(info.calculate_priority(), Priority::Low);
332
333 info.attempts = 5;
335 assert_eq!(info.calculate_priority(), Priority::VeryLow);
336 }
337
338 #[test]
339 fn test_proven_peer_priority() {
340 let mut info = PeerInfo::new(IdBytes::random());
341 info.trust = TrustLevel::Proven;
342
343 assert_eq!(info.calculate_priority(), Priority::VeryHigh);
345
346 info.attempts = 3;
348 assert_eq!(info.calculate_priority(), Priority::Normal);
349
350 info.attempts = 5;
351 assert_eq!(info.calculate_priority(), Priority::Low);
352 }
353
354 #[test]
355 fn test_should_gc() {
356 let mut info = PeerInfo::new(IdBytes::random());
357
358 assert!(!info.should_gc());
360
361 info.ban();
363 assert!(info.should_gc());
364
365 info.trust = TrustLevel::Unknown;
367 info.topics.insert(IdBytes::random());
368 assert!(!info.should_gc());
369
370 info.topics.clear();
372 info.origin = PeerOrigin::Explicit;
373 assert!(!info.should_gc());
374
375 info.origin = PeerOrigin::Discovered;
377 info.state = ConnectionState::Queued;
378 assert!(!info.should_gc());
379 }
380}