#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use super::*;
use crate::checkpoint::verify_checkpoint;
use crate::error::TrustError;
use crate::keys::Ed25519Identity;
use crate::merkle::{AppendOnlyTree, RawLeaf};
const GOLDEN_SEED: &[u8; 32] = b"lys-go-conformance-test-seed-01!";
const GOLDEN_ORIGIN: &str = "example.com/lys/test";
const GOLDEN_NOTE_SIZE_3: &str = "example.com/lys/test\n3\nz3Y6BByBzu8VeKYIP3XGG+8uABTyo+aDqX/Pylvn8Zo=\n\n\u{2014} example.com/lys/test UlgM2S4MVZwL9PUGADbPhidG6yKCC0hCE+sx7iXFboC6/rex00vtEy4d33ODa1g0afYmx36opQUAXnwdUl9E7eE28QU=\n";
fn golden_identity() -> (tempfile::TempDir, Ed25519Identity) {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("golden.key");
std::fs::write(&path, GOLDEN_SEED).unwrap();
let identity = Ed25519Identity::load(&path).unwrap();
(dir, identity)
}
fn golden_tree() -> AppendOnlyTree<RawLeaf> {
AppendOnlyTree::<RawLeaf>::reconstruct_from_raw_leaves([b"leaf-0", b"leaf-1", b"leaf-2"])
}
fn golden_prefix_tree() -> AppendOnlyTree<RawLeaf> {
AppendOnlyTree::<RawLeaf>::reconstruct_from_raw_leaves([b"leaf-0", b"leaf-1"])
}
fn golden_verifier() -> crate::checkpoint::NoteVerifierKey {
let (_dir, identity) = golden_identity();
crate::checkpoint::NoteVerifierKey::new(GOLDEN_ORIGIN, identity.public_key_bytes()).unwrap()
}
#[test]
fn inclusion_artifact_matches_golden_vectors() {
let (_dir, identity) = golden_identity();
let tree = golden_tree();
let artifact = build_inclusion_artifact(&tree, b"leaf-1", GOLDEN_ORIGIN, &identity, 1).unwrap();
assert_eq!(artifact.format, INCLUSION_PROOF_FORMAT);
assert_eq!(artifact.tree_size, 3);
assert_eq!(artifact.leaf_index, 1);
assert_eq!(
artifact.hashes,
vec![
"MF31n5WQw8msY9KydDw4jjeSRJB4zr9/s9vmRxZDsrc=".to_string(),
"/KifV8n4yOtAR6f/nTM6z54PM4SyCyVbzqsPIW3Momc=".to_string(),
]
);
assert_eq!(artifact.checkpoint, GOLDEN_NOTE_SIZE_3);
}
#[test]
fn consistency_artifact_matches_golden_vectors() {
let (_dir, identity) = golden_identity();
let artifact = build_consistency_artifact(
&golden_prefix_tree(),
&golden_tree(),
GOLDEN_ORIGIN,
&identity,
)
.unwrap();
assert_eq!(artifact.format, CONSISTENCY_PROOF_FORMAT);
assert_eq!(artifact.tree_size_1, 2);
assert_eq!(artifact.tree_size_2, 3);
assert_eq!(
artifact.hashes,
vec!["/KifV8n4yOtAR6f/nTM6z54PM4SyCyVbzqsPIW3Momc=".to_string()]
);
assert_eq!(artifact.checkpoint_2, GOLDEN_NOTE_SIZE_3);
let body_1 = verify_checkpoint(artifact.checkpoint_1.as_bytes(), &golden_verifier()).unwrap();
assert_eq!(body_1.tree_size(), 2);
assert_eq!(
crate::hex_lower(&body_1.root_hash()),
"60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc"
);
}
#[test]
fn built_artifacts_self_verify_and_verify_externally() {
let (_dir, identity) = golden_identity();
let tree = golden_tree();
let verifier = golden_verifier();
let inclusion =
build_inclusion_artifact(&tree, b"leaf-1", GOLDEN_ORIGIN, &identity, 1).unwrap();
let body = verify_inclusion_artifact(&inclusion, b"leaf-1", &verifier).unwrap();
assert_eq!(body.tree_size(), 3);
let consistency =
build_consistency_artifact(&golden_prefix_tree(), &tree, GOLDEN_ORIGIN, &identity).unwrap();
let (body_1, body_2) = verify_consistency_artifact(&consistency, &verifier).unwrap();
assert_eq!(body_1.tree_size(), 2);
assert_eq!(body_2.tree_size(), 3);
}
#[test]
fn inclusion_build_rejects_out_of_range_index_with_actionable_error() {
let (_dir, identity) = golden_identity();
let tree = golden_tree();
let err = build_inclusion_artifact(&tree, b"leaf-3", GOLDEN_ORIGIN, &identity, 3).unwrap_err();
assert!(matches!(err, TrustError::MerkleTree { .. }));
}
#[test]
fn inclusion_build_rejects_mismatched_leaf_bytes_via_self_verification() {
let (_dir, identity) = golden_identity();
let tree = golden_tree();
let err = build_inclusion_artifact(&tree, b"leaf-0", GOLDEN_ORIGIN, &identity, 1).unwrap_err();
assert!(matches!(err, TrustError::LogArtifactEncoding { .. }));
}
#[test]
fn inclusion_build_rejects_invalid_origin() {
let (_dir, identity) = golden_identity();
let tree = golden_tree();
let err = build_inclusion_artifact(&tree, b"leaf-1", "bad origin", &identity, 1).unwrap_err();
assert!(matches!(err, TrustError::CheckpointEncoding { .. }));
}
#[test]
fn consistency_build_rejects_size_invariant_violations() {
let (_dir, identity) = golden_identity();
let tree = golden_tree();
let empty = AppendOnlyTree::<RawLeaf>::new();
let err = build_consistency_artifact(&empty, &tree, GOLDEN_ORIGIN, &identity).unwrap_err();
assert!(matches!(err, TrustError::LogArtifactEncoding { .. }));
let same = golden_tree();
let err = build_consistency_artifact(&same, &tree, GOLDEN_ORIGIN, &identity).unwrap_err();
assert!(matches!(err, TrustError::LogArtifactEncoding { .. }));
let err = build_consistency_artifact(&tree, &golden_prefix_tree(), GOLDEN_ORIGIN, &identity)
.unwrap_err();
assert!(matches!(err, TrustError::LogArtifactEncoding { .. }));
}
#[test]
fn consistency_build_rejects_non_prefix_old_tree_via_self_verification() {
let (_dir, identity) = golden_identity();
let unrelated =
AppendOnlyTree::<RawLeaf>::reconstruct_from_raw_leaves([b"other-0", b"other-1"]);
let err = build_consistency_artifact(&unrelated, &golden_tree(), GOLDEN_ORIGIN, &identity)
.unwrap_err();
assert!(matches!(err, TrustError::LogArtifactEncoding { .. }));
}
#[test]
fn builders_run_the_json_safety_guard_first() {
let (_dir, identity) = golden_identity();
let tree = golden_tree();
build_inclusion_artifact(&tree, b"leaf-0", GOLDEN_ORIGIN, &identity, 0).unwrap();
}