mod common;
use common::{build_log, SimpleHasher};
use malt::{verify_consistency, verify_inclusion, Log, TreeHasher};
#[test]
fn i_sound_inclusion_proofs_verify() {
for n in 1u64..=17 {
let log = build_log(n);
let root = log.root();
for m in 0..n {
let proof = log.inclusion_proof(m).expect("proof generation failed");
let leaf_hash = SimpleHasher.leaf(format!("leaf-{m}").as_bytes());
assert!(
verify_inclusion(&SimpleHasher, &leaf_hash, &proof, &root),
"I-SOUND failed: n={n}, m={m}"
);
}
}
}
#[test]
fn k_sound_consistency_proofs_verify() {
for n in 2u64..=17 {
let log = build_log(n);
let new_root = log.root();
for m in 1..n {
let proof = log.consistency_proof(m).expect("proof generation failed");
let old_log = build_log(m);
let old_root = old_log.root();
assert!(
verify_consistency(&SimpleHasher, &proof, &old_root, &new_root),
"K-SOUND failed: m={m}, n={n}"
);
}
}
}
#[test]
fn inclusion_rejects_wrong_leaf() {
let log = build_log(8);
let root = log.root();
let proof = log.inclusion_proof(3).unwrap();
let wrong_hash = SimpleHasher.leaf(b"not-the-right-leaf");
assert!(
!verify_inclusion(&SimpleHasher, &wrong_hash, &proof, &root),
"should reject wrong leaf hash"
);
}
#[test]
fn inclusion_rejects_wrong_root() {
let log = build_log(8);
let proof = log.inclusion_proof(3).unwrap();
let leaf_hash = SimpleHasher.leaf(b"leaf-3");
let wrong_root = SimpleHasher.leaf(b"wrong-root");
assert!(
!verify_inclusion(&SimpleHasher, &leaf_hash, &proof, &wrong_root),
"should reject wrong root"
);
}
#[test]
fn consistency_rejects_wrong_old_root() {
let log = build_log(8);
let new_root = log.root();
let proof = log.consistency_proof(4).unwrap();
let wrong_old_root = SimpleHasher.leaf(b"wrong-old-root");
assert!(
!verify_consistency(&SimpleHasher, &proof, &wrong_old_root, &new_root),
"should reject wrong old root"
);
}
#[test]
fn consistency_rejects_wrong_new_root() {
let log = build_log(8);
let proof = log.consistency_proof(4).unwrap();
let old_log = build_log(4);
let old_root = old_log.root();
let wrong_new_root = SimpleHasher.leaf(b"wrong-new-root");
assert!(
!verify_consistency(&SimpleHasher, &proof, &old_root, &wrong_new_root),
"should reject wrong new root"
);
}
#[test]
fn inclusion_proof_size_bounded() {
for n in 1u64..=33 {
let log = build_log(n);
let max_path_len = ceil_log2(n);
for m in 0..n {
let proof = log.inclusion_proof(m).unwrap();
assert!(
proof.path.len() <= max_path_len,
"I-SIZE: n={n}, m={m}, path_len={}, max={}",
proof.path.len(),
max_path_len
);
}
}
}
#[test]
fn consistency_proof_size_bounded() {
for n in 2u64..=33 {
let log = build_log(n);
let max_path_len = ceil_log2(n) + 1;
for m in 1..n {
let proof = log.consistency_proof(m).unwrap();
assert!(
proof.path.len() <= max_path_len,
"K-SIZE: m={m}, n={n}, path_len={}, max={}",
proof.path.len(),
max_path_len
);
}
}
}
#[test]
fn inclusion_proof_empty_tree() {
let log = Log::new(SimpleHasher);
assert!(log.inclusion_proof(0).is_err());
}
#[test]
fn inclusion_proof_out_of_bounds() {
let log = build_log(5);
assert!(log.inclusion_proof(5).is_err());
assert!(log.inclusion_proof(100).is_err());
}
#[test]
fn consistency_proof_empty_tree() {
let log = Log::new(SimpleHasher);
assert!(log.consistency_proof(0).is_err());
}
#[test]
fn consistency_proof_old_size_zero() {
let log = build_log(5);
assert!(log.consistency_proof(0).is_err());
}
#[test]
fn consistency_proof_old_size_ge_new_size() {
let log = build_log(5);
assert!(log.consistency_proof(5).is_err());
assert!(log.consistency_proof(6).is_err());
}
#[test]
fn verify_inclusion_rejects_bad_index() {
use malt::InclusionProof;
let proof = InclusionProof {
index: 5,
tree_size: 5,
path: vec![],
};
let leaf_hash = SimpleHasher.leaf(b"x");
let root = SimpleHasher.empty();
assert!(!verify_inclusion(&SimpleHasher, &leaf_hash, &proof, &root));
}
#[test]
fn verify_consistency_rejects_old_size_zero() {
use malt::ConsistencyProof;
let proof = ConsistencyProof {
old_size: 0,
new_size: 5,
path: vec![],
};
let root = SimpleHasher.empty();
assert!(!verify_consistency(&SimpleHasher, &proof, &root, &root));
}
#[test]
fn verify_consistency_rejects_old_ge_new() {
use malt::ConsistencyProof;
let proof = ConsistencyProof {
old_size: 5,
new_size: 5,
path: vec![],
};
let root = SimpleHasher.empty();
assert!(!verify_consistency(&SimpleHasher, &proof, &root, &root));
}
fn ceil_log2(n: u64) -> usize {
if n <= 1 {
return 0;
}
(u64::BITS - (n - 1).leading_zeros()) as usize
}