confium_tc/coordinator/
audit.rs1use crate::coordinator::session::{SessionId, SignerId};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct AuditEntry {
10 pub timestamp: DateTime<Utc>,
12 pub event: AuditEvent,
14 pub session_id: SessionId,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(tag = "type", rename_all = "snake_case")]
21pub enum AuditEvent {
22 SessionCreated {
24 requested_by: SignerId,
26 quorum_id: String,
28 },
29 CommitmentReceived {
31 signer: SignerId,
33 },
34 ShareReceived {
36 signer: SignerId,
38 },
39 Aggregated,
41 Expired,
43 Aborted {
45 reason: String,
47 },
48}
49
50#[derive(Debug, Default)]
52pub struct AuditLog {
53 entries: Vec<AuditEntry>,
54}
55
56impl AuditLog {
57 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn append(&mut self, session_id: impl Into<SessionId>, event: AuditEvent) {
64 self.entries.push(AuditEntry {
65 timestamp: Utc::now(),
66 event,
67 session_id: session_id.into(),
68 });
69 }
70
71 pub fn entries_for(&self, session_id: &str) -> Vec<&AuditEntry> {
73 self.entries
74 .iter()
75 .filter(|e| e.session_id == session_id)
76 .collect()
77 }
78
79 pub fn all(&self) -> &[AuditEntry] {
81 &self.entries
82 }
83
84 pub fn to_jsonl(&self) -> Result<String, serde_json::Error> {
86 let mut out = String::new();
87 for entry in &self.entries {
88 out.push_str(&serde_json::to_string(entry)?);
89 out.push('\n');
90 }
91 Ok(out)
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn append_and_query() {
101 let mut log = AuditLog::new();
102 log.append(
103 "session-1",
104 AuditEvent::SessionCreated {
105 requested_by: "alice".into(),
106 quorum_id: "biml-root".into(),
107 },
108 );
109 log.append(
110 "session-1",
111 AuditEvent::CommitmentReceived {
112 signer: "alice".into(),
113 },
114 );
115 log.append(
116 "session-2",
117 AuditEvent::SessionCreated {
118 requested_by: "bob".into(),
119 quorum_id: "biml-root".into(),
120 },
121 );
122
123 let s1_entries = log.entries_for("session-1");
124 assert_eq!(s1_entries.len(), 2);
125 let s2_entries = log.entries_for("session-2");
126 assert_eq!(s2_entries.len(), 1);
127 }
128
129 #[test]
130 fn jsonl_round_trips() {
131 let mut log = AuditLog::new();
132 log.append("session-1", AuditEvent::Aggregated);
133 let jsonl = log.to_jsonl().unwrap();
134 assert!(jsonl.contains("session-1"));
135 assert!(jsonl.contains("aggregated"));
136 }
137}