use chrono::Utc;
use rusqlite::{params, Connection};
use serde::{Deserialize, Serialize};
use super::types::TrustScore;
pub fn compute_trust_score(
success_rate: f32,
uptime: f32,
threat_score: f32,
integrity_score: f32,
) -> TrustScore {
let s = success_rate.clamp(0.0, 1.0);
let u = uptime.clamp(0.0, 1.0);
let t = threat_score.clamp(0.0, 1.0);
let i = integrity_score.clamp(0.0, 1.0);
let composite = 0.4 * s + 0.2 * u + 0.2 * (1.0 - t) + 0.2 * i;
let composite = composite.clamp(0.0, 1.0);
TrustScore {
peer_id: String::new(),
success_rate: s,
uptime: u,
threat_score: t,
integrity_score: i,
composite,
last_updated: Utc::now(),
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct InteractionRecord {
pub peer_id: String,
pub ts: chrono::DateTime<Utc>,
pub success: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ThreatSeverity {
Low,
Medium,
High,
Critical,
}
pub fn upgrade_eligibility(history: &[InteractionRecord], score: f32) -> bool {
history.len() >= 10 && score >= 0.7
}
pub fn instant_downgrade_on_threat(score: &mut TrustScore, threat_severity: ThreatSeverity) {
match threat_severity {
ThreatSeverity::Critical => {
score.success_rate = 0.0;
score.integrity_score = 0.0;
score.threat_score = 1.0;
}
ThreatSeverity::High => {
score.success_rate *= 0.5;
score.integrity_score *= 0.5;
score.threat_score = (score.threat_score + 0.5).clamp(0.0, 1.0);
}
ThreatSeverity::Medium | ThreatSeverity::Low => {
return;
}
}
let composite = 0.4 * score.success_rate
+ 0.2 * score.uptime
+ 0.2 * (1.0 - score.threat_score)
+ 0.2 * score.integrity_score;
score.composite = composite.clamp(0.0, 1.0);
score.last_updated = Utc::now();
}
pub fn ensure_schema(conn: &Connection) -> rusqlite::Result<()> {
conn.execute(
"CREATE TABLE IF NOT EXISTS trust_scores (
peer_id TEXT PRIMARY KEY,
success_rate REAL NOT NULL,
uptime REAL NOT NULL,
threat_score REAL NOT NULL,
integrity_score REAL NOT NULL,
composite REAL NOT NULL,
last_updated TEXT NOT NULL
)",
[],
)?;
Ok(())
}
pub fn save_to(conn: &Connection, score: &TrustScore) -> rusqlite::Result<()> {
ensure_schema(conn)?;
conn.execute(
"INSERT INTO trust_scores (peer_id, success_rate, uptime, threat_score, integrity_score, composite, last_updated)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
ON CONFLICT(peer_id) DO UPDATE SET
success_rate = excluded.success_rate,
uptime = excluded.uptime,
threat_score = excluded.threat_score,
integrity_score = excluded.integrity_score,
composite = excluded.composite,
last_updated = excluded.last_updated",
params![
score.peer_id,
score.success_rate as f64,
score.uptime as f64,
score.threat_score as f64,
score.integrity_score as f64,
score.composite as f64,
score.last_updated.to_rfc3339(),
],
)?;
Ok(())
}
pub fn load_from(conn: &Connection, peer_id: &str) -> rusqlite::Result<Option<TrustScore>> {
ensure_schema(conn)?;
let mut stmt = conn.prepare(
"SELECT peer_id, success_rate, uptime, threat_score, integrity_score, composite, last_updated
FROM trust_scores WHERE peer_id = ?1",
)?;
let mut rows = stmt.query(params![peer_id])?;
if let Some(row) = rows.next()? {
let last_updated_s: String = row.get(6)?;
let last_updated = chrono::DateTime::parse_from_rfc3339(&last_updated_s)
.map(|dt| dt.with_timezone(&Utc))
.unwrap_or_else(|_| Utc::now());
Ok(Some(TrustScore {
peer_id: row.get(0)?,
success_rate: row.get::<_, f64>(1)? as f32,
uptime: row.get::<_, f64>(2)? as f32,
threat_score: row.get::<_, f64>(3)? as f32,
integrity_score: row.get::<_, f64>(4)? as f32,
composite: row.get::<_, f64>(5)? as f32,
last_updated,
}))
} else {
Ok(None)
}
}