#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use super::*;
use crate::merkle::tree::AppendOnlyTree;
#[test]
fn inclusion_proof_byte_roundtrip_verifies() {
let mut tree = AppendOnlyTree::<u64>::new();
tree.append(10).unwrap();
tree.append(20).unwrap();
tree.append(30).unwrap();
let root = tree.root();
let proof = tree.prove_inclusion(1).unwrap();
let decoded = InclusionProof::try_from_bytes(proof.as_bytes().to_vec()).unwrap();
verify_inclusion(&root, &20u64, 1, &decoded).unwrap();
}
#[test]
fn malformed_inclusion_proof_bytes_return_trust_error() {
let err = InclusionProof::try_from_bytes(vec![1, 2, 3]).unwrap_err();
assert!(matches!(err, TrustError::MerkleTree { .. }));
}
#[test]
fn consistency_proof_byte_roundtrip_verifies() {
let mut tree = AppendOnlyTree::<u64>::new();
tree.append(1).unwrap();
tree.append(2).unwrap();
let old_root = tree.root();
tree.append(3).unwrap();
tree.append(4).unwrap();
let new_root = tree.root();
let proof = tree.prove_consistency(2, 4).unwrap();
let decoded = ConsistencyProof::try_from_bytes(proof.as_bytes().to_vec()).unwrap();
verify_consistency(&old_root, &new_root, &decoded).unwrap();
}
#[test]
fn malformed_consistency_proof_bytes_return_trust_error() {
let err = ConsistencyProof::try_from_bytes(vec![1, 2, 3]).unwrap_err();
assert!(matches!(err, TrustError::MerkleTree { .. }));
}
#[test]
fn root_hash_parts_roundtrip_preserves_equality_and_accessors() {
let mut tree = AppendOnlyTree::<u64>::new();
tree.append(1).unwrap();
tree.append(2).unwrap();
tree.append(3).unwrap();
let root = tree.root();
let (bytes, count) = root.to_parts();
let rebuilt = RootHash::from_parts(bytes, count);
assert_eq!(rebuilt, root);
assert_eq!(bytes.as_slice(), root.as_bytes());
assert_eq!(count, root.num_leaves());
assert_eq!(count, 3);
}
#[test]
fn external_verifier_verifies_proofs_from_published_primitives_only() {
let mut tree = AppendOnlyTree::<u64>::new();
tree.append(10).unwrap();
tree.append(20).unwrap();
let (published_old_bytes, published_old_count) = tree.root().to_parts();
tree.append(30).unwrap();
tree.append(40).unwrap();
let (published_new_bytes, published_new_count) = tree.root().to_parts();
let published_inclusion_bytes = tree.prove_inclusion(2).unwrap().as_bytes().to_vec();
let published_consistency_bytes = tree.prove_consistency(2, 4).unwrap().as_bytes().to_vec();
drop(tree);
let old_root = RootHash::from_parts(published_old_bytes, published_old_count);
let new_root = RootHash::from_parts(published_new_bytes, published_new_count);
let inclusion = InclusionProof::try_from_bytes(published_inclusion_bytes).unwrap();
let consistency = ConsistencyProof::try_from_bytes(published_consistency_bytes).unwrap();
verify_inclusion(&new_root, &30u64, 2, &inclusion).unwrap();
verify_consistency(&old_root, &new_root, &consistency).unwrap();
}
#[test]
fn from_parts_with_wrong_leaf_count_fails_verification() {
let mut tree = AppendOnlyTree::<u64>::new();
tree.append(10).unwrap();
tree.append(20).unwrap();
let (old_bytes, old_count) = tree.root().to_parts();
tree.append(30).unwrap();
tree.append(40).unwrap();
let (new_bytes, new_count) = tree.root().to_parts();
let inclusion = tree.prove_inclusion(2).unwrap();
let consistency = tree.prove_consistency(2, 4).unwrap();
let miscounted_new = RootHash::from_parts(new_bytes, new_count + 1);
assert!(verify_inclusion(&miscounted_new, &30u64, 2, &inclusion).is_err());
let good_old = RootHash::from_parts(old_bytes, old_count);
assert!(verify_consistency(&good_old, &miscounted_new, &consistency).is_err());
let miscounted_old = RootHash::from_parts(old_bytes, old_count + 1);
let good_new = RootHash::from_parts(new_bytes, new_count);
assert!(verify_consistency(&miscounted_old, &good_new, &consistency).is_err());
}
use crate::merkle::leaf::raw_leaf_hash;
use crate::merkle::tree::RawLeaf;
fn raw_tree(leaves: &[&[u8]]) -> AppendOnlyTree<RawLeaf> {
let mut tree = AppendOnlyTree::<RawLeaf>::new();
for leaf in leaves {
tree.append_raw(leaf);
}
tree
}
#[test]
fn verify_inclusion_raw_accepts_correct_leaf_and_rejects_tampering() {
let tree = raw_tree(&[b"leaf-0", b"leaf-1", b"leaf-2"]);
let root = tree.root();
let proof = tree.prove_inclusion(1).unwrap();
verify_inclusion_raw(&root, b"leaf-1", 1, &proof).unwrap();
assert!(verify_inclusion_raw(&root, b"leaf-x", 1, &proof).is_err());
assert!(verify_inclusion_raw(&root, b"leaf-1", 0, &proof).is_err());
assert!(verify_inclusion_raw(&root, b"leaf-1", 2, &proof).is_err());
let other = raw_tree(&[b"leaf-0", b"leaf-x", b"leaf-2"]);
assert!(verify_inclusion_raw(&other.root(), b"leaf-1", 1, &proof).is_err());
}
#[test]
fn verify_inclusion_raw_rejects_postcard_encoding_of_true_leaf() {
let tree = raw_tree(&[b"leaf-0", b"leaf-1", b"leaf-2"]);
let root = tree.root();
let proof = tree.prove_inclusion(0).unwrap();
verify_inclusion_raw(&root, b"leaf-0", 0, &proof).unwrap();
assert!(verify_inclusion_raw(&root, b"\x06leaf-0", 0, &proof).is_err());
}
#[test]
fn interior_node_preimage_does_not_verify_as_a_leaf() {
let tree = raw_tree(&[b"leaf-0", b"leaf-1"]);
let (root2_bytes, _count) = tree.root().to_parts();
let mut forged_leaf = Vec::with_capacity(64);
forged_leaf.extend_from_slice(&raw_leaf_hash(b"leaf-0"));
forged_leaf.extend_from_slice(&raw_leaf_hash(b"leaf-1"));
let empty_proof = InclusionProof::try_from_bytes(Vec::new()).unwrap();
let root_as_single = RootHash::from_parts(root2_bytes, 1);
assert!(verify_inclusion_raw(&root_as_single, &forged_leaf, 0, &empty_proof).is_err());
let root = tree.root();
for index in 0..2 {
let proof = tree.prove_inclusion(index).unwrap();
assert!(verify_inclusion_raw(&root, &forged_leaf, index, &proof).is_err());
}
assert_ne!(root2_bytes, raw_leaf_hash(&forged_leaf));
}
#[test]
fn from_parts_with_wrong_root_bytes_fails_verification() {
let mut tree = AppendOnlyTree::<u64>::new();
tree.append(10).unwrap();
tree.append(20).unwrap();
let (old_bytes, old_count) = tree.root().to_parts();
tree.append(30).unwrap();
tree.append(40).unwrap();
let (new_bytes, new_count) = tree.root().to_parts();
let inclusion = tree.prove_inclusion(2).unwrap();
let consistency = tree.prove_consistency(2, 4).unwrap();
let mut corrupted_new_bytes = new_bytes;
corrupted_new_bytes[0] ^= 0xff;
let corrupted_new = RootHash::from_parts(corrupted_new_bytes, new_count);
assert!(verify_inclusion(&corrupted_new, &30u64, 2, &inclusion).is_err());
let good_old = RootHash::from_parts(old_bytes, old_count);
assert!(verify_consistency(&good_old, &corrupted_new, &consistency).is_err());
let mut corrupted_old_bytes = old_bytes;
corrupted_old_bytes[31] ^= 0x01;
let corrupted_old = RootHash::from_parts(corrupted_old_bytes, old_count);
let good_new = RootHash::from_parts(new_bytes, new_count);
assert!(verify_consistency(&corrupted_old, &good_new, &consistency).is_err());
}