1use crate::{encode_str, fingerprint_str, UniversalError};
16use serde::{Deserialize, Serialize};
17use std::time::{SystemTime, UNIX_EPOCH};
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct ChainLink {
22 pub seq: u64,
24 pub d: String,
26 pub f: String,
28 pub prev: Option<String>,
30 pub ts: u64,
32}
33
34impl ChainLink {
35 pub fn verify_data(&self) -> Result<String, UniversalError> {
37 use base64::{engine::general_purpose::STANDARD, Engine};
38 let bytes = STANDARD
39 .decode(&self.d)
40 .map_err(|e| UniversalError::DecodeError(e.to_string()))?;
41 let decoded =
42 String::from_utf8(bytes).map_err(|e| UniversalError::DecodeError(e.to_string()))?;
43 let data_fp = fingerprint_str(&decoded);
44 let actual_fp = match &self.prev {
46 Some(prev) => fingerprint_str(&format!("{}{}", data_fp, prev)),
47 None => data_fp,
48 };
49 if actual_fp != self.f {
50 return Err(UniversalError::IntegrityViolation {
51 expected: self.f.clone(),
52 actual: actual_fp,
53 });
54 }
55 Ok(decoded)
56 }
57}
58
59#[derive(Debug, Clone, PartialEq)]
61pub struct ChainVerifyResult {
62 pub valid: bool,
64 pub total_links: usize,
66 pub broken_at: Option<usize>,
68 pub broken_reason: Option<String>,
70}
71
72impl std::fmt::Display for ChainVerifyResult {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 if self.valid {
75 write!(f, "Valid chain ({} links)", self.total_links)
76 } else {
77 write!(
78 f,
79 "Broken at link {} of {}: {}",
80 self.broken_at.unwrap_or(0),
81 self.total_links,
82 self.broken_reason.as_deref().unwrap_or("unknown")
83 )
84 }
85 }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct Chain {
92 pub links: Vec<ChainLink>,
93}
94
95impl Chain {
96 #[must_use]
98 pub fn new(data: &str) -> Self {
99 let ts = SystemTime::now()
100 .duration_since(UNIX_EPOCH)
101 .unwrap_or_default()
102 .as_secs();
103
104 let link = ChainLink {
105 seq: 1,
106 d: encode_str(data),
107 f: fingerprint_str(data),
108 prev: None,
109 ts,
110 };
111
112 Self { links: vec![link] }
113 }
114
115 pub fn append(&mut self, data: &str) -> &ChainLink {
117 let prev_fp = self.links.last().map(|l| l.f.clone());
118 let seq = self.links.len() as u64 + 1;
119 let ts = SystemTime::now()
120 .duration_since(UNIX_EPOCH)
121 .unwrap_or_default()
122 .as_secs();
123
124 let combined = format!(
127 "{}{}",
128 fingerprint_str(data),
129 prev_fp.as_deref().unwrap_or("")
130 );
131 let chained_fp = fingerprint_str(&combined);
132
133 self.links.push(ChainLink {
134 seq,
135 d: encode_str(data),
136 f: chained_fp,
137 prev: prev_fp,
138 ts,
139 });
140
141 self.links.last().unwrap()
142 }
143
144 pub fn verify(&self) -> ChainVerifyResult {
147 if self.links.is_empty() {
148 return ChainVerifyResult {
149 valid: true,
150 total_links: 0,
151 broken_at: None,
152 broken_reason: None,
153 };
154 }
155
156 for (i, link) in self.links.iter().enumerate() {
157 if let Err(e) = link.verify_data() {
159 return ChainVerifyResult {
160 valid: false,
161 total_links: self.links.len(),
162 broken_at: Some(i + 1),
163 broken_reason: Some(format!("Data integrity: {}", e)),
164 };
165 }
166
167 if i > 0 {
169 let prev_fp = &self.links[i - 1].f;
170 if link.prev.as_deref() != Some(prev_fp.as_str()) {
171 return ChainVerifyResult {
172 valid: false,
173 total_links: self.links.len(),
174 broken_at: Some(i + 1),
175 broken_reason: Some(format!(
176 "Chain broken: link {} doesn't reference link {}",
177 i + 1,
178 i
179 )),
180 };
181 }
182 }
183 }
184
185 ChainVerifyResult {
186 valid: true,
187 total_links: self.links.len(),
188 broken_at: None,
189 broken_reason: None,
190 }
191 }
192
193 pub fn len(&self) -> usize {
195 self.links.len()
196 }
197
198 pub fn is_empty(&self) -> bool {
200 self.links.is_empty()
201 }
202
203 pub fn to_json(&self) -> Result<String, UniversalError> {
205 serde_json::to_string(self).map_err(|e| UniversalError::SerializationError(e.to_string()))
206 }
207
208 pub fn from_json(s: &str) -> Result<Self, UniversalError> {
210 serde_json::from_str(s).map_err(|e| UniversalError::SerializationError(e.to_string()))
211 }
212
213 pub fn report(&self) -> String {
215 let result = self.verify();
216 let mut out = String::new();
217 out.push_str("━━━━ Entrouter Universal Chain Report ━━━━\n");
218 out.push_str(&format!(
219 "Links: {} | Valid: {}\n\n",
220 self.links.len(),
221 result.valid
222 ));
223 for link in &self.links {
224 let status = if result.broken_at == Some(link.seq as usize) {
225 "❌"
226 } else {
227 "✅"
228 };
229 out.push_str(&format!(
230 " Link {}: {} | ts: {} | fp: {}...\n",
231 link.seq,
232 status,
233 link.ts,
234 &link.f[..16]
235 ));
236 }
237 if let Some(reason) = &result.broken_reason {
238 out.push_str(&format!("\n ❌ {}\n", reason));
239 }
240 out.push_str("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
241 out
242 }
243
244 pub fn diff(a: &Chain, b: &Chain) -> ChainDiff {
246 let min_len = a.links.len().min(b.links.len());
247 let mut common_length = 0;
248
249 for i in 0..min_len {
250 if a.links[i].f == b.links[i].f {
251 common_length += 1;
252 } else {
253 return ChainDiff {
254 common_length,
255 a_extra: a.links.len() - common_length,
256 b_extra: b.links.len() - common_length,
257 diverges_at: Some(i + 1), };
259 }
260 }
261
262 ChainDiff {
263 common_length,
264 a_extra: a.links.len() - common_length,
265 b_extra: b.links.len() - common_length,
266 diverges_at: None, }
268 }
269
270 pub fn merge(a: &Chain, b: &Chain) -> Result<Chain, UniversalError> {
273 let diff = Chain::diff(a, b);
274 if let Some(pos) = diff.diverges_at {
275 return Err(UniversalError::ChainMergeConflict { diverges_at: pos });
276 }
277 if a.links.len() >= b.links.len() {
279 Ok(a.clone())
280 } else {
281 Ok(b.clone())
282 }
283 }
284}
285
286#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
288pub struct ChainDiff {
289 pub common_length: usize,
291 pub a_extra: usize,
293 pub b_extra: usize,
295 pub diverges_at: Option<usize>,
297}