Skip to main content

eth_id/audit/
mod.rs

1use crate::error::{Result, EthIdError};
2use crate::verifier::VerificationResult;
3use serde::{Deserialize, Serialize};
4use std::path::{Path, PathBuf};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct AuditEntry {
8    pub session_id: String,
9    pub timestamp: chrono::DateTime<chrono::Utc>,
10    pub document_path: String,
11    pub document_hash: String,
12    pub claim: String,
13    pub result: bool,
14    pub confidence: f32,
15    pub proof_type: String,
16}
17
18pub struct AuditLog;
19
20impl AuditLog {
21    pub fn new() -> Self {
22        Self
23    }
24    
25    pub fn record_verification(
26        &self,
27        session_id: &str,
28        document_path: &Path,
29        claim: &str,
30        result: &VerificationResult,
31        use_zk: bool,
32    ) -> Result<()> {
33        let entry = AuditEntry {
34            session_id: session_id.to_string(),
35            timestamp: chrono::Utc::now(),
36            document_path: document_path.display().to_string(),
37            document_hash: self.hash_file(document_path)?,
38            claim: claim.to_string(),
39            result: result.answer,
40            confidence: result.confidence,
41            proof_type: if use_zk { "zk".to_string() } else { "llm".to_string() },
42        };
43        
44        self.append_entry(&entry)?;
45        
46        Ok(())
47    }
48    
49    fn append_entry(&self, entry: &AuditEntry) -> Result<()> {
50        let log_path = self.get_log_path()?;
51        
52        let mut entries = if log_path.exists() {
53            let content = std::fs::read_to_string(&log_path)?;
54            serde_json::from_str::<Vec<AuditEntry>>(&content)
55                .unwrap_or_else(|_| Vec::new())
56        } else {
57            Vec::new()
58        };
59        
60        entries.push(entry.clone());
61        
62        let json = serde_json::to_string_pretty(&entries)?;
63        std::fs::write(&log_path, json)?;
64        
65        tracing::debug!("Audit entry recorded: {}", entry.session_id);
66        
67        Ok(())
68    }
69    
70    pub fn list_entries(&self) -> Result<Vec<AuditEntry>> {
71        let log_path = self.get_log_path()?;
72        
73        if !log_path.exists() {
74            return Ok(Vec::new());
75        }
76        
77        let content = std::fs::read_to_string(&log_path)?;
78        let entries = serde_json::from_str(&content)?;
79        
80        Ok(entries)
81    }
82    
83    pub fn get_entry(&self, session_id: &str) -> Result<AuditEntry> {
84        let entries = self.list_entries()?;
85        
86        entries.into_iter()
87            .find(|e| e.session_id == session_id)
88            .ok_or_else(|| EthIdError::AuditLog(
89                format!("Session not found: {}", session_id)
90            ))
91    }
92    
93    fn get_log_path(&self) -> Result<PathBuf> {
94        let home_dir = dirs::home_dir()
95            .ok_or_else(|| EthIdError::AuditLog("Cannot find home directory".to_string()))?;
96        
97        let audit_dir = home_dir.join(".eth-id").join("audit");
98        std::fs::create_dir_all(&audit_dir)?;
99        
100        Ok(audit_dir.join("audit.json"))
101    }
102    
103    fn hash_file(&self, path: &Path) -> Result<String> {
104        use sha2::{Sha256, Digest};
105        
106        let content = std::fs::read(path)?;
107        let mut hasher = Sha256::new();
108        hasher.update(&content);
109        Ok(format!("{:x}", hasher.finalize()))
110    }
111}