mirror of
https://github.com/vacp2p/zerokit.git
synced 2026-02-20 03:00:20 -05:00
test: Merkle invalid proof and PM tree coverage tests (#367)
## Description
Add Merkle tree invalid proof error test cases and PM tree coverage
tests.
## Tests added
- test_proof_invalid_index
- test_verify_proof_length_mismatch
- test_verify_tampered_sibling
- test_verify_tampered_direction
- test_verify_mismatched_root
- test_pmtree_config_builder
- test_pmtree_config_from_str
- test_pmtree_config_from_str_invalid
- test_pmtree_tree_creation_default
- test_pmtree_tree_creation_new
- test_pmtree_persistence
- test_pmtree_load_nonexistent
- test_pmtree_basic_operations
- test_pmtree_update_next
- test_pmtree_set_range
- test_pmtree_delete
- test_pmtree_override_range
- test_pmtree_get_empty_leaves_indices
- test_pmtree_proof_and_verify
- test_pmtree_get_subtree_root
- test_pmtree_metadata
- test_pmtree_close_db
- test_pmtree_invalid_index
- test_pmtree_invalid_subtree_root
- test_pmtree_verify_tampered_proof
- test_pmtree_modes
- test_pmtree_compression
- test_pmtree_stress_large
- test_pmtree_full_tree
- test_pmtree_large_batch
- test_pmtree_multiple_reopen
- test_pmtree_depth_extremes
- test_pmtree_compaction
## Issues reported
- https://github.com/vacp2p/zerokit/issues/369
## Coverage changed
Before 69.94%
[tarpaulin-report_c35e62a.html](https://github.com/user-attachments/files/24873294/tarpaulin-report_c35e62a.html)
After 71.09%
[tarpaulin-report-PR367.html](https://github.com/user-attachments/files/24872721/tarpaulin-report-PR367.html)
This commit is contained in:
447
rln/tests/pm_tree.rs
Normal file
447
rln/tests/pm_tree.rs
Normal file
@@ -0,0 +1,447 @@
|
||||
#![cfg(feature = "pmtree-ft")]
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use num_traits::identities::Zero;
|
||||
use rln::pm_tree_adapter::{PmTree, PmTreeProof, PmtreeConfig};
|
||||
use rln::prelude::*;
|
||||
use tempfile::TempDir;
|
||||
use zerokit_utils::merkle_tree::{
|
||||
ZerokitMerkleProof, ZerokitMerkleTree, ZerokitMerkleTreeError,
|
||||
};
|
||||
use zerokit_utils::pm_tree::Mode;
|
||||
|
||||
const TEST_DEPTH: usize = 10;
|
||||
|
||||
fn _default_config() -> PmtreeConfig {
|
||||
PmtreeConfig::default()
|
||||
}
|
||||
|
||||
fn temp_config() -> PmtreeConfig {
|
||||
PmtreeConfig::builder().temporary(true).build().unwrap()
|
||||
}
|
||||
|
||||
fn persistent_config(path: PathBuf) -> PmtreeConfig {
|
||||
PmtreeConfig::builder()
|
||||
.path(path)
|
||||
.temporary(false)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_config_builder() {
|
||||
let config = PmtreeConfig::builder()
|
||||
.temporary(true)
|
||||
.cache_capacity(1 << 30)
|
||||
.flush_every_ms(1000)
|
||||
.mode(Mode::LowSpace)
|
||||
.use_compression(false)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Indirect confirmation: create a tree with the config and verify operations work
|
||||
let mut tree = PmTree::new(TEST_DEPTH, Fr::zero(), config).unwrap();
|
||||
let leaf = Fr::from(42);
|
||||
tree.set(0, leaf).unwrap();
|
||||
assert_eq!(tree.get(0).unwrap(), leaf);
|
||||
assert_eq!(tree.leaves_set(), 1);
|
||||
let root = tree.root();
|
||||
assert_ne!(root, Fr::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_config_from_str() {
|
||||
let json = r#"
|
||||
{
|
||||
"path": "test-path",
|
||||
"temporary": false,
|
||||
"cache_capacity": 1073741824,
|
||||
"flush_every_ms": 500,
|
||||
"mode": "HighThroughput",
|
||||
"use_compression": false
|
||||
}"#;
|
||||
|
||||
let config: PmtreeConfig = json.parse().unwrap();
|
||||
|
||||
// Verify the config by creating a persistent tree
|
||||
let mut tree1 = PmTree::new(TEST_DEPTH, Fr::zero(), config.clone()).unwrap();
|
||||
let leaf = Fr::from(42);
|
||||
tree1.set(0, leaf).unwrap();
|
||||
let root1 = tree1.root();
|
||||
tree1.close_db_connection().unwrap();
|
||||
drop(tree1);
|
||||
|
||||
// Reopen and verify persistence
|
||||
let tree2 = PmTree::new(TEST_DEPTH, Fr::zero(), config).unwrap();
|
||||
assert_eq!(tree2.get(0).unwrap(), leaf);
|
||||
assert_eq!(tree2.root(), root1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_config_from_str_invalid() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let existing_path = temp_dir.path().to_str().unwrap();
|
||||
let invalid_json = format!(r#"{{"temporary": true, "path": "{}"}}"#, existing_path);
|
||||
let result: Result<PmtreeConfig, _> = invalid_json.parse();
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_tree_creation_default() {
|
||||
let tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
assert_eq!(tree.depth(), TEST_DEPTH);
|
||||
assert_eq!(tree.capacity(), 1 << TEST_DEPTH);
|
||||
assert_eq!(tree.leaves_set(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_tree_creation_new() {
|
||||
let config = temp_config();
|
||||
let tree = PmTree::new(TEST_DEPTH, Fr::from(0), config).unwrap();
|
||||
assert_eq!(tree.depth(), TEST_DEPTH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_persistence() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let db_path = temp_dir.path().join("test.db");
|
||||
let config = persistent_config(db_path.clone());
|
||||
|
||||
// Create and populate
|
||||
let mut tree1 = PmTree::new(TEST_DEPTH, Fr::zero(), config.clone()).unwrap();
|
||||
let leaf = Fr::from(42);
|
||||
tree1.update_next(leaf).unwrap();
|
||||
let root1 = tree1.root();
|
||||
tree1.set_metadata(b"test metadata").unwrap();
|
||||
tree1.close_db_connection().unwrap();
|
||||
drop(tree1);
|
||||
|
||||
// Load and verify
|
||||
let tree2 = PmTree::new(TEST_DEPTH, Fr::zero(), config).unwrap();
|
||||
assert_eq!(tree2.root(), root1);
|
||||
assert_eq!(tree2.metadata().unwrap(), b"test metadata");
|
||||
assert_eq!(tree2.leaves_set(), 1);
|
||||
assert_eq!(tree2.get(0).unwrap(), leaf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_load_nonexistent() {
|
||||
let config = persistent_config(PathBuf::from("\0invalid"));
|
||||
let result = PmTree::new(TEST_DEPTH, Fr::zero(), config);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ZerokitMerkleTreeError::PmtreeErrorKind(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_basic_operations() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
let leaf = Fr::from(123);
|
||||
tree.set(5, leaf).unwrap();
|
||||
assert_eq!(tree.get(5).unwrap(), leaf);
|
||||
assert_eq!(tree.leaves_set(), 6); // Next index
|
||||
assert_ne!(tree.root(), Fr::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_update_next() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
for i in 0..5 {
|
||||
tree.update_next(Fr::from(i as u64)).unwrap();
|
||||
}
|
||||
assert_eq!(tree.leaves_set(), 5);
|
||||
for i in 0..5 {
|
||||
assert_eq!(tree.get(i).unwrap(), Fr::from(i as u64));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_set_range() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
let leaves: Vec<Fr> = (0..4).map(|i| Fr::from(i as u64)).collect();
|
||||
tree.set_range(1, leaves.into_iter()).unwrap();
|
||||
assert_eq!(tree.get(1).unwrap(), Fr::from(0));
|
||||
assert_eq!(tree.get(4).unwrap(), Fr::from(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_delete() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
let leaf = Fr::from(99);
|
||||
tree.set(2, leaf).unwrap();
|
||||
assert_eq!(tree.get(2).unwrap(), leaf);
|
||||
tree.delete(2).unwrap();
|
||||
assert_eq!(tree.get(2).unwrap(), Fr::zero()); // Default leaf
|
||||
assert_eq!(tree.leaves_set(), 3); // Unchanged
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_override_range() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
tree.set(0, Fr::from(1)).unwrap();
|
||||
tree.set(1, Fr::from(2)).unwrap();
|
||||
|
||||
// Set new leaves
|
||||
let new_leaves = vec![Fr::from(10), Fr::from(20)];
|
||||
tree.override_range(0, new_leaves.into_iter(), vec![].into_iter())
|
||||
.unwrap();
|
||||
assert_eq!(tree.get(0).unwrap(), Fr::from(10));
|
||||
assert_eq!(tree.get(1).unwrap(), Fr::from(20));
|
||||
|
||||
// Delete indices
|
||||
tree.override_range(0, vec![].into_iter(), vec![0].into_iter())
|
||||
.unwrap();
|
||||
assert_eq!(tree.get(0).unwrap(), Fr::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_get_empty_leaves_indices() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
tree.set(0, Fr::from(1)).unwrap();
|
||||
tree.set(2, Fr::from(3)).unwrap();
|
||||
tree.delete(0).unwrap();
|
||||
let empty = tree.get_empty_leaves_indices();
|
||||
assert!(empty.contains(&0));
|
||||
assert!(empty.contains(&1));
|
||||
assert!(!empty.contains(&2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_proof_and_verify() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
let leaf = Fr::from(42);
|
||||
tree.set(3, leaf).unwrap();
|
||||
let proof: PmTreeProof = tree.proof(3).unwrap();
|
||||
assert_eq!(proof.leaf_index(), 3);
|
||||
assert!(tree.verify(&leaf, &proof).unwrap());
|
||||
assert!(matches!(
|
||||
tree.verify(&Fr::from(43), &proof),
|
||||
Err(ZerokitMerkleTreeError::InvalidMerkleProof)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_get_subtree_root() {
|
||||
let mut tree = PmTree::default(3).unwrap(); // Depth 3 for simplicity
|
||||
tree.set(0, Fr::from(1)).unwrap();
|
||||
tree.set(1, Fr::from(2)).unwrap();
|
||||
// Root is level 0
|
||||
assert_eq!(tree.get_subtree_root(0, 0).unwrap(), tree.root());
|
||||
// Leaf is level 3
|
||||
assert_eq!(tree.get_subtree_root(3, 0).unwrap(), Fr::from(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_metadata() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
let meta = b"hello world";
|
||||
tree.set_metadata(meta).unwrap();
|
||||
assert_eq!(tree.metadata().unwrap(), meta);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_close_db() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
tree.close_db_connection().unwrap();
|
||||
// Verify idempotence: calling close again should succeed
|
||||
tree.close_db_connection().unwrap();
|
||||
// Verify that the tree still works after close (close is a no-op)
|
||||
assert_eq!(tree.get(0).unwrap(), Fr::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_invalid_index() {
|
||||
let tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
let capacity = tree.capacity();
|
||||
assert!(matches!(
|
||||
tree.proof(capacity),
|
||||
Err(ZerokitMerkleTreeError::PmtreeErrorKind(_))
|
||||
));
|
||||
assert!(matches!(
|
||||
tree.get(capacity),
|
||||
Err(ZerokitMerkleTreeError::PmtreeErrorKind(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_invalid_subtree_root() {
|
||||
let tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
assert!(matches!(
|
||||
tree.get_subtree_root(TEST_DEPTH + 1, 0),
|
||||
Err(ZerokitMerkleTreeError::InvalidLevel)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_proof_binds_to_leaf_index_even_if_leaf_value_same() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
|
||||
let leaf = Fr::from(42);
|
||||
tree.set(0, leaf).unwrap();
|
||||
tree.set(1, leaf).unwrap();
|
||||
|
||||
let proof0: PmTreeProof = tree.proof(0).unwrap();
|
||||
let proof1: PmTreeProof = tree.proof(1).unwrap();
|
||||
|
||||
// Both proofs should reconstruct the current root when used with the correct leaf value,
|
||||
// but their *paths/indexes* should differ.
|
||||
let root0 = proof0.compute_root_from(&leaf).unwrap();
|
||||
let root1 = proof1.compute_root_from(&leaf).unwrap();
|
||||
assert_eq!(root0, tree.root());
|
||||
assert_eq!(root1, tree.root());
|
||||
|
||||
// The "index binding" evidence: either leaf_index differs or path_index differs.
|
||||
assert_ne!(proof0.leaf_index(), proof1.leaf_index());
|
||||
assert_ne!(proof0.get_path_index(), proof1.get_path_index());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_modes() {
|
||||
let config_ht = PmtreeConfig::builder()
|
||||
.mode(Mode::HighThroughput)
|
||||
.build()
|
||||
.unwrap();
|
||||
let config_ls = PmtreeConfig::builder()
|
||||
.mode(Mode::LowSpace)
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut tree_ht = PmTree::new(TEST_DEPTH, Fr::zero(), config_ht).unwrap();
|
||||
let mut tree_ls = PmTree::new(TEST_DEPTH, Fr::zero(), config_ls).unwrap();
|
||||
tree_ht.set(0, Fr::from(1)).unwrap();
|
||||
tree_ls.set(0, Fr::from(1)).unwrap();
|
||||
// Roots should be same regardless of mode
|
||||
assert_eq!(tree_ht.root(), tree_ls.root());
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
fn test_pmtree_compression() {
|
||||
let config_comp = PmtreeConfig::builder()
|
||||
.use_compression(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
let config_no_comp = PmtreeConfig::builder()
|
||||
.use_compression(false)
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut tree_comp = PmTree::new(TEST_DEPTH, Fr::zero(), config_comp).unwrap();
|
||||
let mut tree_no_comp = PmTree::new(TEST_DEPTH, Fr::zero(), config_no_comp).unwrap();
|
||||
tree_comp.set(0, Fr::from(1)).unwrap();
|
||||
tree_no_comp.set(0, Fr::from(1)).unwrap();
|
||||
assert_eq!(tree_comp.root(), tree_no_comp.root());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_stress_large() {
|
||||
let mut tree = PmTree::default(15).unwrap(); // Smaller for test
|
||||
for i in 0..100 {
|
||||
tree.update_next(Fr::from(i as u64)).unwrap();
|
||||
}
|
||||
assert_eq!(tree.leaves_set(), 100);
|
||||
let proof = tree.proof(50).unwrap();
|
||||
assert!(tree.verify(&Fr::from(50), &proof).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_full_tree() {
|
||||
let mut tree = PmTree::default(4).unwrap(); // 16 capacity
|
||||
for i in 0..16 {
|
||||
tree.set(i, Fr::from(i as u64)).unwrap();
|
||||
}
|
||||
assert_eq!(tree.leaves_set(), 16);
|
||||
assert_eq!(tree.capacity(), 16);
|
||||
// Try overflow
|
||||
assert!(matches!(
|
||||
tree.update_next(Fr::from(16)),
|
||||
Err(ZerokitMerkleTreeError::PmtreeErrorKind(_))
|
||||
));
|
||||
assert!(matches!(
|
||||
tree.set(16, Fr::from(16)),
|
||||
Err(ZerokitMerkleTreeError::PmtreeErrorKind(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_large_batch() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
let leaves: Vec<Fr> = (0..100).map(|i| Fr::from(i as u64)).collect();
|
||||
tree.set_range(0, leaves.into_iter()).unwrap();
|
||||
assert_eq!(tree.leaves_set(), 100);
|
||||
for i in 0..100 {
|
||||
assert_eq!(tree.get(i).unwrap(), Fr::from(i as u64));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_multiple_reopen() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let db_path = temp_dir.path().join("test.db");
|
||||
let config = persistent_config(db_path);
|
||||
|
||||
// First open: write data, close, and fully drop the tree.
|
||||
{
|
||||
let mut tree1 = PmTree::new(TEST_DEPTH, Fr::zero(), config.clone()).unwrap();
|
||||
tree1.set(0, Fr::from(1)).unwrap();
|
||||
|
||||
// Optional stronger signal than just leaf persistence:
|
||||
assert_ne!(tree1.root(), Fr::zero());
|
||||
|
||||
tree1.close_db_connection().unwrap();
|
||||
}
|
||||
|
||||
// Second open: verify data, close, and drop.
|
||||
{
|
||||
let mut tree2 = PmTree::new(TEST_DEPTH, Fr::zero(), config.clone()).unwrap();
|
||||
assert_eq!(tree2.get(0).unwrap(), Fr::from(1));
|
||||
|
||||
// Optional: verify tree is still non-empty (depending on semantics).
|
||||
assert_ne!(tree2.root(), Fr::zero());
|
||||
|
||||
tree2.close_db_connection().unwrap();
|
||||
}
|
||||
|
||||
// Third open: verify again.
|
||||
{
|
||||
let tree3 = PmTree::new(TEST_DEPTH, Fr::zero(), config).unwrap();
|
||||
assert_eq!(tree3.get(0).unwrap(), Fr::from(1));
|
||||
assert_ne!(tree3.root(), Fr::zero());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_depth_extremes() {
|
||||
// Depth 0 (minimal valid depth)
|
||||
let result = PmTree::default(0);
|
||||
assert!(result.is_ok());
|
||||
if let Ok(tree) = result {
|
||||
assert_eq!(tree.depth(), 0);
|
||||
assert_eq!(tree.capacity(), 1);
|
||||
}
|
||||
// Depth 32
|
||||
let result = PmTree::default(32);
|
||||
if let Ok(tree) = result {
|
||||
assert_eq!(tree.depth(), 32);
|
||||
assert_eq!(tree.capacity(), 1usize << 32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmtree_compaction() {
|
||||
let mut tree = PmTree::default(TEST_DEPTH).unwrap();
|
||||
for i in 0..50 {
|
||||
tree.set(i, Fr::from(i as u64)).unwrap();
|
||||
}
|
||||
assert_eq!(tree.leaves_set(), 50);
|
||||
for i in 0..25 {
|
||||
tree.delete(i).unwrap();
|
||||
}
|
||||
assert_eq!(tree.leaves_set(), 50); // Unchanged
|
||||
let empty = tree.get_empty_leaves_indices();
|
||||
assert_eq!(empty.len(), 25);
|
||||
assert!(empty.iter().all(|&i| i < 25));
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,9 @@ serde_json = "1.0.145"
|
||||
rayon = "1.11.0"
|
||||
thiserror = "2.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
sled = { version = "0.34.7", features = ["compression"] }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4.3"
|
||||
hex-literal = "1.1.0"
|
||||
@@ -43,3 +46,5 @@ harness = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ mod test {
|
||||
error::HashError,
|
||||
merkle_tree::{
|
||||
FullMerkleConfig, FullMerkleTree, Hasher, OptimalMerkleConfig, OptimalMerkleTree,
|
||||
ZerokitMerkleProof, ZerokitMerkleTree, MIN_PARALLEL_NODES,
|
||||
ZerokitMerkleProof, ZerokitMerkleTree, ZerokitMerkleTreeError, MIN_PARALLEL_NODES,
|
||||
},
|
||||
};
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
@@ -479,4 +479,93 @@ mod test {
|
||||
assert_eq!(tree_opt.get(i).unwrap(), leaf);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_proof_invalid_index() {
|
||||
let tree_full = default_full_merkle_tree(DEFAULT_DEPTH);
|
||||
let tree_opt = default_optimal_merkle_tree(DEFAULT_DEPTH);
|
||||
let invalid_index = tree_full.capacity();
|
||||
|
||||
assert!(matches!(
|
||||
tree_full.proof(invalid_index),
|
||||
Err(ZerokitMerkleTreeError::InvalidLeaf)
|
||||
));
|
||||
assert!(matches!(
|
||||
tree_opt.proof(invalid_index),
|
||||
Err(ZerokitMerkleTreeError::InvalidLeaf)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_proof_length_mismatch() {
|
||||
let tree_opt = default_optimal_merkle_tree(DEFAULT_DEPTH);
|
||||
let leaf = TestFr::from(1u32);
|
||||
let proof = tree_opt.proof(0).unwrap();
|
||||
let mut short_proof = proof.clone();
|
||||
short_proof.0.truncate(proof.length() - 1); // Shorten
|
||||
|
||||
assert!(matches!(
|
||||
tree_opt.verify(&leaf, &short_proof),
|
||||
Err(ZerokitMerkleTreeError::InvalidMerkleProof)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_tampered_sibling() {
|
||||
let nof_leaves = 4;
|
||||
let leaves: Vec<TestFr> = (0..nof_leaves as u32).map(TestFr::from).collect();
|
||||
|
||||
let mut tree_opt = default_optimal_merkle_tree(DEFAULT_DEPTH);
|
||||
tree_opt.set_range(0, leaves.iter().cloned()).unwrap();
|
||||
|
||||
let index = 1;
|
||||
let leaf = leaves[index];
|
||||
let mut proof_opt = tree_opt.proof(index).unwrap();
|
||||
|
||||
// Tamper first sibling
|
||||
proof_opt.0[0].0 = TestFr::from(999u32);
|
||||
|
||||
assert!(!tree_opt.verify(&leaf, &proof_opt).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_tampered_direction() {
|
||||
let nof_leaves = 4;
|
||||
let leaves: Vec<TestFr> = (0..nof_leaves as u32).map(TestFr::from).collect();
|
||||
|
||||
let mut tree_opt = default_optimal_merkle_tree(DEFAULT_DEPTH);
|
||||
tree_opt.set_range(0, leaves.iter().cloned()).unwrap();
|
||||
|
||||
let index = 1;
|
||||
let leaf = leaves[index];
|
||||
let mut proof_opt = tree_opt.proof(index).unwrap();
|
||||
|
||||
// Flip first direction
|
||||
proof_opt.0[0].1 = 1 - proof_opt.0[0].1;
|
||||
|
||||
assert!(!tree_opt.verify(&leaf, &proof_opt).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_mismatched_root() {
|
||||
let nof_leaves = 4;
|
||||
let leaves: Vec<TestFr> = (0..nof_leaves as u32).map(TestFr::from).collect();
|
||||
|
||||
let mut tree_full = default_full_merkle_tree(DEFAULT_DEPTH);
|
||||
let mut tree_opt = default_optimal_merkle_tree(DEFAULT_DEPTH);
|
||||
tree_full.set_range(0, leaves.iter().cloned()).unwrap();
|
||||
tree_opt.set_range(0, leaves.iter().cloned()).unwrap();
|
||||
|
||||
let index = 0;
|
||||
let leaf = leaves[index];
|
||||
let proof_full = tree_full.proof(index).unwrap();
|
||||
let proof_opt = tree_opt.proof(index).unwrap();
|
||||
|
||||
// Modify another leaf to change root
|
||||
tree_full.set(1, TestFr::from(999u32)).unwrap();
|
||||
tree_opt.set(1, TestFr::from(999u32)).unwrap();
|
||||
|
||||
assert!(!tree_full.verify(&leaf, &proof_full).unwrap());
|
||||
assert!(!tree_opt.verify(&leaf, &proof_opt).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user