use serde::{Deserialize, Serialize};
pub mod merkle;
pub mod verify;
pub const CHECKPOINT_PATH: &str = "/v1/audit/checkpoint";
pub const ENTRIES_PATH: &str = "/v1/audit/entries";
pub const CONSISTENCY_PATH: &str = "/v1/audit/consistency";
pub const MAX_PAGE: u32 = 1000;
pub const MAX_PAGE_BYTES: usize = 2 << 20;
pub const LEAF_VERSION: u32 = 1;
pub const CHECKPOINT_HEADER: &str = "recall-audit-checkpoint";
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditCheckpoint {
pub tree_size: u64,
pub root_hash: String,
}
impl AuditCheckpoint {
pub fn to_header_value(&self) -> String {
format!("{} {}", self.tree_size, self.root_hash)
}
pub fn parse_header_value(value: &str) -> Option<Self> {
let (size, root) = value.trim().split_once(' ')?;
Some(Self {
tree_size: size.parse().ok()?,
root_hash: root.to_string(),
})
}
pub fn root(&self) -> Option<merkle::Hash> {
verify::root_hash(&self.root_hash)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditEntriesResponse {
pub start: u64,
pub end: u64,
pub tree_size: u64,
pub entries: Vec<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditConsistencyResponse {
pub first: u64,
pub second: u64,
pub proof: Vec<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditCapability {
pub leaf_version: u32,
pub max_page: u32,
pub max_page_bytes: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_checkpoint_header_round_trips() {
let cp = AuditCheckpoint {
tree_size: 1042,
root_hash: "CsUYapGGPo4dkMgIAUqom/Xajj7h2fB2MPA3j2jxq2I=".to_string(),
};
assert_eq!(
cp.to_header_value(),
"1042 CsUYapGGPo4dkMgIAUqom/Xajj7h2fB2MPA3j2jxq2I="
);
assert_eq!(
AuditCheckpoint::parse_header_value(&cp.to_header_value()),
Some(cp)
);
assert_eq!(
AuditCheckpoint::parse_header_value("not a checkpoint"),
None
);
assert_eq!(AuditCheckpoint::parse_header_value("abc CsUY"), None);
}
}