1use thiserror::Error;
2
3use crate::record::AuditRecord;
4
5#[derive(Debug, Error, PartialEq, Eq)]
6pub enum ChainError {
7 #[error("invalid previous hash at index {index}")]
8 InvalidPrevHash { index: usize },
9 #[error("invalid sequence at index {index}: expected {expected}, actual {actual}")]
10 InvalidSequence {
11 index: usize,
12 expected: u64,
13 actual: u64,
14 },
15}
16
17pub fn verify_chain(records: &[AuditRecord]) -> Result<(), ChainError> {
18 if records.is_empty() {
19 return Ok(());
20 }
21
22 for (index, record) in records.iter().enumerate() {
23 if index == 0 {
24 if record.prev_record_hash != AuditRecord::zero_hash() {
25 return Err(ChainError::InvalidPrevHash { index });
26 }
27 continue;
28 }
29
30 let previous = &records[index - 1];
31 if record.prev_record_hash != previous.hash() {
32 return Err(ChainError::InvalidPrevHash { index });
33 }
34
35 let expected_sequence = previous.sequence + 1;
36 if record.sequence != expected_sequence {
37 return Err(ChainError::InvalidSequence {
38 index,
39 expected: expected_sequence,
40 actual: record.sequence,
41 });
42 }
43 }
44
45 Ok(())
46}