Merkle Tree name refactors

This commit is contained in:
Pratyush Mishra
2019-10-11 19:24:21 -07:00
parent 25e35a25d0
commit 2a86b59138
3 changed files with 63 additions and 72 deletions

View File

@@ -0,0 +1,339 @@
use algebra::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
use r1cs_std::boolean::AllocatedBit;
use crate::merkle_tree::*;
use crate::crh::{FixedLengthCRH, FixedLengthCRHGadget};
use std::borrow::Borrow;
pub struct MerkleTreePathGadget<P, HGadget, ConstraintF>
where
P: MerkleTreeConfig,
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
ConstraintF: Field,
{
path: Vec<(HGadget::OutputGadget, HGadget::OutputGadget)>,
}
impl<P, CRHGadget, ConstraintF> MerkleTreePathGadget<P, CRHGadget, ConstraintF>
where
P: MerkleTreeConfig,
ConstraintF: Field,
CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
{
pub fn check_membership<CS: ConstraintSystem<ConstraintF>>(
&self,
cs: CS,
parameters: &CRHGadget::ParametersGadget,
root: &CRHGadget::OutputGadget,
leaf: impl ToBytesGadget<ConstraintF>,
) -> Result<(), SynthesisError> {
self.conditionally_check_membership(
cs,
parameters,
root,
leaf,
&Boolean::Constant(true),
)
}
pub fn conditionally_check_membership<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
parameters: &CRHGadget::ParametersGadget,
root: &CRHGadget::OutputGadget,
leaf: impl ToBytesGadget<ConstraintF>,
should_enforce: &Boolean,
) -> Result<(), SynthesisError> {
assert_eq!(self.path.len(), P::HEIGHT - 1);
// Check that the hash of the given leaf matches the leaf hash in the membership
// proof.
let leaf_bits = leaf.to_bytes(&mut cs.ns(|| "leaf_to_bytes"))?;
let leaf_hash = CRHGadget::check_evaluation_gadget(
cs.ns(|| "check_evaluation_gadget"),
parameters,
&leaf_bits,
)?;
// Check if leaf is one of the bottom-most siblings.
let leaf_is_left = AllocatedBit::alloc(&mut cs.ns(|| "leaf_is_left"), || {
Ok(leaf_hash == self.path[0].0)
})?
.into();
CRHGadget::OutputGadget::conditional_enforce_equal_or(
&mut cs.ns(|| "check_leaf_is_left"),
&leaf_is_left,
&leaf_hash,
&self.path[0].0,
&self.path[0].1,
should_enforce,
)?;
// Check levels between leaf level and root.
let mut previous_hash = leaf_hash;
for (i, &(ref left_hash, ref right_hash)) in self.path.iter().enumerate() {
// Check if the previous_hash matches the correct current hash.
let previous_is_left =
AllocatedBit::alloc(&mut cs.ns(|| format!("previous_is_left_{}", i)), || {
Ok(&previous_hash == left_hash)
})?
.into();
CRHGadget::OutputGadget::conditional_enforce_equal_or(
&mut cs.ns(|| format!("check_equals_which_{}", i)),
&previous_is_left,
&previous_hash,
left_hash,
right_hash,
should_enforce,
)?;
previous_hash = hash_inner_node_gadget::<P::H, CRHGadget, ConstraintF, _>(
&mut cs.ns(|| format!("hash_inner_node_{}", i)),
parameters,
left_hash,
right_hash,
)?;
}
root.conditional_enforce_equal(
&mut cs.ns(|| "root_is_last"),
&previous_hash,
should_enforce,
)
}
}
pub(crate) fn hash_inner_node_gadget<H, HG, ConstraintF, CS>(
mut cs: CS,
parameters: &HG::ParametersGadget,
left_child: &HG::OutputGadget,
right_child: &HG::OutputGadget,
) -> Result<HG::OutputGadget, SynthesisError>
where
ConstraintF: Field,
CS: ConstraintSystem<ConstraintF>,
H: FixedLengthCRH,
HG: FixedLengthCRHGadget<H, ConstraintF>,
{
let left_bytes = left_child.to_bytes(&mut cs.ns(|| "left_to_bytes"))?;
let right_bytes = right_child.to_bytes(&mut cs.ns(|| "right_to_bytes"))?;
let mut bytes = left_bytes;
bytes.extend_from_slice(&right_bytes);
HG::check_evaluation_gadget(cs, parameters, &bytes)
}
impl<P, HGadget, ConstraintF> AllocGadget<MerkleTreePath<P>, ConstraintF>
for MerkleTreePathGadget<P, HGadget, ConstraintF>
where
P: MerkleTreeConfig,
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
ConstraintF: Field,
{
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<MerkleTreePath<P>>,
{
let mut path = Vec::new();
for (i, &(ref l, ref r)) in value_gen()?.borrow().path.iter().enumerate() {
let l_hash =
HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("l_child_{}", i)), || {
Ok(l.clone())
})?;
let r_hash =
HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("r_child_{}", i)), || {
Ok(r.clone())
})?;
path.push((l_hash, r_hash));
}
Ok(MerkleTreePathGadget {
path,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<MerkleTreePath<P>>,
{
let mut path = Vec::new();
for (i, &(ref l, ref r)) in value_gen()?.borrow().path.iter().enumerate() {
let l_hash = HGadget::OutputGadget::alloc_input(
&mut cs.ns(|| format!("l_child_{}", i)),
|| Ok(l.clone()),
)?;
let r_hash = HGadget::OutputGadget::alloc_input(
&mut cs.ns(|| format!("r_child_{}", i)),
|| Ok(r.clone()),
)?;
path.push((l_hash, r_hash));
}
Ok(MerkleTreePathGadget {
path,
})
}
}
#[cfg(test)]
mod test {
use std::rc::Rc;
use crate::{
crh::{
pedersen::{PedersenCRH, PedersenWindow},
pedersen::constraints::PedersenCRHGadget,
FixedLengthCRH,
FixedLengthCRHGadget,
},
merkle_tree::*,
};
use algebra::{
curves::jubjub::JubJubAffine as JubJub,
fields::jubjub::fq::Fq,
};
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use r1cs_core::ConstraintSystem;
use super::*;
use r1cs_std::{
groups::curves::twisted_edwards::jubjub::JubJubGadget,
test_constraint_system::TestConstraintSystem,
};
#[derive(Clone)]
pub(super) struct Window4x256;
impl PedersenWindow for Window4x256 {
const WINDOW_SIZE: usize = 4;
const NUM_WINDOWS: usize = 256;
}
type H = PedersenCRH<JubJub, Window4x256>;
type HG = PedersenCRHGadget<JubJub, Fq, JubJubGadget>;
struct JubJubMerkleTreeParams;
impl MerkleTreeConfig for JubJubMerkleTreeParams {
const HEIGHT: usize = 32;
type H = H;
}
type JubJubMerkleTree = MerkleHashTree<JubJubMerkleTreeParams>;
fn generate_merkle_tree(leaves: &[[u8; 30]], use_bad_root: bool) -> () {
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
let tree = JubJubMerkleTree::new(crh_parameters.clone(), leaves).unwrap();
let root = tree.root();
let mut satisfied = true;
for (i, leaf) in leaves.iter().enumerate() {
let mut cs = TestConstraintSystem::<Fq>::new();
let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
// Allocate Merkle Tree Root
let root = <HG as FixedLengthCRHGadget<H, _>>::OutputGadget::alloc(&mut cs.ns(|| format!("new_digest_{}", i)), || {
if use_bad_root {
Ok(<H as FixedLengthCRH>::Output::default())
} else {
Ok(root)
}
})
.unwrap();
let constraints_from_digest = cs.num_constraints();
println!("constraints from digest: {}", constraints_from_digest);
// Allocate Parameters for CRH
let crh_parameters =
<HG as FixedLengthCRHGadget<H, Fq>>::ParametersGadget::alloc(
&mut cs.ns(|| format!("new_parameters_{}", i)),
|| Ok(crh_parameters.clone()),
)
.unwrap();
let constraints_from_parameters = cs.num_constraints() - constraints_from_digest;
println!(
"constraints from parameters: {}",
constraints_from_parameters
);
// Allocate Leaf
let leaf_g = UInt8::constant_vec(leaf);
let constraints_from_leaf =
cs.num_constraints() - constraints_from_parameters - constraints_from_digest;
println!("constraints from leaf: {}", constraints_from_leaf);
// Allocate Merkle Tree Path
let cw = MerkleTreePathGadget::<_, HG, _>::alloc(&mut cs.ns(|| format!("new_witness_{}", i)), || {
Ok(proof)
})
.unwrap();
let constraints_from_path = cs.num_constraints()
- constraints_from_parameters
- constraints_from_digest
- constraints_from_leaf;
println!("constraints from path: {}", constraints_from_path);
let leaf_g: &[UInt8] = leaf_g.as_slice();
cw.check_membership(
&mut cs.ns(|| format!("new_witness_check_{}", i)),
&crh_parameters,
&root,
&leaf_g,
)
.unwrap();
if !cs.is_satisfied() {
satisfied = false;
println!(
"Unsatisfied constraint: {}",
cs.which_is_unsatisfied().unwrap()
);
}
let setup_constraints = constraints_from_leaf
+ constraints_from_digest
+ constraints_from_parameters
+ constraints_from_path;
println!(
"number of constraints: {}",
cs.num_constraints() - setup_constraints
);
}
assert!(satisfied);
}
#[test]
fn good_root_test() {
let mut leaves = Vec::new();
for i in 0..4u8 {
let input = [i ; 30];
leaves.push(input);
}
generate_merkle_tree(&leaves, false);
}
#[should_panic]
#[test]
fn bad_root_test() {
let mut leaves = Vec::new();
for i in 0..4u8 {
let input = [i ; 30];
leaves.push(input);
}
generate_merkle_tree(&leaves, true);
}
}

View File

@@ -0,0 +1,441 @@
use crate::crh::FixedLengthCRH;
use algebra::bytes::ToBytes;
use crate::Error;
use std::{fmt, rc::Rc};
#[cfg(feature = "r1cs")]
pub mod constraints;
pub trait MerkleTreeConfig {
const HEIGHT: usize;
type H: FixedLengthCRH;
}
/// Stores the hashes of a particular path (in order) from leaf to root.
/// Our path `is_left_child()` if the boolean in `path` is true.
#[derive(Derivative)]
#[derivative(
Clone(bound = "P: MerkleTreeConfig"),
Debug(bound = "P: MerkleTreeConfig, <P::H as FixedLengthCRH>::Output: fmt::Debug")
)]
pub struct MerkleTreePath<P: MerkleTreeConfig> {
pub(crate) path: Vec<(<P::H as FixedLengthCRH>::Output, <P::H as FixedLengthCRH>::Output)>,
}
pub type MerkleTreeParams<P> = <<P as MerkleTreeConfig>::H as FixedLengthCRH>::Parameters;
pub type MerkleTreeDigest<P> = <<P as MerkleTreeConfig>::H as FixedLengthCRH>::Output;
impl<P: MerkleTreeConfig> Default for MerkleTreePath<P> {
fn default() -> Self {
let mut path = Vec::with_capacity(P::HEIGHT as usize);
for _i in 1..P::HEIGHT as usize {
path.push((<P::H as FixedLengthCRH>::Output::default(), <P::H as FixedLengthCRH>::Output::default()));
}
Self {
path,
}
}
}
impl<P: MerkleTreeConfig> MerkleTreePath<P> {
pub fn verify<L: ToBytes>(
&self,
parameters: &<P::H as FixedLengthCRH>::Parameters,
root_hash: &<P::H as FixedLengthCRH>::Output,
leaf: &L,
) -> Result<bool, Error> {
if self.path.len() != (P::HEIGHT - 1) as usize {
return Ok(false);
}
// Check that the given leaf matches the leaf in the membership proof.
let mut buffer = [0u8; 128];
if !self.path.is_empty() {
let claimed_leaf_hash = hash_leaf::<P::H, L>(parameters, leaf, &mut buffer)?;
// Check if leaf is one of the bottom-most siblings.
if claimed_leaf_hash != self.path[0].0 && claimed_leaf_hash != self.path[0].1 {
return Ok(false);
};
let mut prev = claimed_leaf_hash;
// Check levels between leaf level and root.
for &(ref hash, ref sibling_hash) in &self.path {
// Check if the previous hash matches the correct current hash.
if &prev != hash && &prev != sibling_hash {
return Ok(false);
};
prev = hash_inner_node::<P::H>(parameters, hash, sibling_hash, &mut buffer)?;
}
if root_hash != &prev {
return Ok(false);
}
Ok(true)
} else {
Ok(false)
}
}
}
pub struct MerkleHashTree<P: MerkleTreeConfig> {
tree: Vec<<P::H as FixedLengthCRH>::Output>,
padding_tree: Vec<(<P::H as FixedLengthCRH>::Output, <P::H as FixedLengthCRH>::Output)>,
parameters: Rc<<P::H as FixedLengthCRH>::Parameters>,
root: Option<<P::H as FixedLengthCRH>::Output>,
}
impl<P: MerkleTreeConfig> MerkleHashTree<P> {
pub const HEIGHT: u8 = P::HEIGHT as u8;
pub fn blank(parameters: Rc<<P::H as FixedLengthCRH>::Parameters>) -> Self {
MerkleHashTree {
tree: Vec::new(),
padding_tree: Vec::new(),
root: None,
parameters,
}
}
pub fn new<L: ToBytes>(parameters: Rc<<P::H as FixedLengthCRH>::Parameters>, leaves: &[L]) -> Result<Self, Error> {
let new_time = start_timer!(|| "MerkleTree::New");
let last_level_size = leaves.len().next_power_of_two();
let tree_size = 2 * last_level_size - 1;
let tree_height = tree_height(tree_size);
assert!(tree_height as u8 <= Self::HEIGHT);
// Initialize the merkle tree.
let mut tree = Vec::with_capacity(tree_size);
let empty_hash = hash_empty::<P::H>(&parameters)?;
for _ in 0..tree_size {
tree.push(empty_hash.clone());
}
// Compute the starting indices for each level of the tree.
let mut index = 0;
let mut level_indices = Vec::with_capacity(tree_height);
for _ in 0..tree_height {
level_indices.push(index);
index = left_child(index);
}
// Compute and store the hash values for each leaf.
let last_level_index = level_indices.pop().unwrap();
let mut buffer = [0u8; 128];
for (i, leaf) in leaves.iter().enumerate() {
tree[last_level_index + i] = hash_leaf::<P::H, _>(&parameters, leaf, &mut buffer)?;
}
// Compute the hash values for every node in the tree.
let mut upper_bound = last_level_index;
let mut buffer = [0u8; 128];
level_indices.reverse();
for &start_index in &level_indices {
// Iterate over the current level.
for current_index in start_index..upper_bound {
let left_index = left_child(current_index);
let right_index = right_child(current_index);
// Compute Hash(left || right).
tree[current_index] = hash_inner_node::<P::H>(
&parameters,
&tree[left_index],
&tree[right_index],
&mut buffer,
)?;
}
upper_bound = start_index;
}
// Finished computing actual tree.
// Now, we compute the dummy nodes until we hit our HEIGHT goal.
let mut cur_height = tree_height;
let mut padding_tree = Vec::new();
let mut cur_hash = tree[0].clone();
while cur_height < (Self::HEIGHT - 1) as usize {
cur_hash = hash_inner_node::<P::H>(&parameters, &cur_hash, &empty_hash, &mut buffer)?;
padding_tree.push((cur_hash.clone(), empty_hash.clone()));
cur_height += 1;
}
let root_hash = hash_inner_node::<P::H>(&parameters, &cur_hash, &empty_hash, &mut buffer)?;
end_timer!(new_time);
Ok(MerkleHashTree {
tree,
padding_tree,
parameters,
root: Some(root_hash),
})
}
#[inline]
pub fn root(&self) -> <P::H as FixedLengthCRH>::Output {
self.root.clone().unwrap()
}
pub fn generate_proof<L: ToBytes>(
&self,
index: usize,
leaf: &L,
) -> Result<MerkleTreePath<P>, Error> {
let prove_time = start_timer!(|| "MerkleTree::GenProof");
let mut path = Vec::new();
let mut buffer = [0u8; 128];
let leaf_hash = hash_leaf::<P::H, _>(&self.parameters, leaf, &mut buffer)?;
let tree_height = tree_height(self.tree.len());
let tree_index = convert_index_to_last_level(index, tree_height);
let empty_hash = hash_empty::<P::H>(&self.parameters)?;
// Check that the given index corresponds to the correct leaf.
if leaf_hash != self.tree[tree_index] {
Err(MerkleTreeError::IncorrectLeafIndex(tree_index))?
}
// Iterate from the leaf up to the root, storing all intermediate hash values.
let mut current_node = tree_index;
while !is_root(current_node) {
let sibling_node = sibling(current_node).unwrap();
let (curr_hash, sibling_hash) = (
self.tree[current_node].clone(),
self.tree[sibling_node].clone(),
);
if is_left_child(current_node) {
path.push((curr_hash, sibling_hash));
} else {
path.push((sibling_hash, curr_hash));
}
current_node = parent(current_node).unwrap();
}
// Store the root node. Set boolean as true for consistency with digest
// location.
assert!(path.len() < Self::HEIGHT as usize);
if path.len() != (Self::HEIGHT - 1) as usize {
path.push((self.tree[0].clone(), empty_hash));
for &(ref hash, ref sibling_hash) in &self.padding_tree {
path.push((hash.clone(), sibling_hash.clone()));
}
}
end_timer!(prove_time);
if path.len() != (Self::HEIGHT - 1) as usize {
Err(MerkleTreeError::IncorrectPathLength(path.len()))?
} else {
Ok(MerkleTreePath {
path,
})
}
}
}
#[derive(Debug)]
pub enum MerkleTreeError {
IncorrectLeafIndex(usize),
IncorrectPathLength(usize),
}
impl std::fmt::Display for MerkleTreeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let msg = match self {
MerkleTreeError::IncorrectLeafIndex(index) => format!("incorrect leaf index: {}", index),
MerkleTreeError::IncorrectPathLength(len) => format!("incorrect path length: {}", len),
};
write!(f, "{}", msg)
}
}
impl std::error::Error for MerkleTreeError {
#[inline]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
/// Returns the log2 value of the given number.
#[inline]
fn log2(number: usize) -> usize {
(number as f64).log2() as usize
}
/// Returns the height of the tree, given the size of the tree.
#[inline]
fn tree_height(tree_size: usize) -> usize {
log2(tree_size + 1)
}
/// Returns true iff the index represents the root.
#[inline]
fn is_root(index: usize) -> bool {
index == 0
}
/// Returns the index of the left child, given an index.
#[inline]
fn left_child(index: usize) -> usize {
2 * index + 1
}
/// Returns the index of the right child, given an index.
#[inline]
fn right_child(index: usize) -> usize {
2 * index + 2
}
/// Returns the index of the sibling, given an index.
#[inline]
fn sibling(index: usize) -> Option<usize> {
if index == 0 {
None
} else if is_left_child(index) {
Some(index + 1)
} else {
Some(index - 1)
}
}
/// Returns true iff the given index represents a left child.
#[inline]
fn is_left_child(index: usize) -> bool {
index % 2 == 1
}
/// Returns the index of the parent, given an index.
#[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
}
/// Returns the output hash, given a left and right hash value.
pub(crate) fn hash_inner_node<H: FixedLengthCRH>(
parameters: &H::Parameters,
left: &H::Output,
right: &H::Output,
buffer: &mut [u8],
) -> Result<H::Output, Error> {
use std::io::Cursor;
let mut writer = Cursor::new(buffer);
// Construct left input.
left.write(&mut writer)?;
// Construct right input.
right.write(&mut writer)?;
let buffer = writer.into_inner();
H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
}
/// Returns the hash of a leaf.
pub(crate) fn hash_leaf<H: FixedLengthCRH, L: ToBytes>(
parameters: &H::Parameters,
leaf: &L,
buffer: &mut [u8],
) -> Result<H::Output, Error> {
use std::io::Cursor;
let mut writer = Cursor::new(buffer);
leaf.write(&mut writer)?;
let buffer = writer.into_inner();
H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
}
pub(crate) fn hash_empty<H: FixedLengthCRH>(
parameters: &H::Parameters,
) -> Result<H::Output, Error> {
let empty_buffer = vec![0u8; H::INPUT_SIZE_BITS / 8];
H::evaluate(parameters, &empty_buffer)
}
#[cfg(test)]
mod test {
use crate::{crh::{pedersen::*, *}, merkle_tree::*};
use algebra::curves::jubjub::JubJubAffine as JubJub;
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
#[derive(Clone)]
pub(super) struct Window4x256;
impl PedersenWindow for Window4x256 {
const WINDOW_SIZE: usize = 4;
const NUM_WINDOWS: usize = 256;
}
type H = PedersenCRH<JubJub, Window4x256>;
struct JubJubMerkleTreeParams;
impl MerkleTreeConfig for JubJubMerkleTreeParams {
const HEIGHT: usize = 32;
type H = H;
}
type JubJubMerkleTree = MerkleHashTree<JubJubMerkleTreeParams>;
fn generate_merkle_tree<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
let tree = JubJubMerkleTree::new(crh_parameters.clone(), &leaves).unwrap();
let root = tree.root();
for (i, leaf) in leaves.iter().enumerate() {
let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
}
}
#[test]
fn good_root_test() {
let mut leaves = Vec::new();
for i in 0..4u8 {
leaves.push([i, i, i, i, i, i, i, i]);
}
generate_merkle_tree(&leaves);
let mut leaves = Vec::new();
for i in 0..100u8 {
leaves.push([i, i, i, i, i, i, i, i]);
}
generate_merkle_tree(&leaves);
}
fn bad_merkle_tree_verify<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
use algebra::groups::Group;
let mut rng = XorShiftRng::seed_from_u64(13423423u64);
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
let tree = JubJubMerkleTree::new(crh_parameters.clone(), &leaves).unwrap();
let root = JubJub::zero();
for (i, leaf) in leaves.iter().enumerate() {
let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
}
}
#[should_panic]
#[test]
fn bad_root_test() {
let mut leaves = Vec::new();
for i in 0..4u8 {
leaves.push([i, i, i, i, i, i, i, i]);
}
generate_merkle_tree(&leaves);
let mut leaves = Vec::new();
for i in 0..100u8 {
leaves.push([i, i, i, i, i, i, i, i]);
}
bad_merkle_tree_verify(&leaves);
}
}