1use crate::error::PrivacyError;
10use crate::types::{PosetEvent, PrivacyProof, ProofScheme};
11use sha2::{Digest, Sha256};
12
13extern crate alloc;
14use alloc::{format, collections::BTreeMap, string::String, vec::Vec};
15
16pub struct ChronosyncEngine {
18 events: BTreeMap<String, PosetEvent>,
19 order: Vec<String>,
21}
22
23impl ChronosyncEngine {
24 pub fn new() -> Self {
25 Self { events: BTreeMap::new(), order: Vec::new() }
26 }
27
28 pub fn add_event(&mut self, event: PosetEvent) {
30 self.events.insert(event.id.clone(), event);
31 }
32
33 pub fn resolve(
38 &mut self,
39 chaos_seed: &[u8; 32],
40 ) -> Result<Vec<String>, PrivacyError> {
41 if self.events.is_empty() {
42 return Ok(Vec::new());
43 }
44
45 let mut in_degree: BTreeMap<String, usize> = BTreeMap::new();
48 for event in self.events.values() {
49 in_degree.entry(event.id.clone()).or_insert(0);
50 for dep in &event.dependencies {
51 in_degree.entry(dep.clone()).or_insert(0); *in_degree.entry(event.id.clone()).or_insert(0) += 1;
54 }
55 }
56
57 let mut queue: Vec<String> = in_degree
58 .iter()
59 .filter(|(_, &d)| d == 0)
60 .map(|(id, _)| id.clone())
61 .collect();
62
63 let mut sorted = Vec::new();
64 while !queue.is_empty() {
65 queue.sort_by(|a, b| {
67 let ha = self.chaos_hash(a, chaos_seed);
68 let hb = self.chaos_hash(b, chaos_seed);
69 ha.cmp(&hb)
70 });
71
72 let current = queue.remove(0);
73 sorted.push(current.clone());
74
75 for event in self.events.values() {
77 if event.dependencies.contains(¤t) {
78 let deg = in_degree.entry(event.id.clone()).or_insert(1);
79 *deg = deg.saturating_sub(1);
80 if *deg == 0 {
81 queue.push(event.id.clone());
82 }
83 }
84 }
85 }
86
87 if sorted.len() != self.events.len() {
88 return Err(PrivacyError::PosetConflict(
89 "Cycle detected in poset".into()
90 ));
91 }
92
93 self.order = sorted.clone();
94 Ok(sorted)
95 }
96
97 pub fn prove_merge(
99 &self,
100 event_a: &str,
101 event_b: &str,
102 chaos_seed: &[u8; 32],
103 ) -> Result<PrivacyProof, PrivacyError> {
104 let ea = self.events.get(event_a)
105 .ok_or_else(|| PrivacyError::PosetConflict(format!("Event not found: {event_a}")))?;
106 let eb = self.events.get(event_b)
107 .ok_or_else(|| PrivacyError::PosetConflict(format!("Event not found: {event_b}")))?;
108
109 let mut hasher = Sha256::new();
110 hasher.update(ea.payload_hash.as_bytes());
111 hasher.update(eb.payload_hash.as_bytes());
112 hasher.update(chaos_seed);
113 hasher.update(b"zk-merge-v1");
114 let commitment: [u8; 32] = hasher.finalize().into();
115
116 Ok(PrivacyProof {
117 proof_bytes: hex::encode(commitment),
118 public_inputs: hex::encode(Sha256::digest(event_a.as_bytes())),
119 scheme: ProofScheme::Snark,
120 security_bits: 128,
121 proof_size: 64,
122 chsh_value: 0.0,
123 lyapunov: 4.5,
124 })
125 }
126
127 pub fn anchor_to_tuplechain(&self, chaos_seed: &[u8; 32]) -> String {
129 let mut hasher = Sha256::new();
130 for id in &self.order {
131 hasher.update(id.as_bytes());
132 }
133 hasher.update(chaos_seed);
134 hasher.update(b"tuplechain-anchor-v1");
135 hex::encode(hasher.finalize())
136 }
137
138 pub fn event_count(&self) -> usize {
139 self.events.len()
140 }
141
142 fn chaos_hash(&self, id: &str, chaos_seed: &[u8; 32]) -> Vec<u8> {
143 let mut hasher = Sha256::new();
144 hasher.update(id.as_bytes());
145 hasher.update(chaos_seed);
146 hasher.finalize().to_vec()
147 }
148}
149
150impl Default for ChronosyncEngine {
151 fn default() -> Self {
152 Self::new()
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_resolve_linear() {
162 let mut engine = ChronosyncEngine::new();
163 let seed = [0u8; 32];
164 engine.add_event(PosetEvent {
165 id: "e1".into(), dependencies: vec![],
166 payload_hash: "h1".into(), timestamp_ms: 1, zk_merge: None,
167 });
168 engine.add_event(PosetEvent {
169 id: "e2".into(), dependencies: vec!["e1".into()],
170 payload_hash: "h2".into(), timestamp_ms: 2, zk_merge: None,
171 });
172 let order = engine.resolve(&seed).unwrap();
173 assert_eq!(order[0], "e1");
174 assert_eq!(order[1], "e2");
175 }
176
177 #[test]
178 fn test_anchor() {
179 let mut engine = ChronosyncEngine::new();
180 let seed = [0u8; 32];
181 engine.add_event(PosetEvent {
182 id: "e1".into(), dependencies: vec![],
183 payload_hash: "h1".into(), timestamp_ms: 1, zk_merge: None,
184 });
185 engine.resolve(&seed).unwrap();
186 let anchor = engine.anchor_to_tuplechain(&seed);
187 assert_eq!(anchor.len(), 64);
188 }
189}