use serde::{Deserialize, Serialize};
pub trait MerkleProof {
fn describe(&self) -> String;
fn verify_structure(&self) -> bool;
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct InclusionProof {
pub manifest_id: String,
pub leaf_index: usize,
pub leaf_hash: String,
pub merkle_path: Vec<String>,
pub tree_size: usize,
pub root_hash: String,
}
impl InclusionProof {
pub fn describe(&self) -> String {
format!(
"Inclusion proof for manifest '{}' at index {} in tree of size {}",
self.manifest_id, self.leaf_index, self.tree_size
)
}
pub fn verify_path(&self, hasher: &dyn crate::merkle_tree::hasher::Hasher) -> bool {
if self.tree_size == 0 {
return false;
}
if self.leaf_index >= self.tree_size {
return false;
}
let mut current_hash = self.leaf_hash.clone();
let mut level_pos = self.leaf_index;
let mut level_size = self.tree_size;
let mut path_index = 0;
while level_size > 1 {
let has_sibling = if level_pos % 2 == 0 {
level_pos + 1 < level_size
} else {
true };
if has_sibling && path_index < self.merkle_path.len() {
let sibling_hash = &self.merkle_path[path_index];
let is_left = level_pos % 2 == 0;
current_hash = if is_left {
let combined = format!("node:{}:{}", current_hash, sibling_hash);
hasher.hash(combined.as_bytes())
} else {
let combined = format!("node:{}:{}", sibling_hash, current_hash);
hasher.hash(combined.as_bytes())
};
path_index += 1;
}
level_pos /= 2;
level_size = (level_size + 1) / 2; }
path_index == self.merkle_path.len() && current_hash == self.root_hash
}
}
impl MerkleProof for InclusionProof {
fn describe(&self) -> String {
self.describe()
}
fn verify_structure(&self) -> bool {
if self.manifest_id.is_empty() || self.tree_size == 0 {
return false;
}
if self.leaf_index >= self.tree_size {
return false;
}
if self.leaf_hash.is_empty() || self.root_hash.is_empty() {
return false;
}
if self.tree_size == 1 {
return self.merkle_path.is_empty() && self.leaf_index == 0;
}
let max_path_length = (self.tree_size as f64).log2().ceil() as usize;
self.merkle_path.len() <= max_path_length
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ConsistencyProof {
pub old_size: usize,
pub new_size: usize,
pub old_root: String,
pub new_root: String,
pub proof_hashes: Vec<String>,
}
impl ConsistencyProof {
pub fn describe(&self) -> String {
format!(
"Consistency proof from tree size {} to {} (proof elements: {})",
self.old_size,
self.new_size,
self.proof_hashes.len()
)
}
pub fn verify(&self, expected_old_root: &str, expected_new_root: &str) -> bool {
self.old_root == expected_old_root && self.new_root == expected_new_root
}
}
impl MerkleProof for ConsistencyProof {
fn describe(&self) -> String {
self.describe()
}
fn verify_structure(&self) -> bool {
if self.old_size == 0 || self.new_size == 0 {
return false;
}
if self.old_size > self.new_size {
return false;
}
if self.old_root.is_empty() || self.new_root.is_empty() {
return false;
}
if self.old_size == self.new_size {
return self.proof_hashes.is_empty() && self.old_root == self.new_root;
}
true
}
}