Skip to main content

ipfrs_network/
trust_manager.rs

1//! Peer trust management for selective content acceptance based on trust hierarchies.
2//!
3//! This module provides [`PeerTrustManager`] which tracks trust levels and attestations
4//! for peers in the network.
5
6use std::collections::HashMap;
7
8/// Trust level assigned to a peer, from lowest to highest.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[repr(u8)]
11pub enum TrustLevel {
12    /// No trust; default for unknown peers.
13    Untrusted = 0,
14    /// Limited trust; restricted capabilities.
15    Limited = 1,
16    /// Standard trusted peer.
17    Trusted = 2,
18    /// Vouched for by another trusted peer.
19    Vouched = 3,
20    /// Highest trust level; network authority.
21    Authority = 4,
22}
23
24/// An attestation that one peer vouches for another.
25#[derive(Clone, Debug)]
26pub struct TrustAttestation {
27    /// The peer that issued this attestation.
28    pub attester_peer_id: String,
29    /// The peer being vouched for.
30    pub target_peer_id: String,
31    /// The trust level being granted.
32    pub granted_level: TrustLevel,
33    /// The tick at which this attestation was issued.
34    pub issued_at_tick: u64,
35    /// The tick at which this attestation expires.
36    pub expiry_tick: u64,
37}
38
39impl TrustAttestation {
40    /// Returns `true` if this attestation is still valid at `current_tick`.
41    pub fn is_valid(&self, current_tick: u64) -> bool {
42        current_tick < self.expiry_tick
43    }
44}
45
46/// A record of trust information for a single peer.
47#[derive(Clone, Debug)]
48pub struct TrustRecord {
49    /// The peer this record belongs to.
50    pub peer_id: String,
51    /// The base trust level set directly on this peer.
52    pub level: TrustLevel,
53    /// Attestations received by this peer from others.
54    pub attestations: Vec<TrustAttestation>,
55}
56
57impl TrustRecord {
58    fn new(peer_id: impl Into<String>, level: TrustLevel) -> Self {
59        Self {
60            peer_id: peer_id.into(),
61            level,
62            attestations: Vec::new(),
63        }
64    }
65
66    /// Returns the effective trust level considering valid attestations.
67    ///
68    /// Starts with the base `level` and upgrades to the highest granted level
69    /// among valid (non-expired) attestations.
70    pub fn effective_level(&self, current_tick: u64) -> TrustLevel {
71        let mut effective = self.level;
72        for attestation in &self.attestations {
73            if attestation.is_valid(current_tick) && attestation.granted_level > effective {
74                effective = attestation.granted_level;
75            }
76        }
77        effective
78    }
79}
80
81/// Statistics about the current state of the trust manager.
82#[derive(Clone, Debug)]
83pub struct TrustManagerStats {
84    /// Total number of peers tracked.
85    pub total_peers: usize,
86    /// Count of peers at each effective trust level.
87    pub by_level: HashMap<TrustLevel, usize>,
88    /// Total number of attestations across all peers.
89    pub total_attestations: usize,
90    /// Number of expired attestations across all peers.
91    pub expired_attestations: usize,
92}
93
94/// Manages trust levels and attestations for peers in the network.
95pub struct PeerTrustManager {
96    /// Trust records keyed by peer_id.
97    pub records: HashMap<String, TrustRecord>,
98}
99
100impl PeerTrustManager {
101    /// Creates a new, empty `PeerTrustManager`.
102    pub fn new() -> Self {
103        Self {
104            records: HashMap::new(),
105        }
106    }
107
108    /// Sets the base trust level for a peer. Creates the record if it does not exist.
109    pub fn set_trust(&mut self, peer_id: &str, level: TrustLevel) {
110        let record = self
111            .records
112            .entry(peer_id.to_string())
113            .or_insert_with(|| TrustRecord::new(peer_id, TrustLevel::Untrusted));
114        record.level = level;
115    }
116
117    /// Adds an attestation. Auto-creates the target peer record at `Untrusted` if missing.
118    pub fn add_attestation(&mut self, attestation: TrustAttestation) {
119        let target = attestation.target_peer_id.clone();
120        let record = self
121            .records
122            .entry(target.clone())
123            .or_insert_with(|| TrustRecord::new(&target, TrustLevel::Untrusted));
124        record.attestations.push(attestation);
125    }
126
127    /// Returns the effective trust level for a peer at `current_tick`.
128    ///
129    /// Returns `TrustLevel::Untrusted` if the peer is not tracked.
130    pub fn effective_trust(&self, peer_id: &str, current_tick: u64) -> TrustLevel {
131        self.records
132            .get(peer_id)
133            .map(|r| r.effective_level(current_tick))
134            .unwrap_or(TrustLevel::Untrusted)
135    }
136
137    /// Returns peer IDs whose effective trust is at or above `min_level`, sorted alphabetically.
138    pub fn peers_at_or_above(&self, min_level: TrustLevel, current_tick: u64) -> Vec<&str> {
139        let mut result: Vec<&str> = self
140            .records
141            .values()
142            .filter(|r| r.effective_level(current_tick) >= min_level)
143            .map(|r| r.peer_id.as_str())
144            .collect();
145        result.sort_unstable();
146        result
147    }
148
149    /// Removes all attestations issued by `attester_peer_id` across all peer records.
150    pub fn revoke_attestations(&mut self, attester_peer_id: &str) {
151        for record in self.records.values_mut() {
152            record
153                .attestations
154                .retain(|a| a.attester_peer_id != attester_peer_id);
155        }
156    }
157
158    /// Removes a peer record. Returns `true` if the peer was present.
159    pub fn remove_peer(&mut self, peer_id: &str) -> bool {
160        self.records.remove(peer_id).is_some()
161    }
162
163    /// Returns aggregate statistics for the trust manager at `current_tick`.
164    pub fn stats(&self, current_tick: u64) -> TrustManagerStats {
165        let mut by_level: HashMap<TrustLevel, usize> = HashMap::new();
166        let mut total_attestations = 0usize;
167        let mut expired_attestations = 0usize;
168
169        for record in self.records.values() {
170            let eff = record.effective_level(current_tick);
171            *by_level.entry(eff).or_insert(0) += 1;
172
173            for attestation in &record.attestations {
174                total_attestations += 1;
175                if !attestation.is_valid(current_tick) {
176                    expired_attestations += 1;
177                }
178            }
179        }
180
181        TrustManagerStats {
182            total_peers: self.records.len(),
183            by_level,
184            total_attestations,
185            expired_attestations,
186        }
187    }
188}
189
190impl Default for PeerTrustManager {
191    fn default() -> Self {
192        Self::new()
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    // ------------------------------------------------------------------
201    // Helpers
202    // ------------------------------------------------------------------
203
204    fn make_attestation(
205        attester: &str,
206        target: &str,
207        level: TrustLevel,
208        issued: u64,
209        expiry: u64,
210    ) -> TrustAttestation {
211        TrustAttestation {
212            attester_peer_id: attester.to_string(),
213            target_peer_id: target.to_string(),
214            granted_level: level,
215            issued_at_tick: issued,
216            expiry_tick: expiry,
217        }
218    }
219
220    // ------------------------------------------------------------------
221    // TrustLevel ordering
222    // ------------------------------------------------------------------
223
224    #[test]
225    fn test_trust_level_ordering() {
226        assert!(TrustLevel::Untrusted < TrustLevel::Limited);
227        assert!(TrustLevel::Limited < TrustLevel::Trusted);
228        assert!(TrustLevel::Trusted < TrustLevel::Vouched);
229        assert!(TrustLevel::Vouched < TrustLevel::Authority);
230    }
231
232    // ------------------------------------------------------------------
233    // is_valid
234    // ------------------------------------------------------------------
235
236    #[test]
237    fn test_is_valid_before_expiry() {
238        let a = make_attestation("A", "B", TrustLevel::Trusted, 0, 100);
239        assert!(a.is_valid(99));
240    }
241
242    #[test]
243    fn test_is_valid_at_expiry() {
244        let a = make_attestation("A", "B", TrustLevel::Trusted, 0, 100);
245        assert!(!a.is_valid(100));
246    }
247
248    #[test]
249    fn test_is_valid_after_expiry() {
250        let a = make_attestation("A", "B", TrustLevel::Trusted, 0, 100);
251        assert!(!a.is_valid(200));
252    }
253
254    // ------------------------------------------------------------------
255    // PeerTrustManager::new
256    // ------------------------------------------------------------------
257
258    #[test]
259    fn test_new_starts_empty() {
260        let mgr = PeerTrustManager::new();
261        assert!(mgr.records.is_empty());
262    }
263
264    // ------------------------------------------------------------------
265    // set_trust
266    // ------------------------------------------------------------------
267
268    #[test]
269    fn test_set_trust_creates_record() {
270        let mut mgr = PeerTrustManager::new();
271        mgr.set_trust("peer-1", TrustLevel::Trusted);
272        assert!(mgr.records.contains_key("peer-1"));
273        assert_eq!(mgr.records["peer-1"].level, TrustLevel::Trusted);
274    }
275
276    #[test]
277    fn test_set_trust_updates_existing_record() {
278        let mut mgr = PeerTrustManager::new();
279        mgr.set_trust("peer-1", TrustLevel::Limited);
280        mgr.set_trust("peer-1", TrustLevel::Authority);
281        assert_eq!(mgr.records["peer-1"].level, TrustLevel::Authority);
282    }
283
284    // ------------------------------------------------------------------
285    // effective_trust
286    // ------------------------------------------------------------------
287
288    #[test]
289    fn test_effective_trust_unknown_peer_returns_untrusted() {
290        let mgr = PeerTrustManager::new();
291        assert_eq!(mgr.effective_trust("nobody", 0), TrustLevel::Untrusted);
292    }
293
294    #[test]
295    fn test_effective_trust_base_level_no_attestations() {
296        let mut mgr = PeerTrustManager::new();
297        mgr.set_trust("peer-1", TrustLevel::Trusted);
298        assert_eq!(mgr.effective_trust("peer-1", 50), TrustLevel::Trusted);
299    }
300
301    #[test]
302    fn test_effective_trust_upgraded_by_valid_attestation() {
303        let mut mgr = PeerTrustManager::new();
304        mgr.set_trust("peer-1", TrustLevel::Limited);
305        mgr.add_attestation(make_attestation(
306            "auth",
307            "peer-1",
308            TrustLevel::Vouched,
309            0,
310            100,
311        ));
312        assert_eq!(mgr.effective_trust("peer-1", 50), TrustLevel::Vouched);
313    }
314
315    #[test]
316    fn test_effective_trust_not_upgraded_by_expired_attestation() {
317        let mut mgr = PeerTrustManager::new();
318        mgr.set_trust("peer-1", TrustLevel::Limited);
319        mgr.add_attestation(make_attestation(
320            "auth",
321            "peer-1",
322            TrustLevel::Vouched,
323            0,
324            100,
325        ));
326        // After expiry the base level should be returned
327        assert_eq!(mgr.effective_trust("peer-1", 100), TrustLevel::Limited);
328    }
329
330    #[test]
331    fn test_effective_trust_returns_max_of_base_and_attestations() {
332        let mut mgr = PeerTrustManager::new();
333        mgr.set_trust("peer-1", TrustLevel::Trusted);
334        // Attestation granting a lower level than base should be ignored
335        mgr.add_attestation(make_attestation(
336            "auth",
337            "peer-1",
338            TrustLevel::Limited,
339            0,
340            100,
341        ));
342        assert_eq!(mgr.effective_trust("peer-1", 50), TrustLevel::Trusted);
343    }
344
345    #[test]
346    fn test_effective_trust_multiple_attestations_highest_wins() {
347        let mut mgr = PeerTrustManager::new();
348        mgr.set_trust("peer-1", TrustLevel::Untrusted);
349        mgr.add_attestation(make_attestation(
350            "a1",
351            "peer-1",
352            TrustLevel::Limited,
353            0,
354            200,
355        ));
356        mgr.add_attestation(make_attestation(
357            "a2",
358            "peer-1",
359            TrustLevel::Authority,
360            0,
361            200,
362        ));
363        mgr.add_attestation(make_attestation(
364            "a3",
365            "peer-1",
366            TrustLevel::Trusted,
367            0,
368            200,
369        ));
370        assert_eq!(mgr.effective_trust("peer-1", 100), TrustLevel::Authority);
371    }
372
373    // ------------------------------------------------------------------
374    // add_attestation
375    // ------------------------------------------------------------------
376
377    #[test]
378    fn test_add_attestation_auto_creates_target() {
379        let mut mgr = PeerTrustManager::new();
380        mgr.add_attestation(make_attestation(
381            "auth",
382            "peer-new",
383            TrustLevel::Trusted,
384            0,
385            50,
386        ));
387        assert!(mgr.records.contains_key("peer-new"));
388        assert_eq!(mgr.records["peer-new"].level, TrustLevel::Untrusted);
389    }
390
391    #[test]
392    fn test_add_attestation_appends_to_record() {
393        let mut mgr = PeerTrustManager::new();
394        mgr.set_trust("peer-1", TrustLevel::Untrusted);
395        mgr.add_attestation(make_attestation("a1", "peer-1", TrustLevel::Limited, 0, 50));
396        mgr.add_attestation(make_attestation("a2", "peer-1", TrustLevel::Trusted, 0, 50));
397        assert_eq!(mgr.records["peer-1"].attestations.len(), 2);
398    }
399
400    // ------------------------------------------------------------------
401    // peers_at_or_above
402    // ------------------------------------------------------------------
403
404    #[test]
405    fn test_peers_at_or_above_filters_correctly() {
406        let mut mgr = PeerTrustManager::new();
407        mgr.set_trust("alice", TrustLevel::Trusted);
408        mgr.set_trust("bob", TrustLevel::Limited);
409        mgr.set_trust("carol", TrustLevel::Authority);
410
411        let peers = mgr.peers_at_or_above(TrustLevel::Trusted, 0);
412        assert!(peers.contains(&"alice"));
413        assert!(peers.contains(&"carol"));
414        assert!(!peers.contains(&"bob"));
415    }
416
417    #[test]
418    fn test_peers_at_or_above_sorted_alphabetically() {
419        let mut mgr = PeerTrustManager::new();
420        mgr.set_trust("zara", TrustLevel::Trusted);
421        mgr.set_trust("alice", TrustLevel::Trusted);
422        mgr.set_trust("mike", TrustLevel::Trusted);
423
424        let peers = mgr.peers_at_or_above(TrustLevel::Trusted, 0);
425        assert_eq!(peers, vec!["alice", "mike", "zara"]);
426    }
427
428    // ------------------------------------------------------------------
429    // revoke_attestations
430    // ------------------------------------------------------------------
431
432    #[test]
433    fn test_revoke_attestations_removes_all_from_attester() {
434        let mut mgr = PeerTrustManager::new();
435        mgr.set_trust("peer-1", TrustLevel::Untrusted);
436        mgr.set_trust("peer-2", TrustLevel::Untrusted);
437        mgr.add_attestation(make_attestation(
438            "bad-auth",
439            "peer-1",
440            TrustLevel::Vouched,
441            0,
442            200,
443        ));
444        mgr.add_attestation(make_attestation(
445            "bad-auth",
446            "peer-2",
447            TrustLevel::Vouched,
448            0,
449            200,
450        ));
451
452        mgr.revoke_attestations("bad-auth");
453
454        assert!(mgr.records["peer-1"].attestations.is_empty());
455        assert!(mgr.records["peer-2"].attestations.is_empty());
456    }
457
458    #[test]
459    fn test_revoke_attestations_leaves_other_attestations() {
460        let mut mgr = PeerTrustManager::new();
461        mgr.set_trust("peer-1", TrustLevel::Untrusted);
462        mgr.add_attestation(make_attestation(
463            "bad-auth",
464            "peer-1",
465            TrustLevel::Vouched,
466            0,
467            200,
468        ));
469        mgr.add_attestation(make_attestation(
470            "good-auth",
471            "peer-1",
472            TrustLevel::Trusted,
473            0,
474            200,
475        ));
476
477        mgr.revoke_attestations("bad-auth");
478
479        let attestations = &mgr.records["peer-1"].attestations;
480        assert_eq!(attestations.len(), 1);
481        assert_eq!(attestations[0].attester_peer_id, "good-auth");
482    }
483
484    // ------------------------------------------------------------------
485    // remove_peer
486    // ------------------------------------------------------------------
487
488    #[test]
489    fn test_remove_peer_returns_true_when_present() {
490        let mut mgr = PeerTrustManager::new();
491        mgr.set_trust("peer-1", TrustLevel::Trusted);
492        assert!(mgr.remove_peer("peer-1"));
493    }
494
495    #[test]
496    fn test_remove_peer_returns_false_when_absent() {
497        let mut mgr = PeerTrustManager::new();
498        assert!(!mgr.remove_peer("ghost"));
499    }
500
501    // ------------------------------------------------------------------
502    // stats
503    // ------------------------------------------------------------------
504
505    #[test]
506    fn test_stats_total_peers() {
507        let mut mgr = PeerTrustManager::new();
508        mgr.set_trust("p1", TrustLevel::Trusted);
509        mgr.set_trust("p2", TrustLevel::Limited);
510        let s = mgr.stats(0);
511        assert_eq!(s.total_peers, 2);
512    }
513
514    #[test]
515    fn test_stats_by_level_counts_effective_levels() {
516        let mut mgr = PeerTrustManager::new();
517        mgr.set_trust("p1", TrustLevel::Trusted);
518        mgr.set_trust("p2", TrustLevel::Limited);
519        // p2 gets a valid attestation upgrading to Vouched
520        mgr.add_attestation(make_attestation("a", "p2", TrustLevel::Vouched, 0, 100));
521        let s = mgr.stats(50);
522        assert_eq!(
523            s.by_level.get(&TrustLevel::Trusted).copied().unwrap_or(0),
524            1
525        );
526        assert_eq!(
527            s.by_level.get(&TrustLevel::Vouched).copied().unwrap_or(0),
528            1
529        );
530    }
531
532    #[test]
533    fn test_stats_total_attestations() {
534        let mut mgr = PeerTrustManager::new();
535        mgr.set_trust("p1", TrustLevel::Untrusted);
536        mgr.add_attestation(make_attestation("a1", "p1", TrustLevel::Limited, 0, 200));
537        mgr.add_attestation(make_attestation("a2", "p1", TrustLevel::Trusted, 0, 200));
538        let s = mgr.stats(50);
539        assert_eq!(s.total_attestations, 2);
540    }
541
542    #[test]
543    fn test_stats_expired_attestations_count() {
544        let mut mgr = PeerTrustManager::new();
545        mgr.set_trust("p1", TrustLevel::Untrusted);
546        mgr.add_attestation(make_attestation("a1", "p1", TrustLevel::Limited, 0, 50)); // will expire at tick 100
547        mgr.add_attestation(make_attestation("a2", "p1", TrustLevel::Trusted, 0, 200)); // still valid
548        let s = mgr.stats(100); // tick 100: a1 is expired (100 < 50 is false), a2 is valid
549        assert_eq!(s.expired_attestations, 1);
550        assert_eq!(s.total_attestations, 2);
551    }
552}