#![allow(clippy::needless_range_loop)]
use crate::{
crh::{CRHScheme, TwoToOneCRHScheme},
sponge::Absorb,
Error,
};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
#[cfg(not(feature = "std"))]
use ark_std::vec::Vec;
use ark_std::{
borrow::Borrow,
collections::BTreeSet,
fmt::Debug,
hash::{BuildHasherDefault, Hash},
};
use hashbrown::HashMap;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
#[cfg(feature = "constraints")]
pub mod constraints;
pub mod configs;
#[cfg(test)]
mod tests;
#[cfg(all(
target_has_atomic = "8",
target_has_atomic = "16",
target_has_atomic = "32",
target_has_atomic = "64",
target_has_atomic = "ptr"
))]
type DefaultHasher = ahash::AHasher;
#[cfg(not(all(
target_has_atomic = "8",
target_has_atomic = "16",
target_has_atomic = "32",
target_has_atomic = "64",
target_has_atomic = "ptr"
)))]
type DefaultHasher = fnv::FnvHasher;
pub trait DigestConverter<From, To: ?Sized> {
type TargetType: Borrow<To>;
fn convert(item: From) -> Result<Self::TargetType, Error>;
}
pub struct IdentityDigestConverter<T> {
_prev_layer_digest: T,
}
impl<T> DigestConverter<T, T> for IdentityDigestConverter<T> {
type TargetType = T;
fn convert(item: T) -> Result<T, Error> {
Ok(item)
}
}
pub struct ByteDigestConverter<T: CanonicalSerialize> {
_prev_layer_digest: T,
}
impl<T: CanonicalSerialize> DigestConverter<T, [u8]> for ByteDigestConverter<T> {
type TargetType = Vec<u8>;
fn convert(item: T) -> Result<Self::TargetType, Error> {
Ok(crate::to_uncompressed_bytes!(item)?)
}
}
pub trait Config {
type Leaf: ?Sized + Send; type LeafDigest: Clone
+ Eq
+ Debug
+ Hash
+ Default
+ CanonicalSerialize
+ CanonicalDeserialize
+ Send
+ Sync;
type LeafInnerDigestConverter: DigestConverter<
Self::LeafDigest,
<Self::TwoToOneHash as TwoToOneCRHScheme>::Input,
>;
type InnerDigest: Clone
+ Eq
+ Debug
+ Hash
+ Default
+ CanonicalSerialize
+ CanonicalDeserialize
+ Send
+ Sync
+ Absorb;
type LeafHash: CRHScheme<Input = Self::Leaf, Output = Self::LeafDigest>;
type TwoToOneHash: TwoToOneCRHScheme<Output = Self::InnerDigest>;
}
pub type TwoToOneParam<P> = <<P as Config>::TwoToOneHash as TwoToOneCRHScheme>::Parameters;
pub type LeafParam<P> = <<P as Config>::LeafHash as CRHScheme>::Parameters;
#[derive(Derivative, CanonicalSerialize, CanonicalDeserialize)]
#[derivative(
PartialEq(bound = "P: Config"),
Clone(bound = "P: Config"),
Debug(bound = "P: Config"),
Default(bound = "P: Config")
)]
pub struct Path<P: Config> {
pub leaf_sibling_hash: P::LeafDigest,
pub auth_path: Vec<P::InnerDigest>,
pub leaf_index: usize,
}
impl<P: Config> Path<P> {
#[allow(unused)] fn position_list(&'_ self) -> impl '_ + Iterator<Item = bool> {
(0..self.auth_path.len() + 1)
.map(move |i| ((self.leaf_index >> i) & 1) != 0)
.rev()
}
}
impl<P: Config> Path<P> {
pub fn verify<L: Borrow<P::Leaf>>(
&self,
leaf_hash_params: &LeafParam<P>,
two_to_one_params: &TwoToOneParam<P>,
root_hash: &P::InnerDigest,
leaf: L,
) -> Result<bool, crate::Error> {
let claimed_leaf_hash = P::LeafHash::evaluate(&leaf_hash_params, leaf)?;
let (left_child, right_child) =
select_left_right_child(self.leaf_index, &claimed_leaf_hash, &self.leaf_sibling_hash)?;
let left_child = P::LeafInnerDigestConverter::convert(left_child)?;
let right_child = P::LeafInnerDigestConverter::convert(right_child)?;
let mut curr_path_node =
P::TwoToOneHash::evaluate(&two_to_one_params, left_child, right_child)?;
let mut index = self.leaf_index;
index >>= 1;
for level in (0..self.auth_path.len()).rev() {
let (left, right) =
select_left_right_child(index, &curr_path_node, &self.auth_path[level])?;
curr_path_node = P::TwoToOneHash::compress(&two_to_one_params, &left, &right)?;
index >>= 1;
}
if &curr_path_node != root_hash {
return Ok(false);
}
Ok(true)
}
}
#[derive(Derivative, CanonicalSerialize, CanonicalDeserialize)]
#[derivative(
Clone(bound = "P: Config"),
Debug(bound = "P: Config"),
Default(bound = "P: Config")
)]
pub struct MultiPath<P: Config> {
pub leaf_siblings_hashes: Vec<P::LeafDigest>,
pub auth_paths_prefix_lenghts: Vec<usize>,
pub auth_paths_suffixes: Vec<Vec<P::InnerDigest>>,
pub leaf_indexes: Vec<usize>,
}
impl<P: Config> MultiPath<P> {
pub fn verify<L: Borrow<P::Leaf> + Clone>(
&self,
leaf_hash_params: &LeafParam<P>,
two_to_one_params: &TwoToOneParam<P>,
root_hash: &P::InnerDigest,
leaves: impl IntoIterator<Item = L>,
) -> Result<bool, crate::Error> {
let tree_height = self.auth_paths_suffixes[0].len() + 2;
let mut leaves = leaves.into_iter();
let mut hash_lut: HashMap<usize, P::InnerDigest, _> =
HashMap::with_hasher(BuildHasherDefault::<DefaultHasher>::default());
let mut prev_path: Vec<_> = self.auth_paths_suffixes[0].clone();
for i in 0..self.leaf_indexes.len() {
let leaf_index = self.leaf_indexes[i];
let leaf = leaves.next().unwrap();
let leaf_sibling_hash = &self.leaf_siblings_hashes[i];
let auth_path = prefix_decode_path(
&prev_path,
self.auth_paths_prefix_lenghts[i],
&self.auth_paths_suffixes[i],
);
prev_path = auth_path.clone();
let claimed_leaf_hash = P::LeafHash::evaluate(&leaf_hash_params, leaf.clone())?;
let (left_child, right_child) =
select_left_right_child(leaf_index, &claimed_leaf_hash, &leaf_sibling_hash)?;
let left_child = P::LeafInnerDigestConverter::convert(left_child)?;
let right_child = P::LeafInnerDigestConverter::convert(right_child)?;
let mut index = leaf_index;
let mut index_in_tree = convert_index_to_last_level(leaf_index, tree_height);
index >>= 1;
index_in_tree = parent(index_in_tree).unwrap();
let mut curr_path_node = hash_lut.entry(index_in_tree).or_insert_with(|| {
P::TwoToOneHash::evaluate(&two_to_one_params, left_child, right_child).unwrap()
});
for level in (0..auth_path.len()).rev() {
let (left, right) =
select_left_right_child(index, curr_path_node, &auth_path[level])?;
index >>= 1;
index_in_tree = parent(index_in_tree).unwrap();
curr_path_node = hash_lut.entry(index_in_tree).or_insert_with(|| {
P::TwoToOneHash::compress(&two_to_one_params, left, right).unwrap()
});
}
if curr_path_node != root_hash {
return Ok(false);
}
}
Ok(true)
}
#[allow(unused)] fn position_list(&'_ self) -> impl '_ + Iterator<Item = Vec<bool>> {
let path_len = self.auth_paths_suffixes[0].len();
cfg_into_iter!(self.leaf_indexes.clone())
.map(move |i| {
(0..path_len + 1)
.map(move |j| ((i >> j) & 1) != 0)
.rev()
.collect()
})
.collect::<Vec<_>>()
.into_iter()
}
}
fn select_left_right_child<L: Clone>(
index: usize,
computed_hash: &L,
sibling_hash: &L,
) -> Result<(L, L), crate::Error> {
let is_left = index & 1 == 0;
let mut left_child = computed_hash;
let mut right_child = sibling_hash;
if !is_left {
core::mem::swap(&mut left_child, &mut right_child);
}
Ok((left_child.clone(), right_child.clone()))
}
#[derive(Derivative)]
#[derivative(Clone(bound = "P: Config"))]
pub struct MerkleTree<P: Config> {
non_leaf_nodes: Vec<P::InnerDigest>,
leaf_nodes: Vec<P::LeafDigest>,
two_to_one_hash_param: TwoToOneParam<P>,
leaf_hash_param: LeafParam<P>,
height: usize,
}
impl<P: Config> MerkleTree<P> {
pub fn blank(
leaf_hash_param: &LeafParam<P>,
two_to_one_hash_param: &TwoToOneParam<P>,
height: usize,
) -> Result<Self, crate::Error> {
let leaf_digests = vec![P::LeafDigest::default(); 1 << (height - 1)];
Self::new_with_leaf_digest(leaf_hash_param, two_to_one_hash_param, leaf_digests)
}
pub fn new<L: AsRef<P::Leaf> + Send>(
leaf_hash_param: &LeafParam<P>,
two_to_one_hash_param: &TwoToOneParam<P>,
#[cfg(not(feature = "parallel"))] leaves: impl IntoIterator<Item = L>,
#[cfg(feature = "parallel")] leaves: impl IntoParallelIterator<Item = L>,
) -> Result<Self, crate::Error> {
let leaf_digests: Vec<_> = cfg_into_iter!(leaves)
.map(|input| P::LeafHash::evaluate(leaf_hash_param, input.as_ref()))
.collect::<Result<Vec<_>, _>>()?;
Self::new_with_leaf_digest(leaf_hash_param, two_to_one_hash_param, leaf_digests)
}
pub fn new_with_leaf_digest(
leaf_hash_param: &LeafParam<P>,
two_to_one_hash_param: &TwoToOneParam<P>,
leaf_digests: Vec<P::LeafDigest>,
) -> Result<Self, crate::Error> {
let leaf_nodes_size = leaf_digests.len();
assert!(
leaf_nodes_size.is_power_of_two() && leaf_nodes_size > 1,
"`leaves.len() should be power of two and greater than one"
);
let non_leaf_nodes_size = leaf_nodes_size - 1;
let tree_height = tree_height(leaf_nodes_size);
let hash_of_empty: P::InnerDigest = P::InnerDigest::default();
let mut non_leaf_nodes: Vec<P::InnerDigest> = cfg_into_iter!(0..non_leaf_nodes_size)
.map(|_| hash_of_empty.clone())
.collect();
let mut index = 0;
let mut level_indices = Vec::with_capacity(tree_height - 1);
for _ in 0..(tree_height - 1) {
level_indices.push(index);
index = left_child(index);
}
{
let start_index = level_indices.pop().unwrap();
let upper_bound = left_child(start_index);
cfg_iter_mut!(non_leaf_nodes[start_index..upper_bound])
.enumerate()
.try_for_each(|(i, n)| {
let current_index = i + start_index;
let left_leaf_index = left_child(current_index) - upper_bound;
let right_leaf_index = right_child(current_index) - upper_bound;
*n = P::TwoToOneHash::evaluate(
two_to_one_hash_param,
P::LeafInnerDigestConverter::convert(
leaf_digests[left_leaf_index].clone(),
)?,
P::LeafInnerDigestConverter::convert(
leaf_digests[right_leaf_index].clone(),
)?,
)?;
Ok::<(), crate::Error>(())
})?;
}
level_indices.reverse();
for &start_index in &level_indices {
let upper_bound = left_child(start_index);
let (nodes_at_level, nodes_at_prev_level) =
non_leaf_nodes[..].split_at_mut(upper_bound);
cfg_iter_mut!(nodes_at_level[start_index..])
.enumerate()
.try_for_each(|(i, n)| {
let current_index = i + start_index;
let left_leaf_index = left_child(current_index) - upper_bound;
let right_leaf_index = right_child(current_index) - upper_bound;
*n = P::TwoToOneHash::compress(
two_to_one_hash_param,
nodes_at_prev_level[left_leaf_index].clone(),
nodes_at_prev_level[right_leaf_index].clone(),
)?;
Ok::<_, crate::Error>(())
})?;
}
Ok(MerkleTree {
leaf_nodes: leaf_digests,
non_leaf_nodes,
height: tree_height,
leaf_hash_param: leaf_hash_param.clone(),
two_to_one_hash_param: two_to_one_hash_param.clone(),
})
}
pub fn root(&self) -> P::InnerDigest {
self.non_leaf_nodes[0].clone()
}
pub fn height(&self) -> usize {
self.height
}
pub fn get_leaf_sibling_hash(&self, index: usize) -> P::LeafDigest {
if index & 1 == 0 {
self.leaf_nodes[index + 1].clone()
} else {
self.leaf_nodes[index - 1].clone()
}
}
fn compute_auth_path(&self, index: usize) -> Vec<P::InnerDigest> {
let tree_height = tree_height(self.leaf_nodes.len());
let leaf_index_in_tree = convert_index_to_last_level(index, tree_height);
let mut path = Vec::with_capacity(tree_height - 2);
let mut current_node = parent(leaf_index_in_tree).unwrap();
while !is_root(current_node) {
let sibling_node = sibling(current_node).unwrap();
path.push(self.non_leaf_nodes[sibling_node].clone());
current_node = parent(current_node).unwrap();
}
debug_assert_eq!(path.len(), tree_height - 2);
path.reverse();
path
}
pub fn generate_proof(&self, index: usize) -> Result<Path<P>, crate::Error> {
let path = self.compute_auth_path(index);
Ok(Path {
leaf_index: index,
auth_path: path,
leaf_sibling_hash: self.get_leaf_sibling_hash(index),
})
}
pub fn generate_multi_proof(
&self,
indexes: impl IntoIterator<Item = usize>,
) -> Result<MultiPath<P>, crate::Error> {
let indexes: BTreeSet<usize> = indexes.into_iter().collect();
let mut auth_paths_prefix_lenghts: Vec<usize> = Vec::with_capacity(indexes.len());
let mut auth_paths_suffixes: Vec<Vec<P::InnerDigest>> = Vec::with_capacity(indexes.len());
let mut leaf_siblings_hashes = Vec::with_capacity(indexes.len());
let mut prev_path = Vec::new();
for index in &indexes {
leaf_siblings_hashes.push(self.get_leaf_sibling_hash(*index));
let path = self.compute_auth_path(*index);
let (prefix_len, suffix) = prefix_encode_path(&prev_path, &path);
auth_paths_prefix_lenghts.push(prefix_len);
auth_paths_suffixes.push(suffix);
prev_path = path;
}
Ok(MultiPath {
leaf_indexes: Vec::from_iter(indexes),
auth_paths_prefix_lenghts,
auth_paths_suffixes,
leaf_siblings_hashes,
})
}
fn updated_path<T: Borrow<P::Leaf>>(
&self,
index: usize,
new_leaf: T,
) -> Result<(P::LeafDigest, Vec<P::InnerDigest>), crate::Error> {
let new_leaf_hash: P::LeafDigest = P::LeafHash::evaluate(&self.leaf_hash_param, new_leaf)?;
let (leaf_left, leaf_right) = if index & 1 == 0 {
(&new_leaf_hash, &self.leaf_nodes[index + 1])
} else {
(&self.leaf_nodes[index - 1], &new_leaf_hash)
};
let mut path_bottom_to_top = Vec::with_capacity(self.height - 1);
{
path_bottom_to_top.push(P::TwoToOneHash::evaluate(
&self.two_to_one_hash_param,
P::LeafInnerDigestConverter::convert(leaf_left.clone())?,
P::LeafInnerDigestConverter::convert(leaf_right.clone())?,
)?);
}
let leaf_index_in_tree = convert_index_to_last_level(index, self.height);
let mut prev_index = parent(leaf_index_in_tree).unwrap();
while !is_root(prev_index) {
let (left_child, right_child) = if is_left_child(prev_index) {
(
path_bottom_to_top.last().unwrap(),
&self.non_leaf_nodes[sibling(prev_index).unwrap()],
)
} else {
(
&self.non_leaf_nodes[sibling(prev_index).unwrap()],
path_bottom_to_top.last().unwrap(),
)
};
let evaluated =
P::TwoToOneHash::compress(&self.two_to_one_hash_param, left_child, right_child)?;
path_bottom_to_top.push(evaluated);
prev_index = parent(prev_index).unwrap();
}
debug_assert_eq!(path_bottom_to_top.len(), self.height - 1);
let path_top_to_bottom: Vec<_> = path_bottom_to_top.into_iter().rev().collect();
Ok((new_leaf_hash, path_top_to_bottom))
}
pub fn update(&mut self, index: usize, new_leaf: &P::Leaf) -> Result<(), crate::Error> {
assert!(index < self.leaf_nodes.len(), "index out of range");
let (updated_leaf_hash, mut updated_path) = self.updated_path(index, new_leaf)?;
self.leaf_nodes[index] = updated_leaf_hash;
let mut curr_index = convert_index_to_last_level(index, self.height);
for _ in 0..self.height - 1 {
curr_index = parent(curr_index).unwrap();
self.non_leaf_nodes[curr_index] = updated_path.pop().unwrap();
}
Ok(())
}
pub fn check_update<T: Borrow<P::Leaf>>(
&mut self,
index: usize,
new_leaf: &P::Leaf,
asserted_new_root: &P::InnerDigest,
) -> Result<bool, crate::Error> {
assert!(index < self.leaf_nodes.len(), "index out of range");
let (updated_leaf_hash, mut updated_path) = self.updated_path(index, new_leaf)?;
if &updated_path[0] != asserted_new_root {
return Ok(false);
}
self.leaf_nodes[index] = updated_leaf_hash;
let mut curr_index = convert_index_to_last_level(index, self.height);
for _ in 0..self.height - 1 {
curr_index = parent(curr_index).unwrap();
self.non_leaf_nodes[curr_index] = updated_path.pop().unwrap();
}
Ok(true)
}
}
#[inline]
fn tree_height(num_leaves: usize) -> usize {
if num_leaves == 1 {
return 1;
}
(ark_std::log2(num_leaves) as usize) + 1
}
#[inline]
fn is_root(index: usize) -> bool {
index == 0
}
#[inline]
fn left_child(index: usize) -> usize {
2 * index + 1
}
#[inline]
fn right_child(index: usize) -> usize {
2 * index + 2
}
#[inline]
fn sibling(index: usize) -> Option<usize> {
if index == 0 {
None
} else if is_left_child(index) {
Some(index + 1)
} else {
Some(index - 1)
}
}
#[inline]
fn is_left_child(index: usize) -> bool {
index % 2 == 1
}
#[inline]
fn parent(index: usize) -> Option<usize> {
if index > 0 {
Some((index - 1) >> 1)
} else {
None
}
}
#[inline]
fn convert_index_to_last_level(index: usize, tree_height: usize) -> usize {
index + (1 << (tree_height - 1)) - 1
}
#[inline]
fn prefix_encode_path<T>(prev_path: &Vec<T>, path: &Vec<T>) -> (usize, Vec<T>)
where
T: Eq + Clone,
{
let prefix_length = prev_path
.iter()
.zip(path.iter())
.take_while(|(a, b)| a == b)
.count();
(prefix_length, path[prefix_length..].to_vec())
}
fn prefix_decode_path<T>(prev_path: &Vec<T>, prefix_len: usize, suffix: &Vec<T>) -> Vec<T>
where
T: Eq + Clone,
{
if prefix_len == 0 {
suffix.clone()
} else {
vec![prev_path[0..prefix_len].to_vec(), suffix.clone()].concat()
}
}