subspace_networking/constructor/
temporary_bans.rs

1use backoff::ExponentialBackoff;
2use backoff::backoff::Backoff;
3use libp2p::PeerId;
4use schnellru::{ByLength, LruMap};
5use std::ops::Add;
6use std::time::Instant;
7
8/// Details about temporary ban, used to track lifecycle of retries
9#[derive(Debug)]
10struct TemporaryBan {
11    backoff: ExponentialBackoff,
12    next_release: Option<Instant>,
13}
14
15impl TemporaryBan {
16    /// Create new temporary ban
17    fn new(backoff: ExponentialBackoff) -> Self {
18        let mut instance = Self {
19            backoff,
20            next_release: None,
21        };
22        instance.backoff.reset();
23        instance.next_release = instance
24            .backoff
25            .next_backoff()
26            .map(|duration| Instant::now().add(duration));
27        instance
28    }
29
30    /// Whether ban is currently active and not expired
31    fn is_active(&self) -> bool {
32        if let Some(next_release) = self.next_release {
33            next_release > Instant::now()
34        } else {
35            true
36        }
37    }
38
39    /// Extend temporary ban if it expired already expired, do nothing otherwise
40    fn try_extend(&mut self) {
41        let now = Instant::now();
42
43        if let Some(next_release) = self.next_release {
44            if next_release > now {
45                // Old ban if still active, no need to extend it
46                return;
47            }
48        } else {
49            // Ban is permanent
50            return;
51        }
52
53        self.next_release = self
54            .backoff
55            .next_backoff()
56            .map(|duration| now.add(duration));
57    }
58}
59
60/// Collection of temporary bans that help to prevent reaching out to the same peer ID over and
61/// over again.
62#[derive(Debug)]
63pub(crate) struct TemporaryBans {
64    backoff: ExponentialBackoff,
65    list: LruMap<PeerId, TemporaryBan>,
66}
67
68impl TemporaryBans {
69    pub(super) fn new(capacity: u32, backoff: ExponentialBackoff) -> Self {
70        Self {
71            backoff,
72            list: LruMap::new(ByLength::new(capacity)),
73        }
74    }
75
76    /// Checks if peer is currently banned.
77    ///
78    /// `false` means peer either is not banned at all or previous temporary ban has expired and
79    /// new connection attempt is allowed to be made.
80    pub(crate) fn is_banned(&self, peer_id: &PeerId) -> bool {
81        self.list
82            .peek(peer_id)
83            .map(TemporaryBan::is_active)
84            .unwrap_or_default()
85    }
86
87    /// Create temporary ban for peer or extend existing ban
88    pub(crate) fn create_or_extend(&mut self, peer_id: &PeerId) {
89        if let Some(ban) = self.list.get(peer_id) {
90            ban.try_extend();
91        } else {
92            self.list
93                .insert(*peer_id, TemporaryBan::new(self.backoff.clone()));
94        }
95    }
96
97    /// Remove temporary ban for peer.
98    ///
99    /// Returns `true` if there was an entry for peer during call.
100    pub(crate) fn remove(&mut self, peer_id: &PeerId) -> bool {
101        self.list.remove(peer_id).is_some()
102    }
103}