use crate::credential::{KwaaiCredentialType, VerifiableCredential};
use chrono::{DateTime, Utc};
#[derive(Debug, Clone)]
pub struct TrustScore {
pub score: f64,
pub credential_contribution: f64,
pub peer_endorsement_contribution: f64,
pub credential_count: usize,
}
impl TrustScore {
pub fn from_credentials(vcs: &[VerifiableCredential]) -> Self {
let mut credential_score = 0.0_f64;
let mut count = 0usize;
for vc in vcs {
if vc.is_expired() {
continue;
}
let Some(vc_type) = vc.kwaai_type() else {
continue;
};
let weight = vc_type.trust_weight();
let decay = time_decay(vc.issuance_date);
credential_score += weight * decay;
count += 1;
}
let capped = credential_score.min(1.0);
TrustScore {
score: capped,
credential_contribution: capped,
peer_endorsement_contribution: 0.0,
credential_count: count,
}
}
pub fn tier_label(&self) -> &'static str {
match self.score {
s if s >= 0.70 => "Trusted",
s if s >= 0.40 => "Verified",
s if s >= 0.10 => "Known",
_ => "Unknown",
}
}
pub fn max_single_weight() -> f64 {
[
KwaaiCredentialType::FiduciaryPledgeVC,
KwaaiCredentialType::VerifiedNodeVC,
KwaaiCredentialType::UptimeVC,
KwaaiCredentialType::ThroughputVC,
KwaaiCredentialType::SummitAttendeeVC,
KwaaiCredentialType::PeerEndorsementVC,
]
.iter()
.map(|t| t.trust_weight())
.fold(0.0_f64, f64::max)
}
}
fn time_decay(issuance_date: DateTime<Utc>) -> f64 {
let age_days = (Utc::now() - issuance_date).num_days().max(0) as f64;
0.5_f64.powf(age_days / 365.0)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::credential::summit_attendee_vc;
#[test]
fn empty_credentials_zero_score() {
let score = TrustScore::from_credentials(&[]);
assert_eq!(score.score, 0.0);
assert_eq!(score.tier_label(), "Unknown");
}
#[test]
fn summit_vc_gives_known_tier() {
let vc = summit_attendee_vc(
"did:peer:issuer",
"did:peer:subject",
"Kwaai Summit 2026",
"2026-03-15",
);
let score = TrustScore::from_credentials(&[vc]);
assert!(score.score > 0.0);
assert_eq!(score.tier_label(), "Known");
}
#[test]
fn time_decay_is_one_for_fresh_vc() {
let decay = 0.5_f64.powf(0.0 / 365.0);
assert!((decay - 1.0).abs() < 1e-10);
}
}