use super::receipt::ActionReceipt;
use super::verify::verify_receipt;
use crate::error::{IdentityError, Result};
pub fn verify_chain(chain: &[ActionReceipt]) -> Result<bool> {
if chain.is_empty() {
return Ok(true);
}
if chain[0].previous_receipt.is_some() {
}
for i in 0..chain.len() {
let verification = verify_receipt(&chain[i])?;
if !verification.signature_valid {
return Err(IdentityError::InvalidChain);
}
if i > 0 {
let expected_prev = &chain[i - 1].id;
match &chain[i].previous_receipt {
Some(prev) if prev == expected_prev => {}
_ => return Err(IdentityError::InvalidChain),
}
}
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::identity::IdentityAnchor;
use crate::receipt::action::{ActionContent, ActionType};
use crate::receipt::receipt::ReceiptBuilder;
#[test]
fn test_verify_chain_valid() {
let anchor = IdentityAnchor::new(None);
let r1 = ReceiptBuilder::new(
anchor.id(),
ActionType::Observation,
ActionContent::new("step 1"),
)
.sign(anchor.signing_key())
.unwrap();
let r2 = ReceiptBuilder::new(
anchor.id(),
ActionType::Decision,
ActionContent::new("step 2"),
)
.chain_to(r1.id.clone())
.sign(anchor.signing_key())
.unwrap();
let r3 = ReceiptBuilder::new(
anchor.id(),
ActionType::Mutation,
ActionContent::new("step 3"),
)
.chain_to(r2.id.clone())
.sign(anchor.signing_key())
.unwrap();
assert!(verify_chain(&[r1, r2, r3]).is_ok());
}
#[test]
fn test_verify_chain_broken_link() {
let anchor = IdentityAnchor::new(None);
let r1 = ReceiptBuilder::new(
anchor.id(),
ActionType::Observation,
ActionContent::new("step 1"),
)
.sign(anchor.signing_key())
.unwrap();
let r2 = ReceiptBuilder::new(
anchor.id(),
ActionType::Decision,
ActionContent::new("step 2"),
)
.chain_to(r1.id.clone())
.sign(anchor.signing_key())
.unwrap();
let r3 = ReceiptBuilder::new(
anchor.id(),
ActionType::Mutation,
ActionContent::new("step 3"),
)
.chain_to(r1.id.clone())
.sign(anchor.signing_key())
.unwrap();
assert!(verify_chain(&[r1, r2, r3]).is_err());
}
#[test]
fn test_verify_chain_5_receipts() {
let anchor = IdentityAnchor::new(None);
let mut chain = Vec::new();
let r = ReceiptBuilder::new(
anchor.id(),
ActionType::Decision,
ActionContent::new("receipt 0"),
)
.sign(anchor.signing_key())
.unwrap();
chain.push(r);
for i in 1..5 {
let prev_id = chain.last().unwrap().id.clone();
let r = ReceiptBuilder::new(
anchor.id(),
ActionType::Decision,
ActionContent::new(format!("receipt {i}")),
)
.chain_to(prev_id)
.sign(anchor.signing_key())
.unwrap();
chain.push(r);
}
assert!(verify_chain(&chain).is_ok());
}
}