hopr_transport_network/
store.rs1use std::{collections::HashSet, sync::Arc};
2
3use hopr_api::{Multiaddr, PeerId};
4
5use crate::errors::{NetworkError, Result};
6
7#[cfg(all(feature = "prometheus", not(test)))]
8lazy_static::lazy_static! {
9 static ref METRIC_PEER_COUNT: hopr_metrics::SimpleGauge =
10 hopr_metrics::SimpleGauge::new("hopr_peer_count", "Number of all peers").unwrap();
11}
12
13#[derive(Clone, Debug)]
21pub struct NetworkPeerStore {
22 me: PeerId,
23 my_addresses: HashSet<Multiaddr>,
24 addresses: Arc<dashmap::DashMap<PeerId, HashSet<Multiaddr>>>,
25}
26
27impl NetworkPeerStore {
28 pub fn new(me: PeerId, my_addresses: HashSet<Multiaddr>) -> Self {
29 Self {
30 me,
31 my_addresses,
32 addresses: Arc::new(dashmap::DashMap::new()),
33 }
34 }
35
36 #[inline]
37 pub fn me(&self) -> &PeerId {
38 &self.me
39 }
40
41 #[inline]
43 #[tracing::instrument(level = "trace", skip(self), ret(Display))]
44 pub fn has(&self, peer: &PeerId) -> bool {
45 peer == &self.me || self.addresses.contains_key(peer)
46 }
47
48 #[tracing::instrument(level = "debug", skip(self), ret(level = "trace"), err)]
52 pub fn add(&self, peer: PeerId, addresses: HashSet<Multiaddr>) -> Result<()> {
53 if peer == self.me {
54 return Err(NetworkError::DisallowedOperationOnOwnPeerIdError);
55 }
56
57 if let Some(mut unit) = self.addresses.get_mut(&peer) {
58 unit.value_mut().extend(addresses.into_iter());
59 } else {
60 self.addresses.insert(peer, addresses);
61
62 #[cfg(all(feature = "prometheus", not(test)))]
63 METRIC_PEER_COUNT.increment(1.0);
64 }
65
66 Ok(())
67 }
68
69 #[tracing::instrument(level = "trace", skip(self))]
71 pub fn get(&self, peer: &PeerId) -> Option<HashSet<Multiaddr>> {
72 if peer == &self.me {
73 Some(self.my_addresses.clone())
74 } else {
75 self.addresses.get(peer).map(|addrs| addrs.value().clone())
76 }
77 }
78
79 #[tracing::instrument(level = "debug", skip(self))]
81 pub fn remove(&self, peer: &PeerId) -> Result<()> {
82 if peer == &self.me {
83 return Err(NetworkError::DisallowedOperationOnOwnPeerIdError);
84 }
85
86 if self.addresses.remove(peer).is_some() {
87 #[cfg(all(feature = "prometheus", not(test)))]
88 METRIC_PEER_COUNT.decrement(1.0);
89 }
90
91 Ok(())
92 }
93
94 #[inline]
96 pub fn iter_keys(&self) -> impl Iterator<Item = PeerId> + '_ {
97 self.addresses.iter().map(|entry| *entry.key())
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use std::collections::HashSet;
104
105 use anyhow::Context;
106 use hopr_api::PeerId;
107
108 use super::NetworkPeerStore;
109
110 #[test]
111 fn network_peer_store_should_recognize_self() {
112 let me = PeerId::random();
113 let store = NetworkPeerStore::new(me.clone(), HashSet::new());
114
115 assert!(store.has(&me));
116 }
117
118 #[test]
119 fn network_peer_store_should_add_and_recognize_peer() {
120 let store = NetworkPeerStore::new(PeerId::random(), HashSet::new());
121
122 let peer = PeerId::random();
123
124 assert!(!store.has(&peer));
125
126 assert!(store.add(peer, HashSet::new()).is_ok());
127
128 assert!(store.has(&peer));
129 }
130
131 #[test]
132 fn network_peer_store_own_peer_should_fail_on_adding() {
133 let me = PeerId::random();
134 let store = NetworkPeerStore::new(me.clone(), HashSet::new());
135
136 assert!(store.add(me, HashSet::new()).is_err());
137 }
138
139 #[test]
140 fn network_peer_store_adding_the_same_peer_should_extend_multiaddresses() -> anyhow::Result<()> {
141 let store = NetworkPeerStore::new(PeerId::random(), HashSet::new());
142
143 let peer = PeerId::random();
144 assert!(store.add(peer.clone(), HashSet::new()).is_ok());
145
146 assert_eq!(store.get(&peer).context("should contain a value")?, HashSet::new());
147
148 let multiaddresses = HashSet::from(["/ip4/127.0.0.1/tcp/12345".try_into()?]);
149 assert!(store.add(peer.clone(), multiaddresses.clone()).is_ok());
150
151 assert_eq!(store.get(&peer).context("should contain a value")?, multiaddresses);
152
153 Ok(())
154 }
155
156 #[test]
157 fn network_peer_store_should_return_own_multiaddresses() -> anyhow::Result<()> {
158 let me = PeerId::random();
159 let multiaddresses = HashSet::from(["/ip4/127.0.0.1/tcp/12345".try_into()?]);
160 let store = NetworkPeerStore::new(me.clone(), multiaddresses.clone());
161
162 assert_eq!(store.get(&me), Some(multiaddresses));
163
164 Ok(())
165 }
166
167 #[test]
168 fn network_peer_store_should_return_stored_multiaddress() -> anyhow::Result<()> {
169 let store = NetworkPeerStore::new(PeerId::random(), HashSet::new());
170
171 let peer = PeerId::random();
172 let multiaddresses = HashSet::from(["/ip4/127.0.0.1/tcp/12345".try_into()?]);
173 assert!(store.add(peer.clone(), multiaddresses.clone()).is_ok());
174
175 assert_eq!(store.get(&peer), Some(multiaddresses));
176
177 Ok(())
178 }
179
180 #[test]
181 fn network_peer_store_should_fail_on_removing_self() -> anyhow::Result<()> {
182 let me = PeerId::random();
183 let multiaddresses = HashSet::from(["/ip4/127.0.0.1/tcp/12345".try_into()?]);
184 let store = NetworkPeerStore::new(me.clone(), multiaddresses.clone());
185
186 assert!(store.remove(&me).is_err());
187
188 Ok(())
189 }
190
191 #[test]
192 fn network_peer_store_should_succeed_on_removing_a_known_peer() -> anyhow::Result<()> {
193 let store = NetworkPeerStore::new(PeerId::random(), HashSet::new());
194
195 let peer = PeerId::random();
196 let multiaddresses = HashSet::from(["/ip4/127.0.0.1/tcp/12345".try_into()?]);
197 assert!(store.add(peer.clone(), multiaddresses.clone()).is_ok());
198
199 store.remove(&peer)?;
200
201 Ok(())
202 }
203
204 #[test]
205 fn network_peer_store_should_succeed_on_removing_an_unknown_peer() -> anyhow::Result<()> {
206 let store = NetworkPeerStore::new(PeerId::random(), HashSet::new());
207
208 let peer = PeerId::random();
209
210 store.remove(&peer)?;
211
212 Ok(())
213 }
214
215 #[test]
216 fn network_peer_store_should_be_referencing_the_same_underlying_data() -> anyhow::Result<()> {
217 let store = NetworkPeerStore::new(PeerId::random(), HashSet::new());
218 let store2 = store.clone();
219
220 let peer = PeerId::random();
221
222 store2.add(peer, HashSet::new())?;
223
224 assert_eq!(store.get(&peer).context("should contain a value")?, HashSet::new());
225
226 Ok(())
227 }
228}