pub mod tree;
pub mod checkpoint;
pub mod proof;
pub use tree::{
MerkleTree, MerkleError, InclusionProof, Direction, ProofStep,
MERKLE_ALGORITHM_V1, MERKLE_ALGORITHM_V2, MERKLE_VERSION_V1, MERKLE_VERSION_V2,
default_merkle_version_v1,
};
pub use checkpoint::{
Checkpoint, CheckpointError,
CANONICAL_VERSION_V1, CANONICAL_VERSION_V2, CANONICAL_VERSION_V3,
};
pub use proof::{ProofFile, ArtifactSummary};
#[cfg(test)]
mod tests {
use super::*;
use crate::attestation::{Ed25519Signer, Signer};
use crate::trust::{encode_ed25519_pubkey, TrustRoot, TrustRootKind, TrustRootStore};
fn trust_for(signer: &Ed25519Signer) -> TrustRootStore {
use ed25519_dalek::VerifyingKey;
let pk_bytes: [u8; 32] = signer.public_key_bytes().try_into().unwrap();
let vk = VerifyingKey::from_bytes(&pk_bytes).unwrap();
TrustRootStore::with_roots(vec![TrustRoot {
key_id: signer.key_id().to_string(),
public_key: encode_ed25519_pubkey(&vk),
kind: TrustRootKind::HubCheckpoint,
label: "test".into(),
added_at: "2026-05-15T00:00:00Z".into(),
}])
}
#[test]
fn checkpoint_signs_and_verifies() {
let mut tree = MerkleTree::new();
tree.append("art_a");
tree.append("art_b");
let signer = Ed25519Signer::generate("key_test").unwrap();
let trust = trust_for(&signer);
let checkpoint = Checkpoint::create(1, &tree, &signer).unwrap();
assert!(checkpoint.verify(&trust));
}
#[test]
fn tampered_checkpoint_fails() {
let mut tree = MerkleTree::new();
tree.append("art_a");
let signer = Ed25519Signer::generate("key_test").unwrap();
let trust = trust_for(&signer);
let mut checkpoint = Checkpoint::create(1, &tree, &signer).unwrap();
checkpoint.tree_size = 999;
assert!(!checkpoint.verify(&trust));
}
#[test]
fn proof_file_round_trips_json() {
let mut tree = MerkleTree::new();
tree.append("art_a");
tree.append("art_b");
let signer = Ed25519Signer::generate("key_test").unwrap();
let checkpoint = Checkpoint::create(1, &tree, &signer).unwrap();
let inclusion_proof = tree.inclusion_proof(1).unwrap();
let file = ProofFile {
artifact_id: "art_b".to_string(),
artifact_summary: ArtifactSummary {
actor: "agent://test".to_string(),
action: "test.run".to_string(),
timestamp: "2026-03-26T00:00:00Z".to_string(),
key_id: "key_test".to_string(),
},
inclusion_proof,
checkpoint,
};
let json = serde_json::to_string(&file).unwrap();
let restored: ProofFile = serde_json::from_str(&json).unwrap();
assert_eq!(restored.artifact_id, "art_b");
}
}