1use std::collections::HashMap;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[repr(u8)]
11pub enum TrustLevel {
12 Untrusted = 0,
14 Limited = 1,
16 Trusted = 2,
18 Vouched = 3,
20 Authority = 4,
22}
23
24#[derive(Clone, Debug)]
26pub struct TrustAttestation {
27 pub attester_peer_id: String,
29 pub target_peer_id: String,
31 pub granted_level: TrustLevel,
33 pub issued_at_tick: u64,
35 pub expiry_tick: u64,
37}
38
39impl TrustAttestation {
40 pub fn is_valid(&self, current_tick: u64) -> bool {
42 current_tick < self.expiry_tick
43 }
44}
45
46#[derive(Clone, Debug)]
48pub struct TrustRecord {
49 pub peer_id: String,
51 pub level: TrustLevel,
53 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 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#[derive(Clone, Debug)]
83pub struct TrustManagerStats {
84 pub total_peers: usize,
86 pub by_level: HashMap<TrustLevel, usize>,
88 pub total_attestations: usize,
90 pub expired_attestations: usize,
92}
93
94pub struct PeerTrustManager {
96 pub records: HashMap<String, TrustRecord>,
98}
99
100impl PeerTrustManager {
101 pub fn new() -> Self {
103 Self {
104 records: HashMap::new(),
105 }
106 }
107
108 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 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 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 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 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 pub fn remove_peer(&mut self, peer_id: &str) -> bool {
160 self.records.remove(peer_id).is_some()
161 }
162
163 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 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 #[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 #[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 #[test]
259 fn test_new_starts_empty() {
260 let mgr = PeerTrustManager::new();
261 assert!(mgr.records.is_empty());
262 }
263
264 #[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 #[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 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 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 #[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 #[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 #[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 #[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 #[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 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)); mgr.add_attestation(make_attestation("a2", "p1", TrustLevel::Trusted, 0, 200)); let s = mgr.stats(100); assert_eq!(s.expired_attestations, 1);
550 assert_eq!(s.total_attestations, 2);
551 }
552}