1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! Verifier Reputation Tracking (AUDIT ONLY)
//!
//! [`VerifierReputation`] tracks aggregate statistics about a verifier's
//! historical behaviour for audit and monitoring purposes.
//!
//! # CRITICAL CONSTRAINT
//!
//! Reputation data **MUST NOT** influence consensus decisions, policy
//! evaluation, or trust judgments in any form. It exists exclusively as an
//! audit-visibility tool for human operators.
//!
//! This constraint is enforced architecturally: `VerifierReputation` does
//! not implement any trait used by the consensus or policy engines, and
//! `reliability_score()` is explicitly annotated as audit-only.
//!
//! # Intended Use
//!
//! - Display in monitoring dashboards
//! - Manual operator review prior to governance actions
//! - Post-incident forensic analysis
use alloc::string::String;
// ── VerifierReputation ────────────────────────────────────────────────────
/// Aggregate statistics about a verifier's historical behaviour.
///
/// # AUDIT ONLY
///
/// These counters are for human inspection only. They MUST NOT be read by
/// the consensus engine, policy engine, or any automated trust decision
/// pathway. Feeding reputation into consensus would create a covert channel
/// for implicit trust weighting, which violates the explainability invariant.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct VerifierReputation {
/// The verifier whose reputation this tracks.
pub verifier_id: String,
/// Count of attestations this verifier evaluated as trustworthy and
/// whose trust was subsequently confirmed by other means.
pub successful_verifications: u64,
/// Count of times this verifier's policy evaluation conflicted with
/// the federation majority.
pub policy_conflicts: u64,
/// Count of times this verifier failed to produce a required transparency
/// log entry within the expected window.
pub transparency_failures: u64,
/// Count of quorum votes this verifier has participated in.
pub quorum_participations: u64,
/// Unix seconds of the last update to this reputation record.
pub last_updated: u64,
}
impl VerifierReputation {
/// Creates a zeroed reputation record for the given verifier.
#[must_use]
pub fn new(verifier_id: String, now: u64) -> Self {
Self {
verifier_id,
successful_verifications: 0,
policy_conflicts: 0,
transparency_failures: 0,
quorum_participations: 0,
last_updated: now,
}
}
/// Records a successful verification.
pub fn record_success(&mut self, now: u64) {
self.successful_verifications = self.successful_verifications.saturating_add(1);
self.last_updated = now;
}
/// Records a policy conflict with the federation majority.
pub fn record_conflict(&mut self, now: u64) {
self.policy_conflicts = self.policy_conflicts.saturating_add(1);
self.last_updated = now;
}
/// Records a transparency logging failure.
pub fn record_transparency_failure(&mut self, now: u64) {
self.transparency_failures = self.transparency_failures.saturating_add(1);
self.last_updated = now;
}
/// Records participation in a quorum vote.
pub fn record_quorum_participation(&mut self, now: u64) {
self.quorum_participations = self.quorum_participations.saturating_add(1);
self.last_updated = now;
}
/// Returns a normalized reliability score in [0.0, 1.0].
///
/// # AUDIT ONLY
///
/// This score is computed as `successful / (successful + conflicts + failures)`.
/// It is informational and MUST NOT be used to gate or weight any trust
/// decision, policy rule, or consensus vote.
///
/// Returns `1.0` when the verifier has no recorded events (no evidence of
/// failure is not evidence of perfection; treat this conservatively).
#[must_use]
#[doc = "AUDIT ONLY: must not influence trust decisions or consensus"]
pub fn reliability_score(&self) -> f64 {
let total = self
.successful_verifications
.saturating_add(self.policy_conflicts)
.saturating_add(self.transparency_failures);
if total == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
let score = self.successful_verifications as f64 / total as f64;
score
}
}
// ── Tests ─────────────────────────────────────────────────────────────────
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_reputation_is_zeroed() {
let rep = VerifierReputation::new("v1".into(), 0);
assert_eq!(rep.successful_verifications, 0);
assert_eq!(rep.policy_conflicts, 0);
assert_eq!(rep.transparency_failures, 0);
assert_eq!(rep.quorum_participations, 0);
}
#[test]
fn reliability_score_no_events() {
let rep = VerifierReputation::new("v1".into(), 0);
assert!((rep.reliability_score() - 1.0).abs() < f64::EPSILON);
}
#[test]
fn reliability_score_all_success() {
let mut rep = VerifierReputation::new("v1".into(), 0);
rep.record_success(1);
rep.record_success(2);
rep.record_success(3);
assert!((rep.reliability_score() - 1.0).abs() < f64::EPSILON);
}
#[test]
fn reliability_score_mixed() {
let mut rep = VerifierReputation::new("v1".into(), 0);
rep.record_success(1);
rep.record_success(2);
rep.record_conflict(3);
// 2 / (2 + 1 + 0) = 0.666...
let score = rep.reliability_score();
assert!(score > 0.66 && score < 0.67);
}
#[test]
fn saturating_add_does_not_overflow() {
let mut rep = VerifierReputation::new("v1".into(), 0);
rep.successful_verifications = u64::MAX;
rep.record_success(1); // should not panic
assert_eq!(rep.successful_verifications, u64::MAX);
}
#[test]
fn last_updated_tracks_time() {
let mut rep = VerifierReputation::new("v1".into(), 100);
rep.record_success(200);
assert_eq!(rep.last_updated, 200);
rep.record_conflict(300);
assert_eq!(rep.last_updated, 300);
}
}