use crate::hash;
use crate::log::{MerkleLog, subtree_sizes};
use crate::proof;
use crate::store::{MemStore, Store};
#[test]
fn domain_separation() {
let data = b"hello";
let leaf = hash::hash_leaf(data);
let parent = hash::hash_parent(&[leaf]);
let root = hash::hash_root(&[leaf], 1);
assert_ne!(leaf, parent);
assert_ne!(leaf, root);
assert_ne!(parent, root);
}
#[test]
fn hash_deterministic() {
assert_eq!(hash::hash_leaf(b"x"), hash::hash_leaf(b"x"));
assert_ne!(hash::hash_leaf(b"x"), hash::hash_leaf(b"y"));
}
#[test]
fn hash_leaf_empty() {
let h = hash::hash_leaf(b"");
assert_ne!(h, [0u8; 32]);
assert_eq!(h, hash::hash_leaf(b""));
}
#[test]
fn hash_parent_multiple_children() {
let a = hash::hash_leaf(b"a");
let b = hash::hash_leaf(b"b");
let c = hash::hash_leaf(b"c");
let d = hash::hash_leaf(b"d");
let h = hash::hash_parent(&[a, b, c, d]);
assert_ne!(h, hash::hash_parent(&[d, c, b, a]));
}
#[test]
fn hash_root_multiple_roots() {
let a = hash::hash_leaf(b"a");
let b = hash::hash_leaf(b"b");
let h1 = hash::hash_root(&[a, b], 2);
let h2 = hash::hash_root(&[a, b], 3);
assert_ne!(h1, h2, "different lengths must produce different root hashes");
}
#[test]
fn memstore_put_get() {
let mut store = MemStore::new();
let hash = [42u8; 32];
store.put(hash, b"data").unwrap();
assert_eq!(store.get(hash).unwrap(), b"data");
assert!(store.get([0u8; 32]).is_err());
}
#[test]
fn memstore_len_and_is_empty() {
let mut store = MemStore::new();
assert!(store.is_empty());
assert_eq!(store.len(), 0);
store.put([1u8; 32], b"x").unwrap();
assert!(!store.is_empty());
assert_eq!(store.len(), 1);
store.put([2u8; 32], b"y").unwrap();
assert_eq!(store.len(), 2);
}
#[test]
fn memstore_not_found_display() {
let err = crate::store::NotFound;
assert_eq!(format!("{err}"), "not found");
}
#[test]
fn memstore_put_idempotent() {
let mut store = MemStore::new();
let hash = [99u8; 32];
store.put(hash, b"first").unwrap();
store.put(hash, b"second").unwrap();
assert_eq!(store.get(hash).unwrap(), b"first");
assert_eq!(store.len(), 1);
}
#[test]
fn extract_siblings_basic() {
let children: Vec<[u8; 32]> = (0..4u8).map(|i| [i; 32]).collect();
let sibs = proof::extract_siblings(&children, 1);
assert_eq!(sibs.len(), 3);
assert_eq!(sibs[0], [0u8; 32]);
assert_eq!(sibs[1], [2u8; 32]);
assert_eq!(sibs[2], [3u8; 32]);
}
#[test]
fn reconstruct_children_basic() {
let siblings = vec![[0u8; 32], [2u8; 32], [3u8; 32]];
let entry = [1u8; 32];
let children = proof::reconstruct_children::<4>(&siblings, entry, 1);
assert_eq!(children.len(), 4);
assert_eq!(children[0], [0u8; 32]);
assert_eq!(children[1], [1u8; 32]);
assert_eq!(children[2], [2u8; 32]);
assert_eq!(children[3], [3u8; 32]);
}
#[test]
fn extract_then_reconstruct_roundtrip() {
let children: Vec<[u8; 32]> = (0..4u8).map(|i| [i; 32]).collect();
for pos in 0..4 {
let sibs = proof::extract_siblings(&children, pos);
let rebuilt = proof::reconstruct_children::<4>(&sibs, children[pos], pos);
assert_eq!(rebuilt, children, "roundtrip failed for position {pos}");
}
}
#[test]
fn verify_wrong_sibling_count() {
let bad = proof::Proof {
levels: vec![proof::ProofLevel {
siblings: vec![[0u8; 32]], position: 0,
}],
};
assert!(!proof::verify::<4>(&bad, [0u8; 32], [0u8; 32]));
}
#[test]
fn verify_position_out_of_range() {
let bad = proof::Proof {
levels: vec![proof::ProofLevel {
siblings: vec![[0u8; 32]; 3],
position: 4, }],
};
assert!(!proof::verify::<4>(&bad, [0u8; 32], [0u8; 32]));
}
#[test]
fn subtree_sizes_correctness() {
assert_eq!(subtree_sizes::<4>(16), vec![16]);
assert_eq!(subtree_sizes::<4>(6), vec![4, 1, 1]);
assert_eq!(subtree_sizes::<4>(5), vec![4, 1]);
assert_eq!(subtree_sizes::<4>(21), vec![16, 4, 1]);
}
#[test]
fn subtree_sizes_zero() {
assert!(subtree_sizes::<4>(0).is_empty());
assert!(subtree_sizes::<16>(0).is_empty());
}
#[test]
fn subtree_sizes_one() {
assert_eq!(subtree_sizes::<4>(1), vec![1]);
assert_eq!(subtree_sizes::<16>(1), vec![1]);
}
#[test]
fn empty_log() {
let log = MerkleLog::<MemStore, 16>::new(MemStore::new());
assert_eq!(log.length(), 0);
assert!(log.roots().is_empty());
}
#[test]
fn length_and_roots_accessors() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
assert_eq!(log.length(), 0);
assert_eq!(log.roots().len(), 0);
log.append(b"a").unwrap();
assert_eq!(log.length(), 1);
assert_eq!(log.roots().len(), 1);
for i in 1..4u8 {
log.append(&[i]).unwrap();
}
assert_eq!(log.length(), 4);
assert_eq!(log.roots().len(), 1);
log.append(b"e").unwrap();
assert_eq!(log.length(), 5);
assert_eq!(log.roots().len(), 2);
}
#[test]
fn store_and_store_mut_accessors() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
assert!(log.store().is_empty());
log.append(b"hello").unwrap();
assert!(!log.store().is_empty());
assert_eq!(log.store().len(), 1);
let s = log.store_mut();
assert!(!s.is_empty());
}
#[test]
fn append_single() {
let mut log = MerkleLog::<MemStore, 16>::new(MemStore::new());
let h = log.append(b"hello").unwrap();
assert_eq!(log.length(), 1);
assert_eq!(log.roots().len(), 1);
assert_eq!(log.roots()[0], h);
}
#[test]
fn append_b_entries_single_root() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
for i in 0..4u8 {
log.append(&[i]).unwrap();
}
assert_eq!(log.length(), 4);
assert_eq!(log.roots().len(), 1);
}
#[test]
fn append_b_plus_one_two_roots() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
for i in 0..5u8 {
log.append(&[i]).unwrap();
}
assert_eq!(log.length(), 5);
assert_eq!(log.roots().len(), 2);
}
#[test]
fn append_b_squared_single_root() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
for i in 0..16u8 {
log.append(&[i]).unwrap();
}
assert_eq!(log.length(), 16);
assert_eq!(log.roots().len(), 1);
}
#[test]
fn append_256_with_b16_single_root() {
let mut log = MerkleLog::<MemStore, 16>::new(MemStore::new());
for i in 0..256u32 {
log.append(&i.to_le_bytes()).unwrap();
}
assert_eq!(log.length(), 256);
assert_eq!(log.roots().len(), 1);
}
#[test]
fn deterministic_roots() {
let mut log1 = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut log2 = MerkleLog::<MemStore, 4>::new(MemStore::new());
for i in 0..10u8 {
log1.append(&[i]).unwrap();
log2.append(&[i]).unwrap();
}
assert_eq!(log1.roots(), log2.roots());
assert_eq!(log1.signable(), log2.signable());
}
#[test]
fn different_data_different_roots() {
let mut log1 = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut log2 = MerkleLog::<MemStore, 4>::new(MemStore::new());
log1.append(b"a").unwrap();
log2.append(b"b").unwrap();
assert_ne!(log1.signable(), log2.signable());
}
#[test]
fn signable_changes_with_append() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
log.append(b"first").unwrap();
let s1 = log.signable();
log.append(b"second").unwrap();
let s2 = log.signable();
assert_ne!(s1, s2);
}
#[test]
fn empty_signable() {
let log = MerkleLog::<MemStore, 16>::new(MemStore::new());
let s = log.signable();
assert_eq!(s, hash::hash_root(&[], 0));
}
#[test]
fn binary_tree() {
let mut log = MerkleLog::<MemStore, 2>::new(MemStore::new());
for i in 0..8u8 {
log.append(&[i]).unwrap();
}
assert_eq!(log.length(), 8);
assert_eq!(log.roots().len(), 1);
}
#[test]
#[should_panic(expected = "branching factor must be at least 2")]
fn new_panics_on_b_one() {
MerkleLog::<MemStore, 1>::new(MemStore::new());
}
#[test]
#[should_panic(expected = "branching factor must be a power of 2")]
fn new_panics_on_non_power_of_two() {
MerkleLog::<MemStore, 3>::new(MemStore::new());
}
#[test]
fn save_load_roundtrip() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
for i in 0..10u8 {
log.append(&[i]).unwrap();
}
let saved = log.save();
let store = log.store().clone();
let restored = MerkleLog::<MemStore, 4>::load(store, &saved).unwrap();
assert_eq!(restored.length(), log.length());
assert_eq!(restored.roots(), log.roots());
assert_eq!(restored.signable(), log.signable());
}
#[test]
fn append_after_load() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
for i in 0..6u8 {
log.append(&[i]).unwrap();
}
let saved = log.save();
let store = log.store().clone();
let mut restored = MerkleLog::<MemStore, 4>::load(store, &saved).unwrap();
for i in 6..10u8 {
restored.append(&[i]).unwrap();
}
let mut fresh = MerkleLog::<MemStore, 4>::new(MemStore::new());
for i in 0..10u8 {
fresh.append(&[i]).unwrap();
}
assert_eq!(restored.signable(), fresh.signable());
}
#[test]
fn load_rejects_bad_input() {
assert!(MerkleLog::<MemStore, 4>::load(MemStore::new(), &[]).is_none());
assert!(MerkleLog::<MemStore, 4>::load(MemStore::new(), &[0; 8]).is_none());
let mut bad = vec![0u8; 12];
bad[8] = 1;
assert!(MerkleLog::<MemStore, 4>::load(MemStore::new(), &bad).is_none());
}
#[test]
fn prove_single_entry() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let h = log.append(b"only").unwrap();
let p = log.prove(0).unwrap();
assert!(p.levels.is_empty());
assert!(proof::verify::<4>(&p, h, log.roots()[0]));
}
#[test]
fn prove_and_verify_complete_tree() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut hashes = Vec::new();
for i in 0..4u8 {
hashes.push(log.append(&[i]).unwrap());
}
let root = log.roots()[0];
for (i, &h) in hashes.iter().enumerate() {
let p = log.prove(i as u64).unwrap();
assert!(proof::verify::<4>(&p, h, root), "proof failed for index {i}");
}
}
#[test]
fn prove_and_verify_two_level_tree() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut hashes = Vec::new();
for i in 0..16u8 {
hashes.push(log.append(&[i]).unwrap());
}
let root = log.roots()[0];
for (i, &h) in hashes.iter().enumerate() {
let p = log.prove(i as u64).unwrap();
assert!(proof::verify::<4>(&p, h, root), "proof failed for index {i}");
}
}
#[test]
fn prove_incomplete_tree() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut hashes = Vec::new();
for i in 0..6u8 {
hashes.push(log.append(&[i]).unwrap());
}
let sizes = subtree_sizes::<4>(6);
assert_eq!(sizes, vec![4, 1, 1]);
assert_eq!(log.roots().len(), 3);
for i in 0..4 {
let p = log.prove(i).unwrap();
assert!(proof::verify::<4>(&p, hashes[i as usize], log.roots()[0]));
}
let p4 = log.prove(4).unwrap();
assert!(proof::verify::<4>(&p4, hashes[4], log.roots()[1]));
let p5 = log.prove(5).unwrap();
assert!(proof::verify::<4>(&p5, hashes[5], log.roots()[2]));
}
#[test]
fn invalid_proof_rejected() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
for i in 0..4u8 {
log.append(&[i]).unwrap();
}
let root = log.roots()[0];
let h = hash::hash_leaf(&[0]);
let p = log.prove(0).unwrap();
let mut bad_proof = p.clone();
bad_proof.levels[0].siblings[0] = [0xFF; 32];
assert!(!proof::verify::<4>(&bad_proof, h, root));
assert!(!proof::verify::<4>(&p, [0xAA; 32], root));
}
#[test]
#[should_panic(expected = "out of range")]
fn prove_out_of_range_panics() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
log.append(b"one").unwrap();
let _ = log.prove(1);
}
use crate::diff::{self, NodeBlock};
#[test]
fn diff_empty_log() {
let log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let nodes = diff::collect_needed_nodes(&log, |_| false).unwrap();
assert!(nodes.is_empty());
assert!(diff::verify_partial::<4>(
log.signable(), log.roots(), log.length(), &nodes, |_| None,
));
}
#[test]
fn diff_single_entry_subscriber_has_it() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let h = log.append(b"hello").unwrap();
let nodes = diff::collect_needed_nodes(&log, |_| true).unwrap();
assert!(nodes.is_empty());
assert!(diff::verify_partial::<4>(
log.signable(), log.roots(), log.length(), &nodes, |seq| {
if seq == 0 { Some(h) } else { None }
},
));
}
#[test]
fn diff_single_entry_subscriber_missing() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
log.append(b"hello").unwrap();
let nodes = diff::collect_needed_nodes(&log, |_| false).unwrap();
assert!(nodes.is_empty());
}
#[test]
fn diff_complete_tree_all_missing() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut leaf_hashes = Vec::new();
for i in 0..4u8 {
leaf_hashes.push(log.append(&[i]).unwrap());
}
let nodes = diff::collect_needed_nodes(&log, |_| false).unwrap();
assert_eq!(nodes.len(), 1);
assert_eq!(nodes[0].children.len(), 4);
assert!(diff::verify_partial::<4>(
log.signable(), log.roots(), log.length(), &nodes,
|seq| Some(leaf_hashes[seq as usize]),
));
}
#[test]
fn diff_complete_tree_partial_missing() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut leaf_hashes = Vec::new();
for i in 0..4u8 {
leaf_hashes.push(log.append(&[i]).unwrap());
}
let nodes = diff::collect_needed_nodes(&log, |seq| seq < 2).unwrap();
assert_eq!(nodes.len(), 1);
assert!(diff::verify_partial::<4>(
log.signable(), log.roots(), log.length(), &nodes,
|seq| Some(leaf_hashes[seq as usize]),
));
}
#[test]
fn diff_two_level_tree() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut leaf_hashes = Vec::new();
for i in 0..16u8 {
leaf_hashes.push(log.append(&[i]).unwrap());
}
assert_eq!(log.roots().len(), 1);
let nodes = diff::collect_needed_nodes(&log, |seq| seq != 5).unwrap();
assert_eq!(nodes.len(), 2);
assert_eq!(nodes[0].hash(), log.roots()[0]);
assert!(diff::verify_partial::<4>(
log.signable(), log.roots(), log.length(), &nodes,
|seq| Some(leaf_hashes[seq as usize]),
));
}
#[test]
fn diff_subscriber_has_everything() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut leaf_hashes = Vec::new();
for i in 0..16u8 {
leaf_hashes.push(log.append(&[i]).unwrap());
}
let nodes = diff::collect_needed_nodes(&log, |_| true).unwrap();
assert!(nodes.is_empty());
assert!(diff::verify_partial::<4>(
log.signable(), log.roots(), log.length(), &nodes,
|seq| Some(leaf_hashes[seq as usize]),
));
}
#[test]
fn diff_multiple_subtrees() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut leaf_hashes = Vec::new();
for i in 0..5u8 {
leaf_hashes.push(log.append(&[i]).unwrap());
}
assert_eq!(log.roots().len(), 2);
let nodes = diff::collect_needed_nodes(&log, |seq| seq != 2).unwrap();
assert_eq!(nodes.len(), 1);
assert!(diff::verify_partial::<4>(
log.signable(), log.roots(), log.length(), &nodes,
|seq| Some(leaf_hashes[seq as usize]),
));
}
#[test]
fn diff_nodeblock_roundtrip() {
let children: Vec<[u8; 32]> = (0..4u8).map(|i| {
let mut h = [0u8; 32];
h[0] = i;
h
}).collect();
let block = NodeBlock { children: children.clone() };
let bytes = block.to_bytes();
assert_eq!(bytes.len(), 4 * 32);
let decoded = NodeBlock::from_bytes(&bytes, 4).unwrap();
assert_eq!(decoded.children, children);
assert_eq!(decoded.hash(), block.hash());
}
#[test]
fn diff_nodeblock_bad_size_rejected() {
assert!(NodeBlock::from_bytes(&[0u8; 31], 4).is_none());
assert!(NodeBlock::from_bytes(&[0u8; 129], 4).is_none());
}
#[test]
fn diff_verify_rejects_bad_signable() {
assert!(!diff::verify_partial::<4>([0xFF; 32], &[], 0, &[], |_| None));
}
#[test]
fn diff_verify_rejects_tampered_leaf() {
let mut log = MerkleLog::<MemStore, 4>::new(MemStore::new());
let mut leaf_hashes = Vec::new();
for i in 0..4u8 {
leaf_hashes.push(log.append(&[i]).unwrap());
}
let nodes = diff::collect_needed_nodes(&log, |_| false).unwrap();
assert!(!diff::verify_partial::<4>(
log.signable(), log.roots(), log.length(), &nodes,
|seq| {
if seq == 2 { Some([0xFF; 32]) }
else { Some(leaf_hashes[seq as usize]) }
},
));
}
#[test]
fn diff_b16_tree() {
let mut log = MerkleLog::<MemStore, 16>::new(MemStore::new());
let mut leaf_hashes = Vec::new();
for i in 0..16u8 {
leaf_hashes.push(log.append(&[i]).unwrap());
}
assert_eq!(log.roots().len(), 1);
let nodes = diff::collect_needed_nodes(&log, |seq| seq != 7).unwrap();
assert_eq!(nodes.len(), 1);
assert!(diff::verify_partial::<16>(
log.signable(), log.roots(), log.length(), &nodes,
|seq| Some(leaf_hashes[seq as usize]),
));
}
#[test]
fn diff_b16_two_level() {
let mut log = MerkleLog::<MemStore, 16>::new(MemStore::new());
let mut leaf_hashes = Vec::new();
for i in 0..256u16 {
leaf_hashes.push(log.append(&i.to_le_bytes()).unwrap());
}
assert_eq!(log.roots().len(), 1);
let nodes = diff::collect_needed_nodes(&log, |seq| !(100..104).contains(&seq)).unwrap();
assert_eq!(nodes.len(), 2);
assert!(diff::verify_partial::<16>(
log.signable(), log.roots(), log.length(), &nodes,
|seq| Some(leaf_hashes[seq as usize]),
));
}