use alloy_primitives::{B256, Keccak256};
use super::hasher::hash_pairs;
use crate::bmt::{Hasher, constants::*, error::BmtError};
use crate::error::Result;
#[inline(always)]
fn new_node_hasher(prefix: Option<&[u8]>) -> Keccak256 {
let mut hasher = Keccak256::new();
if let Some(p) = prefix {
hasher.update(p);
}
hasher
}
#[derive(Clone, Debug)]
pub struct Proof {
pub segment_index: usize,
pub segment: B256,
pub proof_segments: Vec<B256>,
pub span: u64,
pub prefix: Option<Vec<u8>>,
}
impl Proof {
pub const fn new(
segment_index: usize,
segment: B256,
proof_segments: Vec<B256>,
span: u64,
prefix: Option<Vec<u8>>,
) -> Self {
Self {
segment_index,
segment,
proof_segments,
span,
prefix,
}
}
pub fn verify(&self, root_hash: &[u8]) -> Result<bool> {
if self.proof_segments.len() != PROOF_LENGTH {
return Err(
BmtError::invalid_proof_length(PROOF_LENGTH, self.proof_segments.len()).into(),
);
}
let mut current_hash = self.segment;
let mut current_index = self.segment_index;
let prefix = self.prefix.as_deref();
for proof_segment in &self.proof_segments {
let mut hasher = new_node_hasher(prefix);
if current_index.is_multiple_of(2) {
hasher.update(current_hash.as_slice());
hasher.update(proof_segment.as_slice());
} else {
hasher.update(proof_segment.as_slice());
hasher.update(current_hash.as_slice());
}
current_hash = B256::from_slice(hasher.finalize().as_slice());
current_index /= 2;
}
let mut hasher = Keccak256::new();
if let Some(prefix) = &self.prefix {
hasher.update(prefix);
}
hasher.update(self.span.to_le_bytes());
hasher.update(current_hash.as_slice());
let computed_root = B256::from_slice(hasher.finalize().as_slice());
Ok(computed_root.as_slice() == root_hash)
}
}
pub trait Prover {
fn generate_proof(&self, data: &[u8], segment_index: usize) -> Result<Proof>;
fn verify_proof(proof: &Proof, root_hash: &[u8]) -> Result<bool>;
}
impl Prover for Hasher {
fn generate_proof(&self, data: &[u8], segment_index: usize) -> Result<Proof> {
if segment_index >= BRANCHES {
return Err(self::BmtError::invalid_input_size(format!(
"Segment index {segment_index} out of bounds for BRANCHES"
))
.into());
}
let mut leaves = [[0u8; SEGMENT_SIZE]; BRANCHES];
let n = data.len().min(BRANCHES * SEGMENT_SIZE);
for (leaf, chunk) in leaves.iter_mut().zip(data[..n].chunks(SEGMENT_SIZE)) {
leaf[..chunk.len()].copy_from_slice(chunk);
}
let segment = B256::from(leaves[segment_index]);
let prefix = if self.prefix().is_empty() {
None
} else {
Some(self.prefix().to_vec())
};
let prefix_ref = prefix.as_deref();
let mut levels: Vec<Vec<[u8; 32]>> = Vec::with_capacity(PROOF_LENGTH);
let mut current = leaves.to_vec();
while current.len() > 1 {
let mut next = vec![[0u8; 32]; current.len() / 2];
hash_pairs(prefix_ref, current.as_flattened(), &mut next);
levels.push(current);
current = next;
}
let mut proof_segments = Vec::with_capacity(PROOF_LENGTH);
let mut index = segment_index;
for level in &levels {
proof_segments.push(B256::from(level[index ^ 1]));
index /= 2;
}
Ok(Proof::new(
segment_index,
segment,
proof_segments,
self.span(),
prefix,
))
}
fn verify_proof(proof: &Proof, root_hash: &[u8]) -> Result<bool> {
proof.verify(root_hash)
}
}