bee_protocol_types/
peer.rs

1// Copyright 2020-2022 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! A module that provides a type describing peers.
5
6use std::{
7    sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicU8, Ordering},
8    time::{SystemTime, UNIX_EPOCH},
9};
10
11use bee_block::payload::milestone::MilestoneIndex;
12use bee_gossip::{Multiaddr, PeerId, PeerInfo, PeerRelation};
13
14use crate::metrics::PeerMetrics;
15
16const SYNCED_THRESHOLD: u32 = 2;
17
18/// A type holding information related to a peer.
19pub struct Peer {
20    id: PeerId,
21    info: PeerInfo,
22    connected: AtomicBool,
23    metrics: PeerMetrics,
24    solid_milestone_index: AtomicU32,
25    pruned_index: AtomicU32,
26    latest_milestone_index: AtomicU32,
27    connected_peers: AtomicU8,
28    synced_peers: AtomicU8,
29    heartbeat_sent_timestamp: AtomicU64,
30    heartbeat_received_timestamp: AtomicU64,
31}
32
33impl Peer {
34    /// Creates a new `Peer`.
35    pub fn new(id: PeerId, info: PeerInfo) -> Self {
36        Self {
37            id,
38            info,
39            connected: AtomicBool::new(false),
40            metrics: PeerMetrics::default(),
41            solid_milestone_index: AtomicU32::new(0),
42            pruned_index: AtomicU32::new(0),
43            latest_milestone_index: AtomicU32::new(0),
44            connected_peers: AtomicU8::new(0),
45            synced_peers: AtomicU8::new(0),
46            heartbeat_sent_timestamp: AtomicU64::new(0),
47            heartbeat_received_timestamp: AtomicU64::new(0),
48        }
49    }
50
51    /// Returns the identifier of the `Peer`.
52    pub fn id(&self) -> &PeerId {
53        &self.id
54    }
55
56    /// Returns the address of the `Peer`.
57    pub fn address(&self) -> &Multiaddr {
58        &self.info.address
59    }
60
61    /// Returns the alias of the `Peer`.
62    pub fn alias(&self) -> &String {
63        &self.info.alias
64    }
65
66    /// Returns the relationship kind of the `Peer`.
67    pub fn relation(&self) -> PeerRelation {
68        self.info.relation
69    }
70
71    /// Returns whether the `Peer` is connected or not.
72    pub fn set_connected(&self, connected: bool) {
73        self.connected.store(connected, Ordering::Relaxed);
74    }
75
76    /// Sets whether the `Peer` is connected or not.
77    pub fn is_connected(&self) -> bool {
78        self.connected.load(Ordering::Relaxed)
79    }
80
81    /// Returns the metrics of the `Peer`.
82    pub fn metrics(&self) -> &PeerMetrics {
83        &self.metrics
84    }
85
86    /// Sets the solid milestone index of the `Peer`.
87    pub fn set_solid_milestone_index(&self, index: MilestoneIndex) {
88        self.solid_milestone_index.store(*index, Ordering::Relaxed);
89    }
90
91    /// Returns the solid milestone index of the `Peer`.
92    pub fn solid_milestone_index(&self) -> MilestoneIndex {
93        self.solid_milestone_index.load(Ordering::Relaxed).into()
94    }
95
96    /// Sets the pruned index of the `Peer`.
97    pub fn set_pruned_index(&self, index: MilestoneIndex) {
98        self.pruned_index.store(*index, Ordering::Relaxed);
99    }
100
101    /// Returns the pruned index of the `Peer`.
102    pub fn pruned_index(&self) -> MilestoneIndex {
103        self.pruned_index.load(Ordering::Relaxed).into()
104    }
105
106    /// Sets the latest milestone index of the `Peer`.
107    pub fn set_latest_milestone_index(&self, index: MilestoneIndex) {
108        self.latest_milestone_index.store(*index, Ordering::Relaxed);
109    }
110
111    /// Returns the latest milestone index of the `Peer`.
112    pub fn latest_milestone_index(&self) -> MilestoneIndex {
113        self.latest_milestone_index.load(Ordering::Relaxed).into()
114    }
115
116    /// Sets the number of connected peers of the `Peer`.
117    pub fn set_connected_peers(&self, connected_peers: u8) {
118        self.connected_peers.store(connected_peers, Ordering::Relaxed);
119    }
120
121    /// Returns the number of connected peers of the `Peer`.
122    pub fn connected_peers(&self) -> u8 {
123        self.connected_peers.load(Ordering::Relaxed)
124    }
125
126    /// Sets the number of synced peers of the `Peer`.
127    pub fn set_synced_peers(&self, synced_peers: u8) {
128        self.synced_peers.store(synced_peers, Ordering::Relaxed);
129    }
130
131    /// Returns the number of synced peers of the `Peer`.
132    pub fn synced_peers(&self) -> u8 {
133        self.synced_peers.load(Ordering::Relaxed)
134    }
135
136    /// Sets the timestamp of the last heartbeat sent by the `Peer`.
137    pub fn set_heartbeat_sent_timestamp(&self) {
138        self.heartbeat_sent_timestamp.store(
139            SystemTime::now()
140                .duration_since(UNIX_EPOCH)
141                .expect("Clock may have gone backwards")
142                .as_millis() as u64,
143            Ordering::Relaxed,
144        );
145    }
146
147    /// Returns the timestamp of the last heartbeat sent by the `Peer`.
148    pub fn heartbeat_sent_timestamp(&self) -> u64 {
149        self.heartbeat_sent_timestamp.load(Ordering::Relaxed)
150    }
151
152    /// Sets the timestamp of the last heartbeat received by the `Peer`.
153    pub fn set_heartbeat_received_timestamp(&self) {
154        self.heartbeat_received_timestamp.store(
155            SystemTime::now()
156                .duration_since(UNIX_EPOCH)
157                .expect("Clock may have gone backwards")
158                .as_millis() as u64,
159            Ordering::Relaxed,
160        );
161    }
162
163    /// Returns the timestamp of the last heartbeat received by the `Peer`.
164    pub fn heartbeat_received_timestamp(&self) -> u64 {
165        self.heartbeat_received_timestamp.load(Ordering::Relaxed)
166    }
167
168    /// Returns whether the `Peer` is synced or not.
169    pub fn is_synced(&self) -> bool {
170        self.is_synced_threshold(SYNCED_THRESHOLD)
171    }
172
173    /// Returns whether the `Peer` is synced with a threshold or not.
174    pub fn is_synced_threshold(&self, threshold: u32) -> bool {
175        *self.solid_milestone_index() >= (*self.latest_milestone_index()).saturating_sub(threshold)
176    }
177
178    /// Returns whether the `Peer` has the data referenced by a given milestone index.
179    pub fn has_data(&self, index: MilestoneIndex) -> bool {
180        // +1 to allow for a little delay before a Heartbeat comes from a peer.
181        index > self.pruned_index() && index <= self.solid_milestone_index() + MilestoneIndex(1)
182    }
183
184    /// Returns whether the `Peer` may have the data referenced by a given milestone index.
185    pub fn maybe_has_data(&self, index: MilestoneIndex) -> bool {
186        // +1 to allow for a little delay before a Heartbeat comes from a peer.
187        index > self.pruned_index() && index <= self.latest_milestone_index() + MilestoneIndex(1)
188    }
189}