• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tari-project / tari / 20715266036

05 Jan 2026 12:22PM UTC coverage: 59.702% (-0.9%) from 60.642%
20715266036

push

github

web-flow
chore(deps): bump azure/trusted-signing-action from 0.5.10 to 0.5.11 (#7632)

Bumps
[azure/trusted-signing-action](https://github.com/azure/trusted-signing-action)
from 0.5.10 to 0.5.11.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/azure/trusted-signing-action/releases">azure/trusted-signing-action's
releases</a>.</em></p>
<blockquote>
<h2>v0.5.11</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: map to environment variables instead of directly from inputs by
<a href="https://github.com/Jaxelr"><code>@​Jaxelr</code></a> in <a
href="https://redirect.github.com/Azure/trusted-signing-action/pull/102">Azure/trusted-signing-action#102</a></li>
<li>chore: update codeowners to trusted signing team by <a
href="https://github.com/Jaxelr"><code>@​Jaxelr</code></a> in <a
href="https://redirect.github.com/Azure/trusted-signing-action/pull/103">Azure/trusted-signing-action#103</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/Azure/trusted-signing-action/compare/v0...v0.5.11">https://github.com/Azure/trusted-signing-action/compare/v0...v0.5.11</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Azure/trusted-signing-action/commit/1d365fec1"><code>1d365fe</code></a>
chore: update codeowners to trusted signing team (<a
href="https://redirect.github.com/azure/trusted-signing-action/issues/103">#103</a>)</li>
<li><a
href="https://github.com/Azure/trusted-signing-action/commit/34bc367eb"><code>34bc367</code></a>
fix: map to environment variables instead of directly from inputs (<a
href="https://redirect.github.com/azure/trusted-signing-action/issues/102">#102</a>)</li>
<li>See full diff in <a
href="https://github.com/azure/trusted-signing-action/compare/v0.5.10...v0.5.11">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubap... (continued)

69282 of 116047 relevant lines covered (59.7%)

300086.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.1
/comms/core/src/peer_manager/peer_storage_sql.rs
1
//  Copyright 2019 The Tari Project
2
//
3
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4
//  following conditions are met:
5
//
6
//  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7
//  disclaimer.
8
//
9
//  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10
//  following disclaimer in the documentation and/or other materials provided with the distribution.
11
//
12
//  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13
//  products derived from this software without specific prior written permission.
14
//
15
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16
//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
//  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19
//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20
//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21
//  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22

23
use std::{cmp::min, time::Duration};
24

25
use log::*;
26
use multiaddr::Multiaddr;
27

28
use crate::{
29
    net_address::PeerAddressSource,
30
    peer_manager::{
31
        database::{PeerDatabaseSql, ThisPeerIdentity},
32
        peer::Peer,
33
        peer_id::PeerId,
34
        NodeDistance,
35
        NodeId,
36
        PeerFeatures,
37
        PeerFlags,
38
        PeerManagerError,
39
    },
40
    types::{CommsDatabase, CommsPublicKey, TransportProtocol},
41
};
42

43
const LOG_TARGET: &str = "comms::peer_manager::peer_storage_sql";
44
// The maximum number of peers to return in peer manager
45
const PEER_MANAGER_SYNC_PEERS: usize = 100;
46
// The maximum amount of time a peer can be inactive before being considered stale:
47
// ((5 days, 24h, 60m, 60s)/2 = 2.5 days)
48
pub const STALE_PEER_THRESHOLD_DURATION: Duration = Duration::from_secs(5 * 24 * 60 * 60 / 2);
49

50
/// PeerStorageSql provides a mechanism to keep a datastore and a local copy of all peers in sync and allow fast
51
/// searches using the node_id, public key or net_address of a peer.
52
#[derive(Clone)]
53
pub struct PeerStorageSql {
54
    peer_db: PeerDatabaseSql,
55
}
56

57
impl PeerStorageSql {
58
    /// Constructs a new PeerStorageSql, with indexes populated from the given datastore
59
    pub fn new_indexed(database: PeerDatabaseSql) -> Result<PeerStorageSql, PeerManagerError> {
220✔
60
        trace!(
220✔
61
            target: LOG_TARGET,
×
62
            "Peer storage is initialized. {} total entries.",
×
63
            database.size(),
×
64
        );
65

66
        Ok(PeerStorageSql { peer_db: database })
220✔
67
    }
220✔
68

69
    /// Get this peer's identity
70
    pub fn this_peer_identity(&self) -> ThisPeerIdentity {
1,517✔
71
        self.peer_db.this_peer_identity()
1,517✔
72
    }
1,517✔
73

74
    /// Get the size of the database
75
    pub fn count(&self) -> usize {
46,426✔
76
        self.peer_db.size()
46,426✔
77
    }
46,426✔
78

79
    /// Adds or updates a peer and sets the last connection as successful.
80
    /// If the peer is marked as offline, it will be unmarked.
81
    pub fn add_or_update_peer(&self, peer: Peer) -> Result<PeerId, PeerManagerError> {
44,954✔
82
        Ok(self.peer_db.add_or_update_peer(peer)?)
44,954✔
83
    }
44,954✔
84

85
    /// Adds a peer an online peer if the peer does not already exist. When a peer already
86
    /// exists, the stored version will be replaced with the newly provided peer.
87
    pub fn add_or_update_online_peer(
1✔
88
        &self,
1✔
89
        pubkey: &CommsPublicKey,
1✔
90
        node_id: &NodeId,
1✔
91
        addresses: &[Multiaddr],
1✔
92
        peer_features: &PeerFeatures,
1✔
93
        source: &PeerAddressSource,
1✔
94
    ) -> Result<Peer, PeerManagerError> {
1✔
95
        Ok(self
1✔
96
            .peer_db
1✔
97
            .add_or_update_online_peer(pubkey, node_id, addresses, peer_features, source)?)
1✔
98
    }
1✔
99

100
    /// The peer with the specified node id will be soft deleted (marked as deleted)
101
    pub fn soft_delete_peer(&self, node_id: &NodeId) -> Result<(), PeerManagerError> {
1✔
102
        self.peer_db.soft_delete_peer(node_id)?;
1✔
103
        Ok(())
1✔
104
    }
1✔
105

106
    /// Find the peer with the provided NodeID
107
    pub fn get_peer_by_node_id(&self, node_id: &NodeId) -> Result<Option<Peer>, PeerManagerError> {
125,938✔
108
        Ok(self.peer_db.get_peer_by_node_id(node_id)?)
125,938✔
109
    }
125,938✔
110

111
    /// Get all peers based on a list of their node_ids
112
    pub fn get_peers_by_node_ids(&self, node_ids: &[NodeId]) -> Result<Vec<Peer>, PeerManagerError> {
1✔
113
        Ok(self.peer_db.get_peers_by_node_ids(node_ids)?)
1✔
114
    }
1✔
115

116
    /// Get all peers based on a list of their node_ids
117
    pub fn get_peer_public_keys_by_node_ids(
×
118
        &self,
×
119
        node_ids: &[NodeId],
×
120
    ) -> Result<Vec<CommsPublicKey>, PeerManagerError> {
×
121
        Ok(self.peer_db.get_peer_public_keys_by_node_ids(node_ids)?)
×
122
    }
×
123

124
    /// Get all banned peers
125
    pub fn get_banned_peers(&self) -> Result<Vec<Peer>, PeerManagerError> {
×
126
        Ok(self.peer_db.get_banned_peers()?)
×
127
    }
×
128

129
    pub fn find_all_starts_with(&self, partial: &[u8]) -> Result<Vec<Peer>, PeerManagerError> {
×
130
        Ok(self.peer_db.find_all_peers_match_partial_key(partial)?)
×
131
    }
×
132

133
    /// Find the peer with the provided PublicKey
134
    pub fn find_by_public_key(&self, public_key: &CommsPublicKey) -> Result<Option<Peer>, PeerManagerError> {
41,365✔
135
        Ok(self.peer_db.get_peer_by_public_key(public_key)?)
41,365✔
136
    }
41,365✔
137

138
    /// Check if a peer exist using the specified public_key
139
    pub fn exists_public_key(&self, public_key: &CommsPublicKey) -> Result<bool, PeerManagerError> {
17✔
140
        if let Ok(val) = self.peer_db.peer_exists_by_public_key(public_key) {
17✔
141
            Ok(val.is_some())
17✔
142
        } else {
143
            Ok(false)
×
144
        }
145
    }
17✔
146

147
    /// Check if a peer exist using the specified node_id
148
    pub fn exists_node_id(&self, node_id: &NodeId) -> Result<bool, PeerManagerError> {
×
149
        if let Ok(val) = self.peer_db.peer_exists_by_node_id(node_id) {
×
150
            Ok(val.is_some())
×
151
        } else {
152
            Ok(false)
×
153
        }
154
    }
×
155

156
    /// Return the peer by corresponding to the provided NodeId if it is not banned
157
    pub fn direct_identity_node_id(&self, node_id: &NodeId) -> Result<Peer, PeerManagerError> {
40,867✔
158
        let peer = self
40,867✔
159
            .get_peer_by_node_id(node_id)?
40,867✔
160
            .ok_or(PeerManagerError::peer_not_found(node_id))?;
40,867✔
161

162
        if peer.is_banned() {
40,866✔
163
            Err(PeerManagerError::BannedPeer)
×
164
        } else {
165
            Ok(peer)
40,866✔
166
        }
167
    }
40,867✔
168

169
    /// Return the peer by corresponding to the provided public key if it is not banned
170
    pub fn direct_identity_public_key(&self, public_key: &CommsPublicKey) -> Result<Peer, PeerManagerError> {
40,861✔
171
        let peer = self
40,861✔
172
            .find_by_public_key(public_key)?
40,861✔
173
            .ok_or(PeerManagerError::peer_not_found(&NodeId::from_public_key(public_key)))?;
40,861✔
174

175
        if peer.is_banned() {
40,861✔
176
            Err(PeerManagerError::BannedPeer)
×
177
        } else {
178
            Ok(peer)
40,861✔
179
        }
180
    }
40,861✔
181

182
    /// Return all peers, optionally filtering on supplied feature
183
    pub fn all(&self, features: Option<PeerFeatures>) -> Result<Vec<Peer>, PeerManagerError> {
1✔
184
        Ok(self.peer_db.get_all_peers(features)?)
1✔
185
    }
1✔
186

187
    /// Return "good" peers for syncing
188
    /// Criteria:
189
    ///  - Peer is not banned
190
    ///  - Peer has been seen within a defined time span (within the threshold)
191
    ///  - Only returns a maximum number of syncable peers (corresponds with the max possible number of requestable
192
    ///    peers to sync)
193
    ///  - Uses 0 as max PEER_MANAGER_SYNC_PEERS
194
    ///  - Peers has an address that is reachable - with supported transport protocols
195
    pub fn discovery_syncing(
8✔
196
        &self,
8✔
197
        mut n: usize,
8✔
198
        excluded_peers: &[NodeId],
8✔
199
        features: Option<PeerFeatures>,
8✔
200
        external_addresses_only: bool,
8✔
201
    ) -> Result<Vec<Peer>, PeerManagerError> {
8✔
202
        if n == 0 {
8✔
203
            n = PEER_MANAGER_SYNC_PEERS;
×
204
        } else {
8✔
205
            n = min(n, PEER_MANAGER_SYNC_PEERS);
8✔
206
        }
8✔
207

208
        Ok(self.peer_db.get_n_random_active_peers(
8✔
209
            n,
8✔
210
            excluded_peers,
8✔
211
            features,
8✔
212
            None,
8✔
213
            Some(STALE_PEER_THRESHOLD_DURATION),
8✔
214
            external_addresses_only,
8✔
215
            &[],
8✔
216
        )?)
×
217
    }
8✔
218

219
    /// Compile a list of all known peers
220
    pub fn get_not_banned_or_deleted_peers(&self) -> Result<Vec<Peer>, PeerManagerError> {
1✔
221
        Ok(self
1✔
222
            .peer_db
1✔
223
            .get_n_not_banned_or_deleted_peers(PEER_MANAGER_SYNC_PEERS)?)
1✔
224
    }
1✔
225

226
    /// Get available dial candidates that are communication nodes, not banned, not deleted, reachable,
227
    /// optionally not failed, optionally at random, and not in the excluded node IDs list
228
    pub fn get_available_dial_candidates(
546✔
229
        &self,
546✔
230
        exclude_node_ids: &[NodeId],
546✔
231
        limit: Option<usize>,
546✔
232
        transport_protocols: &[TransportProtocol],
546✔
233
        exclude_failed: bool,
546✔
234
        randomize: bool,
546✔
235
    ) -> Result<Vec<Peer>, PeerManagerError> {
546✔
236
        Ok(self.peer_db.get_available_dial_candidates(
546✔
237
            exclude_node_ids,
546✔
238
            limit,
546✔
239
            transport_protocols,
546✔
240
            exclude_failed,
546✔
241
            randomize,
546✔
242
        )?)
×
243
    }
546✔
244

245
    /// Compile a list of closest `n` active peers
246
    pub fn closest_n_active_peers(
2,590✔
247
        &self,
2,590✔
248
        region_node_id: &NodeId,
2,590✔
249
        n: usize,
2,590✔
250
        excluded_peers: &[NodeId],
2,590✔
251
        features: Option<PeerFeatures>,
2,590✔
252
        peer_flags: Option<PeerFlags>,
2,590✔
253
        stale_peer_threshold: Option<Duration>,
2,590✔
254
        exclude_if_all_address_failed: bool,
2,590✔
255
        exclusion_distance: Option<NodeDistance>,
2,590✔
256
        external_addresses_only: bool,
2,590✔
257
        transport_protocols: &[TransportProtocol],
2,590✔
258
    ) -> Result<Vec<Peer>, PeerManagerError> {
2,590✔
259
        Ok(self.peer_db.get_closest_n_active_peers(
2,590✔
260
            region_node_id,
2,590✔
261
            n,
2,590✔
262
            excluded_peers,
2,590✔
263
            features,
2,590✔
264
            peer_flags,
2,590✔
265
            stale_peer_threshold,
2,590✔
266
            exclude_if_all_address_failed,
2,590✔
267
            exclusion_distance,
2,590✔
268
            external_addresses_only,
2,590✔
269
            transport_protocols,
2,590✔
270
        )?)
×
271
    }
2,590✔
272

273
    /// Get all seed peers
274
    pub fn get_seed_peers(&self) -> Result<Vec<Peer>, PeerManagerError> {
213✔
275
        let seed_peers = self.peer_db.get_seed_peers()?;
213✔
276
        trace!(
213✔
277
            target: LOG_TARGET,
×
278
            "Get seed peers: {:?}",
×
279
            seed_peers.iter().map(|p| p.node_id.short_str()).collect::<Vec<_>>(),
×
280
        );
281
        Ok(seed_peers)
213✔
282
    }
213✔
283

284
    /// Compile a random list of communication node peers of size _n_ that are not banned or offline and
285
    /// external addresses support protocols defined in the `transport_protocols` vector.
286
    pub fn random_peers(
627✔
287
        &self,
627✔
288
        n: usize,
627✔
289
        exclude_peers: &[NodeId],
627✔
290
        flags: Option<PeerFlags>,
627✔
291
        transport_protocols: &[TransportProtocol],
627✔
292
    ) -> Result<Vec<Peer>, PeerManagerError> {
627✔
293
        Ok(self
627✔
294
            .peer_db
627✔
295
            .get_n_random_peers(n, exclude_peers, flags, transport_protocols)?)
627✔
296
    }
627✔
297

298
    /// Get the closest `n` not failed, banned or deleted peers, ordered by their distance to the given node ID.
299
    pub fn get_closest_n_good_standing_peers(
2✔
300
        &self,
2✔
301
        n: usize,
2✔
302
        features: PeerFeatures,
2✔
303
    ) -> Result<Vec<Peer>, PeerManagerError> {
2✔
304
        Ok(self.peer_db.get_closest_n_good_standing_peers(n, features)?)
2✔
305
    }
2✔
306

307
    /// Check if a specific node_id is in the network region of the N nearest neighbours of the region specified by
308
    /// region_node_id. If there are less than N known peers, this will _always_ return true
309
    pub fn in_network_region(&self, node_id: &NodeId, n: usize) -> Result<bool, PeerManagerError> {
4✔
310
        let region_node_id = self.this_peer_identity().node_id;
4✔
311
        let region_node_distance = region_node_id.distance(node_id);
4✔
312
        let node_threshold = self.calc_region_threshold(n, PeerFeatures::COMMUNICATION_NODE)?;
4✔
313
        // Is node ID in the base node threshold?
314
        if region_node_distance <= node_threshold {
4✔
315
            return Ok(true);
3✔
316
        }
1✔
317
        let client_threshold = self.calc_region_threshold(n, PeerFeatures::COMMUNICATION_CLIENT)?; // Is node ID in the base client threshold?
1✔
318
        Ok(region_node_distance <= client_threshold)
1✔
319
    }
4✔
320

321
    /// Calculate the threshold for the region specified by region_node_id.
322
    pub fn calc_region_threshold(&self, n: usize, features: PeerFeatures) -> Result<NodeDistance, PeerManagerError> {
9✔
323
        let region_node_id = self.this_peer_identity().node_id;
9✔
324
        if n == 0 {
9✔
325
            return Ok(NodeDistance::max_distance());
×
326
        }
9✔
327

328
        let closest_peers = self.peer_db.get_closest_n_good_standing_peer_node_ids(n, features)?;
9✔
329
        let mut dists = Vec::new();
9✔
330
        for node_id in closest_peers {
42✔
331
            dists.push(region_node_id.distance(&node_id));
33✔
332
        }
33✔
333

334
        if dists.is_empty() {
9✔
335
            return Ok(NodeDistance::max_distance());
×
336
        }
9✔
337

338
        // If we have less than `n` matching peers in our threshold group, the threshold should be max
339
        if dists.len() < n {
9✔
340
            return Ok(NodeDistance::max_distance());
1✔
341
        }
8✔
342

343
        Ok(dists.pop().expect("dists cannot be empty at this point"))
8✔
344
    }
9✔
345

346
    /// Unban the peer
347
    pub fn unban_peer(&self, node_id: &NodeId) -> Result<(), PeerManagerError> {
×
348
        let _node_id = self.peer_db.reset_banned(node_id)?;
×
349
        Ok(())
×
350
    }
×
351

352
    /// Unban the peer
353
    pub fn unban_all_peers(&self) -> Result<usize, PeerManagerError> {
×
354
        let number_unbanned = self.peer_db.reset_all_banned()?;
×
355
        Ok(number_unbanned)
×
356
    }
×
357

358
    pub fn reset_offline_non_wallet_peers(&self) -> Result<usize, PeerManagerError> {
×
359
        let number_offline = self.peer_db.reset_offline_non_wallet_peers()?;
×
360
        Ok(number_offline)
×
361
    }
×
362

363
    /// Ban the peer for the given duration
364
    pub fn ban_peer(
×
365
        &self,
×
366
        public_key: &CommsPublicKey,
×
367
        duration: Duration,
×
368
        reason: String,
×
369
    ) -> Result<NodeId, PeerManagerError> {
×
370
        let node_id = NodeId::from_key(public_key);
×
371
        self.peer_db
×
372
            .set_banned(&node_id, duration, reason)?
×
373
            .ok_or(PeerManagerError::peer_not_found(&NodeId::from_public_key(public_key)))
×
374
    }
×
375

376
    /// Ban the peer for the given duration
377
    pub fn ban_peer_by_node_id(
24✔
378
        &self,
24✔
379
        node_id: &NodeId,
24✔
380
        duration: Duration,
24✔
381
        reason: String,
24✔
382
    ) -> Result<NodeId, PeerManagerError> {
24✔
383
        self.peer_db
24✔
384
            .set_banned(node_id, duration, reason)?
24✔
385
            .ok_or(PeerManagerError::peer_not_found(node_id))
24✔
386
    }
24✔
387

388
    pub fn is_peer_banned(&self, node_id: &NodeId) -> Result<bool, PeerManagerError> {
2,718✔
389
        let peer = self
2,718✔
390
            .get_peer_by_node_id(node_id)?
2,718✔
391
            .ok_or(PeerManagerError::peer_not_found(node_id))?;
2,718✔
392
        Ok(peer.is_banned())
2,718✔
393
    }
2,718✔
394

395
    /// This will store metadata inside of the metadata field in the peer provided by the nodeID.
396
    /// It will return None if the value was empty and the old value if the value was updated
397
    pub fn set_peer_metadata(
2,246✔
398
        &self,
2,246✔
399
        node_id: &NodeId,
2,246✔
400
        key: u8,
2,246✔
401
        data: Vec<u8>,
2,246✔
402
    ) -> Result<Option<Vec<u8>>, PeerManagerError> {
2,246✔
403
        Ok(self.peer_db.set_metadata(node_id, key, data)?)
2,246✔
404
    }
2,246✔
405
}
406

407
#[allow(clippy::from_over_into)]
408
impl Into<CommsDatabase> for PeerStorageSql {
409
    fn into(self) -> CommsDatabase {
×
410
        self.peer_db
×
411
    }
×
412
}
413

414
#[cfg(test)]
415
mod test {
416
    #![allow(clippy::indexing_slicing)]
417
    use std::{borrow::BorrowMut, iter::repeat_with};
418

419
    use chrono::{DateTime, Utc};
420
    use multiaddr::Multiaddr;
421
    use rand::Rng;
422
    use tari_common_sqlite::connection::DbConnection;
423

424
    use super::*;
425
    use crate::{
426
        net_address::{MultiaddrWithStats, MultiaddressesWithStats, PeerAddressSource},
427
        peer_manager::{create_test_peer_add_internal_addresses, database::MIGRATIONS, peer::PeerFlags},
428
    };
429

430
    fn get_peer_db_sql_test_db() -> Result<PeerDatabaseSql, PeerManagerError> {
6✔
431
        let db_connection = DbConnection::connect_temp_file_and_migrate(MIGRATIONS).unwrap();
6✔
432
        Ok(PeerDatabaseSql::new(
6✔
433
            db_connection,
6✔
434
            &create_test_peer(PeerFeatures::COMMUNICATION_NODE, false),
6✔
435
        )?)
×
436
    }
6✔
437

438
    fn get_peer_storage_sql_test_db() -> Result<PeerStorageSql, PeerManagerError> {
5✔
439
        PeerStorageSql::new_indexed(get_peer_db_sql_test_db()?)
5✔
440
    }
5✔
441

442
    #[test]
443
    fn test_restore() {
1✔
444
        // Create Peers
445
        let mut rng = rand::rngs::OsRng;
1✔
446
        let (_sk, pk) = CommsPublicKey::random_keypair(&mut rng);
1✔
447
        let node_id = NodeId::from_key(&pk);
1✔
448
        let net_address1 = "/ip4/1.2.3.4/tcp/8000".parse::<Multiaddr>().unwrap();
1✔
449
        let net_address2 = "/ip4/5.6.7.8/tcp/8000".parse::<Multiaddr>().unwrap();
1✔
450
        let net_address3 = "/ip4/5.6.7.8/tcp/7000".parse::<Multiaddr>().unwrap();
1✔
451
        let mut net_addresses =
1✔
452
            MultiaddressesWithStats::from_addresses_with_source(vec![net_address1], &PeerAddressSource::Config);
1✔
453
        net_addresses.add_or_update_addresses(&[net_address2], &PeerAddressSource::Config);
1✔
454
        net_addresses.add_or_update_addresses(&[net_address3], &PeerAddressSource::Config);
1✔
455
        let peer1 = Peer::new(
1✔
456
            pk,
1✔
457
            node_id,
1✔
458
            net_addresses,
1✔
459
            PeerFlags::default(),
1✔
460
            PeerFeatures::empty(),
1✔
461
            Default::default(),
1✔
462
            Default::default(),
1✔
463
        );
464

465
        let (_sk, pk) = CommsPublicKey::random_keypair(&mut rng);
1✔
466
        let node_id = NodeId::from_key(&pk);
1✔
467
        let net_address4 = "/ip4/9.10.11.12/tcp/7000".parse::<Multiaddr>().unwrap();
1✔
468
        let net_addresses =
1✔
469
            MultiaddressesWithStats::from_addresses_with_source(vec![net_address4], &PeerAddressSource::Config);
1✔
470
        let peer2: Peer = Peer::new(
1✔
471
            pk,
1✔
472
            node_id,
1✔
473
            net_addresses,
1✔
474
            PeerFlags::default(),
1✔
475
            PeerFeatures::empty(),
1✔
476
            Default::default(),
1✔
477
            Default::default(),
1✔
478
        );
479

480
        let (_sk, pk) = CommsPublicKey::random_keypair(&mut rng);
1✔
481
        let node_id = NodeId::from_key(&pk);
1✔
482
        let net_address5 = "/ip4/13.14.15.16/tcp/6000".parse::<Multiaddr>().unwrap();
1✔
483
        let net_address6 = "/ip4/17.18.19.20/tcp/8000".parse::<Multiaddr>().unwrap();
1✔
484
        let mut net_addresses =
1✔
485
            MultiaddressesWithStats::from_addresses_with_source(vec![net_address5], &PeerAddressSource::Config);
1✔
486
        net_addresses.add_or_update_addresses(&[net_address6], &PeerAddressSource::Config);
1✔
487
        let peer3 = Peer::new(
1✔
488
            pk,
1✔
489
            node_id,
1✔
490
            net_addresses,
1✔
491
            PeerFlags::default(),
1✔
492
            PeerFeatures::empty(),
1✔
493
            Default::default(),
1✔
494
            Default::default(),
1✔
495
        );
496

497
        // Create new datastore with a peer database
498
        let mut db = Some(get_peer_db_sql_test_db().unwrap());
1✔
499
        {
500
            let peer_storage = db.take().unwrap();
1✔
501

502
            // Test adding and searching for peers
503
            assert!(peer_storage.add_or_update_peer(peer1.clone()).is_ok());
1✔
504
            assert!(peer_storage.add_or_update_peer(peer2.clone()).is_ok());
1✔
505
            assert!(peer_storage.add_or_update_peer(peer3.clone()).is_ok());
1✔
506

507
            assert_eq!(peer_storage.size(), 3);
1✔
508
            assert!(peer_storage.get_peer_by_public_key(&peer1.public_key).is_ok());
1✔
509
            assert!(peer_storage.get_peer_by_public_key(&peer2.public_key).is_ok());
1✔
510
            assert!(peer_storage.get_peer_by_public_key(&peer3.public_key).is_ok());
1✔
511
            db = Some(peer_storage);
1✔
512
        }
513
        // Restore from existing database
514
        let peer_storage = PeerStorageSql::new_indexed(db.take().unwrap()).unwrap();
1✔
515

516
        assert_eq!(peer_storage.peer_db.size(), 3);
1✔
517
        assert!(peer_storage.find_by_public_key(&peer1.public_key).is_ok());
1✔
518
        assert!(peer_storage.find_by_public_key(&peer2.public_key).is_ok());
1✔
519
        assert!(peer_storage.find_by_public_key(&peer3.public_key).is_ok());
1✔
520
    }
1✔
521

522
    #[allow(clippy::too_many_lines)]
523
    #[test]
524
    fn test_add_delete_find_peer() {
1✔
525
        let peer_storage = get_peer_storage_sql_test_db().unwrap();
1✔
526

527
        // Create Peers
528
        let mut rng = rand::rngs::OsRng;
1✔
529
        let (_sk, pk) = CommsPublicKey::random_keypair(&mut rng);
1✔
530
        let node_id = NodeId::from_key(&pk);
1✔
531
        let net_address1 = "/ip4/1.2.3.4/tcp/8000".parse::<Multiaddr>().unwrap();
1✔
532
        let net_address2 = "/ip4/5.6.7.8/tcp/8000".parse::<Multiaddr>().unwrap();
1✔
533
        let net_address3 = "/ip4/5.6.7.8/tcp/7000".parse::<Multiaddr>().unwrap();
1✔
534
        let mut net_addresses =
1✔
535
            MultiaddressesWithStats::from_addresses_with_source(vec![net_address1], &PeerAddressSource::Config);
1✔
536
        net_addresses.add_or_update_addresses(&[net_address2], &PeerAddressSource::Config);
1✔
537
        net_addresses.add_or_update_addresses(&[net_address3], &PeerAddressSource::Config);
1✔
538
        let peer1 = Peer::new(
1✔
539
            pk,
1✔
540
            node_id,
1✔
541
            net_addresses,
1✔
542
            PeerFlags::default(),
1✔
543
            PeerFeatures::empty(),
1✔
544
            Default::default(),
1✔
545
            Default::default(),
1✔
546
        );
547

548
        let (_sk, pk) = CommsPublicKey::random_keypair(&mut rng);
1✔
549
        let node_id = NodeId::from_key(&pk);
1✔
550
        let net_address4 = "/ip4/9.10.11.12/tcp/7000".parse::<Multiaddr>().unwrap();
1✔
551
        let net_addresses =
1✔
552
            MultiaddressesWithStats::from_addresses_with_source(vec![net_address4], &PeerAddressSource::Config);
1✔
553
        let peer2: Peer = Peer::new(
1✔
554
            pk,
1✔
555
            node_id,
1✔
556
            net_addresses,
1✔
557
            PeerFlags::default(),
1✔
558
            PeerFeatures::empty(),
1✔
559
            Default::default(),
1✔
560
            Default::default(),
1✔
561
        );
562

563
        let (_sk, pk) = CommsPublicKey::random_keypair(&mut rng);
1✔
564
        let node_id = NodeId::from_key(&pk);
1✔
565
        let net_address5 = "/ip4/13.14.15.16/tcp/6000".parse::<Multiaddr>().unwrap();
1✔
566
        let net_address6 = "/ip4/17.18.19.20/tcp/8000".parse::<Multiaddr>().unwrap();
1✔
567
        let mut net_addresses =
1✔
568
            MultiaddressesWithStats::from_addresses_with_source(vec![net_address5], &PeerAddressSource::Config);
1✔
569
        net_addresses.add_or_update_addresses(&[net_address6], &PeerAddressSource::Config);
1✔
570
        let peer3 = Peer::new(
1✔
571
            pk,
1✔
572
            node_id,
1✔
573
            net_addresses,
1✔
574
            PeerFlags::default(),
1✔
575
            PeerFeatures::empty(),
1✔
576
            Default::default(),
1✔
577
            Default::default(),
1✔
578
        );
579
        // Test adding and searching for peers
580
        peer_storage.add_or_update_peer(peer1.clone()).unwrap(); // assert!(peer_storage.add_or_update_peer(peer1.clone()).is_ok());
1✔
581
        assert!(peer_storage.add_or_update_peer(peer2.clone()).is_ok());
1✔
582
        assert!(peer_storage.add_or_update_peer(peer3.clone()).is_ok());
1✔
583

584
        assert_eq!(peer_storage.peer_db.size(), 3);
1✔
585

586
        assert_eq!(
1✔
587
            peer_storage
1✔
588
                .find_by_public_key(&peer1.public_key)
1✔
589
                .unwrap()
1✔
590
                .unwrap()
1✔
591
                .public_key,
592
            peer1.public_key
593
        );
594
        assert_eq!(
1✔
595
            peer_storage
1✔
596
                .find_by_public_key(&peer2.public_key)
1✔
597
                .unwrap()
1✔
598
                .unwrap()
1✔
599
                .public_key,
600
            peer2.public_key
601
        );
602
        assert_eq!(
1✔
603
            peer_storage
1✔
604
                .find_by_public_key(&peer3.public_key)
1✔
605
                .unwrap()
1✔
606
                .unwrap()
1✔
607
                .public_key,
608
            peer3.public_key
609
        );
610

611
        assert_eq!(
1✔
612
            peer_storage
1✔
613
                .get_peer_by_node_id(&peer1.node_id)
1✔
614
                .unwrap()
1✔
615
                .unwrap()
1✔
616
                .node_id,
617
            peer1.node_id
618
        );
619
        assert_eq!(
1✔
620
            peer_storage
1✔
621
                .get_peer_by_node_id(&peer2.node_id)
1✔
622
                .unwrap()
1✔
623
                .unwrap()
1✔
624
                .node_id,
625
            peer2.node_id
626
        );
627
        assert_eq!(
1✔
628
            peer_storage
1✔
629
                .get_peer_by_node_id(&peer3.node_id)
1✔
630
                .unwrap()
1✔
631
                .unwrap()
1✔
632
                .node_id,
633
            peer3.node_id
634
        );
635

636
        peer_storage.find_by_public_key(&peer1.public_key).unwrap().unwrap();
1✔
637
        peer_storage.find_by_public_key(&peer2.public_key).unwrap().unwrap();
1✔
638
        peer_storage.find_by_public_key(&peer3.public_key).unwrap().unwrap();
1✔
639

640
        // Test delete of border case peer
641
        assert!(peer_storage.soft_delete_peer(&peer3.node_id).is_ok());
1✔
642

643
        // It is a logical delete, so there should still be 3 peers in the db
644
        assert_eq!(peer_storage.peer_db.size(), 3);
1✔
645

646
        assert_eq!(
1✔
647
            peer_storage
1✔
648
                .find_by_public_key(&peer1.public_key)
1✔
649
                .unwrap()
1✔
650
                .unwrap()
1✔
651
                .public_key,
652
            peer1.public_key
653
        );
654
        assert_eq!(
1✔
655
            peer_storage
1✔
656
                .find_by_public_key(&peer2.public_key)
1✔
657
                .unwrap()
1✔
658
                .unwrap()
1✔
659
                .public_key,
660
            peer2.public_key
661
        );
662
        assert!(peer_storage
1✔
663
            .find_by_public_key(&peer3.public_key)
1✔
664
            .unwrap()
1✔
665
            .unwrap()
1✔
666
            .deleted_at
1✔
667
            .is_some());
1✔
668

669
        assert_eq!(
1✔
670
            peer_storage
1✔
671
                .get_peer_by_node_id(&peer1.node_id)
1✔
672
                .unwrap()
1✔
673
                .unwrap()
1✔
674
                .node_id,
675
            peer1.node_id
676
        );
677
        assert_eq!(
1✔
678
            peer_storage
1✔
679
                .get_peer_by_node_id(&peer2.node_id)
1✔
680
                .unwrap()
1✔
681
                .unwrap()
1✔
682
                .node_id,
683
            peer2.node_id
684
        );
685
        assert!(peer_storage
1✔
686
            .get_peer_by_node_id(&peer3.node_id)
1✔
687
            .unwrap()
1✔
688
            .unwrap()
1✔
689
            .deleted_at
1✔
690
            .is_some());
1✔
691
    }
1✔
692

693
    fn create_test_peer(features: PeerFeatures, ban: bool) -> Peer {
30✔
694
        let mut rng = rand::rngs::OsRng;
30✔
695

696
        let (_sk, pk) = CommsPublicKey::random_keypair(&mut rng);
30✔
697
        let node_id = NodeId::from_key(&pk);
30✔
698

699
        let mut net_addresses = MultiaddressesWithStats::from_addresses_with_source(vec![], &PeerAddressSource::Config);
30✔
700

701
        // Create 1 to 4 random addresses
702
        for _i in 1..=rand::thread_rng().gen_range(1..4) {
57✔
703
            let n = [
57✔
704
                rand::thread_rng().gen_range(1..255),
57✔
705
                rand::thread_rng().gen_range(1..255),
57✔
706
                rand::thread_rng().gen_range(1..255),
57✔
707
                rand::thread_rng().gen_range(1..255),
57✔
708
                rand::thread_rng().gen_range(5000..9000),
57✔
709
            ];
57✔
710
            let net_address = format!("/ip4/{}.{}.{}.{}/tcp/{}", n[0], n[1], n[2], n[3], n[4])
57✔
711
                .parse::<Multiaddr>()
57✔
712
                .unwrap();
57✔
713
            net_addresses.add_or_update_addresses(&[net_address], &PeerAddressSource::Config);
57✔
714
        }
57✔
715

716
        let mut peer = Peer::new(
30✔
717
            pk,
30✔
718
            node_id,
30✔
719
            net_addresses,
30✔
720
            PeerFlags::default(),
30✔
721
            features,
30✔
722
            Default::default(),
30✔
723
            Default::default(),
30✔
724
        );
725
        if ban {
30✔
726
            peer.ban_for(Duration::from_secs(600), "".to_string());
1✔
727
        }
29✔
728
        peer
30✔
729
    }
30✔
730

731
    #[test]
732
    fn test_in_network_region() {
1✔
733
        let peer_storage = get_peer_storage_sql_test_db().unwrap();
1✔
734

735
        let mut nodes = repeat_with(|| create_test_peer(PeerFeatures::COMMUNICATION_NODE, false))
5✔
736
            .take(5)
1✔
737
            .chain(repeat_with(|| create_test_peer(PeerFeatures::COMMUNICATION_CLIENT, false)).take(4))
4✔
738
            .collect::<Vec<_>>();
1✔
739

740
        for p in &nodes {
10✔
741
            peer_storage.add_or_update_peer(p.clone()).unwrap();
9✔
742
        }
9✔
743

744
        let main_peer_node_id = peer_storage.this_peer_identity().node_id;
1✔
745

746
        nodes.sort_by(|a, b| {
19✔
747
            a.node_id
19✔
748
                .distance(&main_peer_node_id)
19✔
749
                .cmp(&b.node_id.distance(&main_peer_node_id))
19✔
750
        });
19✔
751

752
        let db_nodes = peer_storage.peer_db.get_all_peers(None).unwrap();
1✔
753
        assert_eq!(db_nodes.len(), 9);
1✔
754

755
        let close_node = &nodes.first().unwrap().node_id;
1✔
756
        let far_node = &nodes.last().unwrap().node_id;
1✔
757

758
        let is_in_region = peer_storage.in_network_region(&main_peer_node_id, 1).unwrap();
1✔
759
        assert!(is_in_region);
1✔
760

761
        let is_in_region = peer_storage.in_network_region(close_node, 1).unwrap();
1✔
762
        assert!(is_in_region);
1✔
763

764
        let is_in_region = peer_storage.in_network_region(far_node, 9).unwrap();
1✔
765
        assert!(is_in_region);
1✔
766

767
        let is_in_region = peer_storage.in_network_region(far_node, 3).unwrap();
1✔
768
        assert!(!is_in_region);
1✔
769
    }
1✔
770

771
    #[test]
772
    fn get_just_seeds() {
1✔
773
        let peer_storage = get_peer_storage_sql_test_db().unwrap();
1✔
774

775
        let seeds = repeat_with(|| {
5✔
776
            let mut peer = create_test_peer(PeerFeatures::COMMUNICATION_NODE, false);
5✔
777
            peer.add_flags(PeerFlags::SEED);
5✔
778
            peer
5✔
779
        })
5✔
780
        .take(5)
1✔
781
        .collect::<Vec<_>>();
1✔
782

783
        for p in &seeds {
6✔
784
            peer_storage.add_or_update_peer(p.clone()).unwrap();
5✔
785
        }
5✔
786

787
        let nodes = repeat_with(|| create_test_peer(PeerFeatures::COMMUNICATION_NODE, false))
5✔
788
            .take(5)
1✔
789
            .collect::<Vec<_>>();
1✔
790

791
        for p in &nodes {
6✔
792
            peer_storage.add_or_update_peer(p.clone()).unwrap();
5✔
793
        }
5✔
794
        let retrieved_seeds = peer_storage.get_seed_peers().unwrap();
1✔
795
        assert_eq!(retrieved_seeds.len(), seeds.len());
1✔
796
        for seed in seeds {
6✔
797
            assert!(retrieved_seeds.iter().any(|p| p.node_id == seed.node_id));
15✔
798
        }
799
    }
1✔
800

801
    #[test]
802
    fn discovery_syncing_returns_correct_peers() {
1✔
803
        let peer_storage = get_peer_storage_sql_test_db().unwrap();
1✔
804

805
        // Threshold duration + a minute
806
        #[allow(clippy::cast_possible_wrap)] // Won't wrap around, numbers are static
807
        let above_the_threshold = Utc::now().timestamp() - (STALE_PEER_THRESHOLD_DURATION.as_secs() + 60) as i64;
1✔
808

809
        let never_seen_peer = create_test_peer(PeerFeatures::COMMUNICATION_NODE, false);
1✔
810
        let banned_peer = create_test_peer(PeerFeatures::COMMUNICATION_NODE, true);
1✔
811

812
        let mut not_active_peer = create_test_peer(PeerFeatures::COMMUNICATION_NODE, false);
1✔
813
        let address = not_active_peer.addresses.best().unwrap();
1✔
814
        let mut address = MultiaddrWithStats::new(address.address().clone(), PeerAddressSource::Config);
1✔
815
        address.mark_last_attempted(DateTime::from_timestamp(above_the_threshold, 0).unwrap().naive_utc());
1✔
816
        not_active_peer
1✔
817
            .addresses
1✔
818
            .merge(&MultiaddressesWithStats::from(vec![address]));
1✔
819

820
        let mut good_peer = create_test_peer(PeerFeatures::COMMUNICATION_NODE, false);
1✔
821
        let good_addresses = good_peer.addresses.borrow_mut();
1✔
822
        let good_address = good_addresses.addresses()[0].address().clone();
1✔
823
        good_addresses.mark_last_seen_now(&good_address);
1✔
824

825
        let mut good_seed = create_test_peer(PeerFeatures::COMMUNICATION_NODE, false);
1✔
826
        good_seed.flags = PeerFlags::SEED;
1✔
827
        let good_addresses = good_seed.addresses.borrow_mut();
1✔
828
        let good_address = good_addresses.addresses()[0].address().clone();
1✔
829
        good_addresses.mark_last_seen_now(&good_address);
1✔
830

831
        assert!(peer_storage.add_or_update_peer(never_seen_peer).is_ok());
1✔
832
        assert!(peer_storage.add_or_update_peer(not_active_peer).is_ok());
1✔
833
        assert!(peer_storage.add_or_update_peer(banned_peer).is_ok());
1✔
834
        assert!(peer_storage.add_or_update_peer(good_peer).is_ok());
1✔
835
        assert!(peer_storage.add_or_update_peer(good_seed.clone()).is_ok());
1✔
836

837
        assert_eq!(peer_storage.all(None).unwrap().len(), 5);
1✔
838
        assert_eq!(
1✔
839
            peer_storage
1✔
840
                .discovery_syncing(100, &[good_seed.node_id], Some(PeerFeatures::COMMUNICATION_NODE), false,)
1✔
841
                .unwrap()
1✔
842
                .len(),
1✔
843
            1
844
        );
845
        assert_eq!(
1✔
846
            peer_storage
1✔
847
                .discovery_syncing(100, &[], Some(PeerFeatures::COMMUNICATION_NODE), false)
1✔
848
                .unwrap()
1✔
849
                .len(),
1✔
850
            2
851
        );
852
    }
1✔
853

854
    #[test]
855
    fn discovery_syncing_peers_with_external_addresses_only() {
1✔
856
        let peer_storage = get_peer_storage_sql_test_db().unwrap();
1✔
857
        let nodes = repeat_with(|| create_test_peer_add_internal_addresses(false, PeerFeatures::COMMUNICATION_NODE))
5✔
858
            .take(5)
1✔
859
            .collect::<Vec<_>>();
1✔
860
        let wallets =
1✔
861
            repeat_with(|| create_test_peer_add_internal_addresses(false, PeerFeatures::COMMUNICATION_CLIENT))
5✔
862
                .take(5)
1✔
863
                .collect::<Vec<_>>();
1✔
864
        for peer in nodes.iter().chain(wallets.iter()) {
10✔
865
            peer_storage.add_or_update_peer(peer.clone()).unwrap();
10✔
866
        }
10✔
867

868
        // Assert that peers have internal and external addresses
869
        let nodes_all_addresses = peer_storage
1✔
870
            .discovery_syncing(100, &[], Some(PeerFeatures::COMMUNICATION_NODE), false)
1✔
871
            .unwrap();
1✔
872
        assert!(nodes_all_addresses
1✔
873
            .iter()
1✔
874
            .all(|p| { p.addresses.addresses().iter().any(|addr| addr.is_external()) }));
5✔
875
        assert!(nodes_all_addresses
1✔
876
            .iter()
1✔
877
            .all(|p| { p.addresses.addresses().iter().any(|addr| !addr.is_external()) }));
12✔
878

879
        // Assert that peers have external addresses only
880
        let nodes_external_addresses_only = peer_storage
1✔
881
            .discovery_syncing(100, &[], Some(PeerFeatures::COMMUNICATION_NODE), true)
1✔
882
            .unwrap();
1✔
883
        assert!(nodes_external_addresses_only
1✔
884
            .iter()
1✔
885
            .all(|p| { p.addresses.addresses().iter().all(|addr| addr.is_external()) }));
7✔
886
    }
1✔
887
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc